diff --git a/src/bin/Jamfile b/src/bin/Jamfile index bfb3d34b38..dce6e79837 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -155,6 +155,7 @@ SubInclude HAIKU_TOP src bin ideinfo ; SubInclude HAIKU_TOP src bin keymap ; SubInclude HAIKU_TOP src bin less ; SubInclude HAIKU_TOP src bin listdev ; +SubInclude HAIKU_TOP src bin m4 ; SubInclude HAIKU_TOP src bin make ; SubInclude HAIKU_TOP src bin makebootable ; #SubInclude HAIKU_TOP src bin makeudfimage ; diff --git a/src/bin/m4/COPYING b/src/bin/m4/COPYING new file mode 100644 index 0000000000..d511905c16 --- /dev/null +++ b/src/bin/m4/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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 + + 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. + + + Copyright (C) + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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. + + , 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 Lesser General +Public License instead of this License. diff --git a/src/bin/m4/Jamfile b/src/bin/m4/Jamfile new file mode 100644 index 0000000000..be30672b1a --- /dev/null +++ b/src/bin/m4/Jamfile @@ -0,0 +1,5 @@ +SubDir HAIKU_TOP src bin m4 ; + +SubInclude HAIKU_TOP src bin m4 lib ; +SubInclude HAIKU_TOP src bin m4 src ; + diff --git a/src/bin/m4/doc/Makefile.am b/src/bin/m4/doc/Makefile.am new file mode 100644 index 0000000000..9e2dacf560 --- /dev/null +++ b/src/bin/m4/doc/Makefile.am @@ -0,0 +1,43 @@ +## This file is part of GNU M4. +## +## Copyright (C) 2006 Free Software Foundation, Inc. +## +## 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; see the file COPYING. If not, write to +## the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +## Boston, MA 02110-1301, USA. +## +## This file written by Eric Blake + +info_TEXINFOS = m4.texinfo +m4_TEXINFOS = fdl.texi +man_MANS = m4.1 +EXTRA_DIST = $(man_MANS) gendocs_template +MAINTAINERCLEANFILES = $(man_MANS) gendocs_template +SUFFIXES = .1 + +# Depend on configure.ac for version, m4.c for usage text. Do not depend on +# built m4 executable, since not everyone has help2man or perl. +# Build the man page once in the srcdir, rather than in every VPATH build +# dir, to match how automake builds info pages. This is safe for 'make +# distcheck' since it is distributed pre-built. +$(srcdir)/m4.1: $(top_srcdir)/configure.ac $(top_srcdir)/src/m4.c + @if test -x ../src/m4$(EXEEXT) ; then \ + echo "Updating man page m4.1" ; \ + $(SHELL) $(top_srcdir)/missing --run \ + help2man --name="macro processor" --source=FSF \ + --info-page=m4 --output=$@ ../src/m4$(EXEEXT) ; \ + else \ + echo "WARNING: The \`man' page \`$@' cannot be updated yet."; \ + echo " Retry once the program executable is ready."; \ + fi diff --git a/src/bin/m4/doc/Makefile.in b/src/bin/m4/doc/Makefile.in new file mode 100644 index 0000000000..69183b5788 --- /dev/null +++ b/src/bin/m4/doc/Makefile.in @@ -0,0 +1,708 @@ +# Makefile.in generated by automake 1.10 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = doc +DIST_COMMON = $(m4_TEXINFOS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/stamp-vti \ + $(srcdir)/version.texi mdate-sh texinfo.tex +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \ + $(top_srcdir)/m4/alloca.m4 $(top_srcdir)/m4/allocsa.m4 \ + $(top_srcdir)/m4/cloexec.m4 $(top_srcdir)/m4/close-stream.m4 \ + $(top_srcdir)/m4/closeout.m4 $(top_srcdir)/m4/codeset.m4 \ + $(top_srcdir)/m4/config-h.m4 $(top_srcdir)/m4/eealloc.m4 \ + $(top_srcdir)/m4/eoverflow.m4 $(top_srcdir)/m4/error.m4 \ + $(top_srcdir)/m4/exitfail.m4 $(top_srcdir)/m4/extensions.m4 \ + $(top_srcdir)/m4/fatal-signal.m4 $(top_srcdir)/m4/fpending.m4 \ + $(top_srcdir)/m4/free.m4 $(top_srcdir)/m4/getopt.m4 \ + $(top_srcdir)/m4/gl_list.m4 $(top_srcdir)/m4/gnulib-comp.m4 \ + $(top_srcdir)/m4/inline.m4 $(top_srcdir)/m4/intmax_t.m4 \ + $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/longdouble.m4 \ + $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/mbchar.m4 \ + $(top_srcdir)/m4/mbiter.m4 $(top_srcdir)/m4/mbrtowc.m4 \ + $(top_srcdir)/m4/mbstate_t.m4 $(top_srcdir)/m4/memchr.m4 \ + $(top_srcdir)/m4/mkdtemp.m4 $(top_srcdir)/m4/mkstemp.m4 \ + $(top_srcdir)/m4/pathmax.m4 $(top_srcdir)/m4/quotearg.m4 \ + $(top_srcdir)/m4/regex.m4 $(top_srcdir)/m4/sig_atomic_t.m4 \ + $(top_srcdir)/m4/signalblocking.m4 \ + $(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/ssize_t.m4 \ + $(top_srcdir)/m4/stdarg.m4 $(top_srcdir)/m4/stdbool.m4 \ + $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/m4/stdint_h.m4 \ + $(top_srcdir)/m4/stdio-safer.m4 \ + $(top_srcdir)/m4/stdlib-safer.m4 $(top_srcdir)/m4/strcase.m4 \ + $(top_srcdir)/m4/strstr.m4 $(top_srcdir)/m4/strtol.m4 \ + $(top_srcdir)/m4/sys_stat_h.m4 $(top_srcdir)/m4/tempname.m4 \ + $(top_srcdir)/m4/tmpdir.m4 $(top_srcdir)/m4/ulonglong.m4 \ + $(top_srcdir)/m4/unistd-safer.m4 $(top_srcdir)/m4/unistd_h.m4 \ + $(top_srcdir)/m4/unlocked-io.m4 $(top_srcdir)/m4/vasnprintf.m4 \ + $(top_srcdir)/m4/vasprintf.m4 $(top_srcdir)/m4/wchar_t.m4 \ + $(top_srcdir)/m4/wcwidth.m4 $(top_srcdir)/m4/wint_t.m4 \ + $(top_srcdir)/m4/xalloc.m4 $(top_srcdir)/m4/xsize.m4 \ + $(top_srcdir)/m4/xvasprintf.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/lib/config.h +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +INFO_DEPS = $(srcdir)/m4.info +am__TEXINFO_TEX_DIR = $(srcdir) +DVIS = m4.dvi +PDFS = m4.pdf +PSS = m4.ps +HTMLS = m4.html +TEXINFOS = m4.texinfo +TEXI2DVI = texi2dvi +TEXI2PDF = $(TEXI2DVI) --pdf --batch +MAKEINFOHTML = $(MAKEINFO) --html +AM_MAKEINFOHTMLFLAGS = $(AM_MAKEINFOFLAGS) +DVIPS = dvips +am__installdirs = "$(DESTDIR)$(infodir)" "$(DESTDIR)$(man1dir)" +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +man1dir = $(mandir)/man1 +NROFF = nroff +MANS = $(man_MANS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ABSOLUTE_STDINT_H = @ABSOLUTE_STDINT_H@ +ABSOLUTE_SYS_STAT_H = @ABSOLUTE_SYS_STAT_H@ +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +ALLOCA_H = @ALLOCA_H@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@ +BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@ +BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@ +BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@ +BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EOVERFLOW = @EOVERFLOW@ +EXEEXT = @EXEEXT@ +GETOPT_H = @GETOPT_H@ +GREP = @GREP@ +HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ +HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@ +HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ +HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ +HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ +HAVE_STDINT_H = @HAVE_STDINT_H@ +HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@ +HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@ +HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ +HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@ +HAVE_WCHAR_H = @HAVE_WCHAR_H@ +HAVE__BOOL = @HAVE__BOOL@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBM4_LIBDEPS = @LIBM4_LIBDEPS@ +LIBM4_LTLIBDEPS = @LIBM4_LTLIBDEPS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +M4_LIBOBJS = @M4_LIBOBJS@ +M4_LTLIBOBJS = @M4_LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@ +SIZE_T_SUFFIX = @SIZE_T_SUFFIX@ +STDBOOL_H = @STDBOOL_H@ +STDINT_H = @STDINT_H@ +STRIP = @STRIP@ +SYS_STAT_H = @SYS_STAT_H@ +UNISTD_H = @UNISTD_H@ +VERSION = @VERSION@ +WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ +WINT_T_SUFFIX = @WINT_T_SUFFIX@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +info_TEXINFOS = m4.texinfo +m4_TEXINFOS = fdl.texi +man_MANS = m4.1 +EXTRA_DIST = $(man_MANS) gendocs_template +MAINTAINERCLEANFILES = $(man_MANS) gendocs_template +SUFFIXES = .1 +all: all-am + +.SUFFIXES: +.SUFFIXES: .1 .dvi .html .info .pdf .ps .texinfo +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +.texinfo.info: + restore=: && backupdir="$(am__leading_dot)am$$$$" && \ + am__cwd=`pwd` && cd $(srcdir) && \ + rm -rf $$backupdir && mkdir $$backupdir && \ + if ($(MAKEINFO) --version) >/dev/null 2>&1; then \ + for f in $@ $@-[0-9] $@-[0-9][0-9] $(@:.info=).i[0-9] $(@:.info=).i[0-9][0-9]; do \ + if test -f $$f; then mv $$f $$backupdir; restore=mv; else :; fi; \ + done; \ + else :; fi && \ + cd "$$am__cwd"; \ + if $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ + -o $@ $<; \ + then \ + rc=0; \ + cd $(srcdir); \ + else \ + rc=$$?; \ + cd $(srcdir) && \ + $$restore $$backupdir/* `echo "./$@" | sed 's|[^/]*$$||'`; \ + fi; \ + rm -rf $$backupdir; exit $$rc + +.texinfo.dvi: + TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ + MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ + $(TEXI2DVI) $< + +.texinfo.pdf: + TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ + MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ + $(TEXI2PDF) $< + +.texinfo.html: + rm -rf $(@:.html=.htp) + if $(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ + -o $(@:.html=.htp) $<; \ + then \ + rm -rf $@; \ + if test ! -d $(@:.html=.htp) && test -d $(@:.html=); then \ + mv $(@:.html=) $@; else mv $(@:.html=.htp) $@; fi; \ + else \ + if test ! -d $(@:.html=.htp) && test -d $(@:.html=); then \ + rm -rf $(@:.html=); else rm -Rf $(@:.html=.htp) $@; fi; \ + exit 1; \ + fi +$(srcdir)/m4.info: m4.texinfo $(srcdir)/version.texi $(m4_TEXINFOS) +m4.dvi: m4.texinfo $(srcdir)/version.texi $(m4_TEXINFOS) +m4.pdf: m4.texinfo $(srcdir)/version.texi $(m4_TEXINFOS) +m4.html: m4.texinfo $(srcdir)/version.texi $(m4_TEXINFOS) +$(srcdir)/version.texi: $(srcdir)/stamp-vti +$(srcdir)/stamp-vti: m4.texinfo $(top_srcdir)/configure + @(dir=.; test -f ./m4.texinfo || dir=$(srcdir); \ + set `$(SHELL) $(srcdir)/mdate-sh $$dir/m4.texinfo`; \ + echo "@set UPDATED $$1 $$2 $$3"; \ + echo "@set UPDATED-MONTH $$2 $$3"; \ + echo "@set EDITION $(VERSION)"; \ + echo "@set VERSION $(VERSION)") > vti.tmp + @cmp -s vti.tmp $(srcdir)/version.texi \ + || (echo "Updating $(srcdir)/version.texi"; \ + cp vti.tmp $(srcdir)/version.texi) + -@rm -f vti.tmp + @cp $(srcdir)/version.texi $@ + +mostlyclean-vti: + -rm -f vti.tmp + +maintainer-clean-vti: + -rm -f $(srcdir)/stamp-vti $(srcdir)/version.texi +.dvi.ps: + TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ + $(DVIPS) -o $@ $< + +uninstall-dvi-am: + @$(NORMAL_UNINSTALL) + @list='$(DVIS)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(dvidir)/$$f'"; \ + rm -f "$(DESTDIR)$(dvidir)/$$f"; \ + done + +uninstall-html-am: + @$(NORMAL_UNINSTALL) + @list='$(HTMLS)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -rf '$(DESTDIR)$(htmldir)/$$f'"; \ + rm -rf "$(DESTDIR)$(htmldir)/$$f"; \ + done + +uninstall-info-am: + @$(PRE_UNINSTALL) + @if test -d '$(DESTDIR)$(infodir)' && \ + (install-info --version && \ + install-info --version 2>&1 | sed 1q | grep -i -v debian) >/dev/null 2>&1; then \ + list='$(INFO_DEPS)'; \ + for file in $$list; do \ + relfile=`echo "$$file" | sed 's|^.*/||'`; \ + echo " install-info --info-dir='$(DESTDIR)$(infodir)' --remove '$(DESTDIR)$(infodir)/$$relfile'"; \ + install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$$relfile"; \ + done; \ + else :; fi + @$(NORMAL_UNINSTALL) + @list='$(INFO_DEPS)'; \ + for file in $$list; do \ + relfile=`echo "$$file" | sed 's|^.*/||'`; \ + relfile_i=`echo "$$relfile" | sed 's|\.info$$||;s|$$|.i|'`; \ + (if test -d "$(DESTDIR)$(infodir)" && cd "$(DESTDIR)$(infodir)"; then \ + echo " cd '$(DESTDIR)$(infodir)' && rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]"; \ + rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]; \ + else :; fi); \ + done + +uninstall-pdf-am: + @$(NORMAL_UNINSTALL) + @list='$(PDFS)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(pdfdir)/$$f'"; \ + rm -f "$(DESTDIR)$(pdfdir)/$$f"; \ + done + +uninstall-ps-am: + @$(NORMAL_UNINSTALL) + @list='$(PSS)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(psdir)/$$f'"; \ + rm -f "$(DESTDIR)$(psdir)/$$f"; \ + done + +dist-info: $(INFO_DEPS) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + list='$(INFO_DEPS)'; \ + for base in $$list; do \ + case $$base in \ + $(srcdir)/*) base=`echo "$$base" | sed "s|^$$srcdirstrip/||"`;; \ + esac; \ + if test -f $$base; then d=.; else d=$(srcdir); fi; \ + base_i=`echo "$$base" | sed 's|\.info$$||;s|$$|.i|'`; \ + for file in $$d/$$base $$d/$$base-[0-9] $$d/$$base-[0-9][0-9] $$d/$$base_i[0-9] $$d/$$base_i[0-9][0-9]; do \ + if test -f $$file; then \ + relfile=`expr "$$file" : "$$d/\(.*\)"`; \ + test -f $(distdir)/$$relfile || \ + cp -p $$file $(distdir)/$$relfile; \ + else :; fi; \ + done; \ + done + +mostlyclean-aminfo: + -rm -rf m4.aux m4.cp m4.cps m4.fn m4.fns m4.ky m4.kys m4.log m4.pg m4.pgs \ + m4.tmp m4.toc m4.tp m4.tps m4.vr m4.vrs m4.dvi m4.pdf m4.ps \ + m4.html + +maintainer-clean-aminfo: + @list='$(INFO_DEPS)'; for i in $$list; do \ + i_i=`echo "$$i" | sed 's|\.info$$||;s|$$|.i|'`; \ + echo " rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]"; \ + rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]; \ + done +install-man1: $(man1_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)" + @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 1*) ;; \ + *) ext='1' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst"; \ + done +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 1*) ;; \ + *) ext='1' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man1dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man1dir)/$$inst"; \ + done +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-info +check-am: all-am +check: check-am +all-am: Makefile $(INFO_DEPS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(infodir)" "$(DESTDIR)$(man1dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: $(DVIS) + +html: html-am + +html-am: $(HTMLS) + +info: info-am + +info-am: $(INFO_DEPS) + +install-data-am: install-info-am install-man + +install-dvi: install-dvi-am + +install-dvi-am: $(DVIS) + @$(NORMAL_INSTALL) + test -z "$(dvidir)" || $(MKDIR_P) "$(DESTDIR)$(dvidir)" + @list='$(DVIS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(dvidir)/$$f'"; \ + $(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(dvidir)/$$f"; \ + done +install-exec-am: + +install-html: install-html-am + +install-html-am: $(HTMLS) + @$(NORMAL_INSTALL) + test -z "$(htmldir)" || $(MKDIR_P) "$(DESTDIR)$(htmldir)" + @list='$(HTMLS)'; for p in $$list; do \ + if test -f "$$p" || test -d "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + if test -d "$$d$$p"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)/$$f'"; \ + $(MKDIR_P) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \ + echo " $(INSTALL_DATA) '$$d$$p'/* '$(DESTDIR)$(htmldir)/$$f'"; \ + $(INSTALL_DATA) "$$d$$p"/* "$(DESTDIR)$(htmldir)/$$f"; \ + else \ + echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(htmldir)/$$f'"; \ + $(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(htmldir)/$$f"; \ + fi; \ + done +install-info: install-info-am + +install-info-am: $(INFO_DEPS) + @$(NORMAL_INSTALL) + test -z "$(infodir)" || $(MKDIR_P) "$(DESTDIR)$(infodir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + list='$(INFO_DEPS)'; \ + for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + esac; \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + file_i=`echo "$$file" | sed 's|\.info$$||;s|$$|.i|'`; \ + for ifile in $$d/$$file $$d/$$file-[0-9] $$d/$$file-[0-9][0-9] \ + $$d/$$file_i[0-9] $$d/$$file_i[0-9][0-9] ; do \ + if test -f $$ifile; then \ + relfile=`echo "$$ifile" | sed 's|^.*/||'`; \ + echo " $(INSTALL_DATA) '$$ifile' '$(DESTDIR)$(infodir)/$$relfile'"; \ + $(INSTALL_DATA) "$$ifile" "$(DESTDIR)$(infodir)/$$relfile"; \ + else : ; fi; \ + done; \ + done + @$(POST_INSTALL) + @if (install-info --version && \ + install-info --version 2>&1 | sed 1q | grep -i -v debian) >/dev/null 2>&1; then \ + list='$(INFO_DEPS)'; \ + for file in $$list; do \ + relfile=`echo "$$file" | sed 's|^.*/||'`; \ + echo " install-info --info-dir='$(DESTDIR)$(infodir)' '$(DESTDIR)$(infodir)/$$relfile'";\ + install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$$relfile" || :;\ + done; \ + else : ; fi +install-man: install-man1 + +install-pdf: install-pdf-am + +install-pdf-am: $(PDFS) + @$(NORMAL_INSTALL) + test -z "$(pdfdir)" || $(MKDIR_P) "$(DESTDIR)$(pdfdir)" + @list='$(PDFS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(pdfdir)/$$f'"; \ + $(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(pdfdir)/$$f"; \ + done +install-ps: install-ps-am + +install-ps-am: $(PSS) + @$(NORMAL_INSTALL) + test -z "$(psdir)" || $(MKDIR_P) "$(DESTDIR)$(psdir)" + @list='$(PSS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(psdir)/$$f'"; \ + $(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(psdir)/$$f"; \ + done +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-aminfo \ + maintainer-clean-generic maintainer-clean-vti + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-aminfo mostlyclean-generic mostlyclean-vti + +pdf: pdf-am + +pdf-am: $(PDFS) + +ps: ps-am + +ps-am: $(PSS) + +uninstall-am: uninstall-dvi-am uninstall-html-am uninstall-info-am \ + uninstall-man uninstall-pdf-am uninstall-ps-am + +uninstall-man: uninstall-man1 + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic dist-info \ + distclean distclean-generic distdir dvi dvi-am html html-am \ + info info-am install install-am install-data install-data-am \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-man install-man1 install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-aminfo \ + maintainer-clean-generic maintainer-clean-vti mostlyclean \ + mostlyclean-aminfo mostlyclean-generic mostlyclean-vti pdf \ + pdf-am ps ps-am uninstall uninstall-am uninstall-dvi-am \ + uninstall-html-am uninstall-info-am uninstall-man \ + uninstall-man1 uninstall-pdf-am uninstall-ps-am + + +# Depend on configure.ac for version, m4.c for usage text. Do not depend on +# built m4 executable, since not everyone has help2man or perl. +# Build the man page once in the srcdir, rather than in every VPATH build +# dir, to match how automake builds info pages. This is safe for 'make +# distcheck' since it is distributed pre-built. +$(srcdir)/m4.1: $(top_srcdir)/configure.ac $(top_srcdir)/src/m4.c + @if test -x ../src/m4$(EXEEXT) ; then \ + echo "Updating man page m4.1" ; \ + $(SHELL) $(top_srcdir)/missing --run \ + help2man --name="macro processor" --source=FSF \ + --info-page=m4 --output=$@ ../src/m4$(EXEEXT) ; \ + else \ + echo "WARNING: The \`man' page \`$@' cannot be updated yet."; \ + echo " Retry once the program executable is ready."; \ + fi +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/bin/m4/doc/fdl.texi b/src/bin/m4/doc/fdl.texi new file mode 100644 index 0000000000..9c6d9afe3f --- /dev/null +++ b/src/bin/m4/doc/fdl.texi @@ -0,0 +1,452 @@ + +@node GNU Free Documentation License +@appendixsec GNU Free Documentation License + +@cindex FDL, GNU Free Documentation License +@center Version 1.2, November 2002 + +@display +Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc. +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@enumerate 0 +@item +PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document @dfn{free} in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +@item +APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The ``Document'', below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as ``you''. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section +of the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +@sc{ascii} without markup, Texinfo input format, La@TeX{} input +format, @acronym{SGML} or @acronym{XML} using a publicly available +@acronym{DTD}, and standard-conforming simple @acronym{HTML}, +PostScript or @acronym{PDF} designed for human modification. Examples +of transparent image formats include @acronym{PNG}, @acronym{XCF} and +@acronym{JPG}. Opaque formats include proprietary formats that can be +read and edited only by proprietary word processors, @acronym{SGML} or +@acronym{XML} for which the @acronym{DTD} and/or processing tools are +not generally available, and the machine-generated @acronym{HTML}, +PostScript or @acronym{PDF} produced by some word processors for +output purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section ``Entitled XYZ'' means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as ``Acknowledgements'', +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' +of such a section when you modify the Document means that it remains a +section ``Entitled XYZ'' according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +@item +VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +@item +COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +@item +MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +@enumerate A +@item +Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. + +@item +List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. + +@item +State on the Title page the name of the publisher of the +Modified Version, as the publisher. + +@item +Preserve all the copyright notices of the Document. + +@item +Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. + +@item +Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. + +@item +Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. + +@item +Include an unaltered copy of this License. + +@item +Preserve the section Entitled ``History'', Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled ``History'' in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. + +@item +Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the ``History'' section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. + +@item +For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve +the Title of the section, and preserve in the section all the +substance and tone of each of the contributor acknowledgements and/or +dedications given therein. + +@item +Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. + +@item +Delete any section Entitled ``Endorsements''. Such a section +may not be included in the Modified Version. + +@item +Do not retitle any existing section to be Entitled ``Endorsements'' or +to conflict in title with any Invariant Section. + +@item +Preserve any Warranty Disclaimers. +@end enumerate + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties---for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +@item +COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled ``History'' +in the various original documents, forming one section Entitled +``History''; likewise combine any sections Entitled ``Acknowledgements'', +and any sections Entitled ``Dedications''. You must delete all +sections Entitled ``Endorsements.'' + +@item +COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +@item +AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an ``aggregate'' if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +@item +TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled ``Acknowledgements'', +``Dedications'', or ``History'', the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +@item +TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document 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. + +@item +FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +@uref{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. +@end enumerate + +@page +@heading ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample +@group + Copyright (C) @var{year} @var{your name}. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. +@end group +@end smallexample + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the ``with...Texts.'' line with this: + +@smallexample +@group + with the Invariant Sections being @var{list their titles}, with + the Front-Cover Texts being @var{list}, and with the Back-Cover Texts + being @var{list}. +@end group +@end smallexample + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@c Local Variables: +@c ispell-local-pdict: "ispell-dict" +@c End: + diff --git a/src/bin/m4/doc/gendocs_template b/src/bin/m4/doc/gendocs_template new file mode 100755 index 0000000000..71fc286c96 --- /dev/null +++ b/src/bin/m4/doc/gendocs_template @@ -0,0 +1,91 @@ + + + + + +%%TITLE%% - GNU Project - Free Software Foundation (FSF) + + + + + + + + + + + + + +

%%TITLE%%

+ +
Free Software Foundation
+
last updated %%DATE%%
+

+ +  [image of the head of a GNU] + +

+
+ +

This manual (%%PACKAGE%%) is available in the following formats:

+ + + +

(This page generated by the %%SCRIPTNAME%% +script.)

+ + + + + diff --git a/src/bin/m4/doc/m4.1 b/src/bin/m4/doc/m4.1 new file mode 100644 index 0000000000..66e175f36b --- /dev/null +++ b/src/bin/m4/doc/m4.1 @@ -0,0 +1,136 @@ +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.35. +.TH M4 "1" "November 2006" "FSF" "User Commands" +.SH NAME +M4 \- macro processor +.SH SYNOPSIS +.B m4 +[\fIOPTION\fR]... [\fIFILE\fR]... +.SH DESCRIPTION +Process macros in FILEs. If no FILE or if FILE is `\-', standard input +is read. +.PP +Mandatory or optional arguments to long options are mandatory or optional +for short options too. +.SS "Operation modes:" +.TP +\fB\-\-help\fR +display this help and exit +.TP +\fB\-\-version\fR +output version information and exit +.TP +\fB\-E\fR, \fB\-\-fatal\-warnings\fR +stop execution after first warning +.TP +\fB\-i\fR, \fB\-\-interactive\fR +unbuffer output, ignore interrupts +.TP +\fB\-P\fR, \fB\-\-prefix\-builtins\fR +force a `m4_' prefix to all builtins +.TP +\fB\-Q\fR, \fB\-\-quiet\fR, \fB\-\-silent\fR +suppress some warnings for builtins +.SS "Preprocessor features:" +.TP +\fB\-D\fR, \fB\-\-define=NAME\fR[=\fIVALUE\fR] +define NAME as having VALUE, or empty +.TP +\fB\-I\fR, \fB\-\-include\fR=\fIDIRECTORY\fR +append DIRECTORY to include path +.TP +\fB\-s\fR, \fB\-\-synclines\fR +generate `#line NUM "FILE"' lines +.TP +\fB\-U\fR, \fB\-\-undefine\fR=\fINAME\fR +undefine NAME +.SS "Limits control:" +.TP +\fB\-G\fR, \fB\-\-traditional\fR +suppress all GNU extensions +.TP +\fB\-H\fR, \fB\-\-hashsize\fR=\fIPRIME\fR +set symbol lookup hash table size [509] +.TP +\fB\-L\fR, \fB\-\-nesting\-limit\fR=\fINUMBER\fR +change artificial nesting limit [1024] +.SS "Frozen state files:" +.TP +\fB\-F\fR, \fB\-\-freeze\-state\fR=\fIFILE\fR +produce a frozen state on FILE at end +.TP +\fB\-R\fR, \fB\-\-reload\-state\fR=\fIFILE\fR +reload a frozen state from FILE at start +.SS "Debugging:" +.TP +\fB\-d\fR, \fB\-\-debug\fR[=\fIFLAGS\fR] +set debug level (no FLAGS implies `aeq') +.TP +\fB\-\-debugfile\fR=\fIFILE\fR +redirect debug and trace output +.TP +\fB\-l\fR, \fB\-\-arglength\fR=\fINUM\fR +restrict macro tracing size +.TP +\fB\-t\fR, \fB\-\-trace\fR=\fINAME\fR +trace NAME when it is defined +.SS "FLAGS is any of:" +.TP +a +show actual arguments +.TP +c +show before collect, after collect and after call +.TP +e +show expansion +.TP +f +say current input file name +.TP +i +show changes in input files +.TP +l +say current input line number +.TP +p +show results of path searches +.TP +q +quote values as necessary, with a or e flag +.TP +t +trace for all macro calls, not only traceon'ed +.TP +x +add a unique macro call id, useful with c flag +.TP +V +shorthand for all of the above flags +.PP +If defined, the environment variable `M4PATH' is a colon\-separated list +of directories included after any specified by `\-I'. +.PP +Exit status is 0 for success, 1 for failure, 63 for frozen file version +mismatch, or whatever value was passed to the m4exit macro. +.SH AUTHOR +Written by Rene' Seindal. +.SH "REPORTING BUGS" +Report bugs to . +.SH COPYRIGHT +Copyright \(co 2006 Free Software Foundation, Inc. +.br +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +.SH "SEE ALSO" +The full documentation for +.B M4 +is maintained as a Texinfo manual. If the +.B info +and +.B M4 +programs are properly installed at your site, the command +.IP +.B info m4 +.PP +should give you access to the complete manual. diff --git a/src/bin/m4/doc/m4.info b/src/bin/m4/doc/m4.info new file mode 100644 index 0000000000..051ca7b79a --- /dev/null +++ b/src/bin/m4/doc/m4.info @@ -0,0 +1,6116 @@ +This is m4.info, produced by makeinfo version 4.8 from m4.texinfo. + + This manual is for GNU M4 (version 1.4.8, 20 November 2006), a +package containing an implementation of the m4 macro language. + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006 +Free Software Foundation, Inc. + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.2 or any later version published by the Free Software + Foundation; with no Invariant Sections, no Front-Cover Texts, and + no Back-Cover Texts. A copy of the license is included in the + section entitled "GNU Free Documentation License." + +INFO-DIR-SECTION GNU programming tools +START-INFO-DIR-ENTRY +* M4: (m4). A powerful macro processor. +END-INFO-DIR-ENTRY + + +File: m4.info, Node: Top, Next: Preliminaries, Up: (dir) + +GNU M4 +****** + +This manual is for GNU M4 (version 1.4.8, 20 November 2006), a package +containing an implementation of the m4 macro language. + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006 +Free Software Foundation, Inc. + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.2 or any later version published by the Free Software + Foundation; with no Invariant Sections, no Front-Cover Texts, and + no Back-Cover Texts. A copy of the license is included in the + section entitled "GNU Free Documentation License." + + GNU `m4' is an implementation of the traditional UNIX macro +processor. It is mostly SVR4 compatible, although it has some +extensions (for example, handling more than 9 positional parameters to +macros). `m4' also has builtin functions for including files, running +shell commands, doing arithmetic, etc. Autoconf needs GNU `m4' for +generating `configure' scripts, but not for running them. + + GNU `m4' was originally written by Rene' Seindal, with subsequent +changes by Franc,ois Pinard and other volunteers on the Internet. All +names and email addresses can be found in the files `m4-1.4.8/AUTHORS' +and `m4-1.4.8/THANKS' from the GNU M4 distribution. + + This is release 1.4.8. It is now considered stable: future +releases in the 1.4.x series are only meant to fix bugs, increase speed, +or improve documentation. However... + + An experimental feature, which would improve `m4' usefulness, allows +for changing the syntax for what is a "word" in `m4'. You should use: + ./configure --enable-changeword + if you want this feature compiled in. The current implementation +slows down `m4' considerably and is hardly acceptable. In the future, +`m4' 2.0 will come with a different set of new features that provide +similar capabilities, but without the inefficiencies, so changeword +will go away and _you should not count on it_. + +* Menu: + +* Preliminaries:: Introduction and preliminaries +* Invoking m4:: Invoking `m4' +* Syntax:: Lexical and syntactic conventions + +* Macros:: How to invoke macros +* Definitions:: How to define new macros +* Conditionals:: Conditionals, loops, and recursion + +* Debugging:: How to debug macros and input + +* Input Control:: Input control +* File Inclusion:: File inclusion +* Diversions:: Diverting and undiverting output + +* Text handling:: Macros for text handling +* Arithmetic:: Macros for doing arithmetic +* Shell commands:: Macros for running shell commands +* Miscellaneous:: Miscellaneous builtin macros +* Frozen files:: Fast loading of frozen state + +* Compatibility:: Compatibility with other versions of `m4' +* Answers:: Correct version of some examples +* Copying This Manual:: How to make copies of this manual +* Indices:: Indices of concepts and macros + + --- The Detailed Node Listing --- + +Introduction and preliminaries + +* Intro:: Introduction to `m4' +* History:: Historical references +* Bugs:: Problems and bugs +* Manual:: Using this manual + +Invoking `m4' + +* Operation modes:: Command line options for operation modes +* Preprocessor features:: Command line options for preprocessor features +* Limits control:: Command line options for limits control +* Frozen state:: Command line options for frozen state +* Debugging options:: Command line options for debugging +* Command line files:: Specifying input files on the command line + +Lexical and syntactic conventions + +* Names:: Macro names +* Quoted strings:: Quoting input to `m4' +* Comments:: Comments in `m4' input +* Other tokens:: Other kinds of input tokens +* Input processing:: How `m4' copies input to output + +How to invoke macros + +* Invocation:: Macro invocation +* Inhibiting Invocation:: Preventing macro invocation +* Macro Arguments:: Macro arguments +* Quoting Arguments:: On Quoting Arguments to macros +* Macro expansion:: Expanding macros + +How to define new macros + +* Define:: Defining a new macro +* Arguments:: Arguments to macros +* Pseudo Arguments:: Special arguments to macros +* Undefine:: Deleting a macro +* Defn:: Renaming macros +* Pushdef:: Temporarily redefining macros + +* Indir:: Indirect call of macros +* Builtin:: Indirect call of builtins + +Conditionals, loops, and recursion + +* Ifdef:: Testing if a macro is defined +* Ifelse:: If-else construct, or multibranch +* Shift:: Recursion in `m4' +* Forloop:: Iteration by counting +* Foreach:: Iteration by list contents + +How to debug macros and input + +* Dumpdef:: Displaying macro definitions +* Trace:: Tracing macro calls +* Debug Levels:: Controlling debugging output +* Debug Output:: Saving debugging output + +Input control + +* Dnl:: Deleting whitespace in input +* Changequote:: Changing the quote characters +* Changecom:: Changing the comment delimiters +* Changeword:: Changing the lexical structure of words +* M4wrap:: Saving text until end of input + +File inclusion + +* Include:: Including named files +* Search Path:: Searching for include files + +Diverting and undiverting output + +* Divert:: Diverting output +* Undivert:: Undiverting output +* Divnum:: Diversion numbers +* Cleardivert:: Discarding diverted text + +Macros for text handling + +* Len:: Calculating length of strings +* Index macro:: Searching for substrings +* Regexp:: Searching for regular expressions +* Substr:: Extracting substrings +* Translit:: Translating characters +* Patsubst:: Substituting text by regular expression +* Format:: Formatting strings (printf-like) + +Macros for doing arithmetic + +* Incr:: Decrement and increment operators +* Eval:: Evaluating integer expressions + +Macros for running shell commands + +* Platform macros:: Determining the platform +* Syscmd:: Executing simple commands +* Esyscmd:: Reading the output of commands +* Sysval:: Exit status +* Mkstemp:: Making temporary files + +Miscellaneous builtin macros + +* Errprint:: Printing error messages +* Location:: Printing current location +* M4exit:: Exiting from `m4' + +Fast loading of frozen state + +* Using frozen files:: Using frozen files +* Frozen file format:: Frozen file format + +Compatibility with other versions of `m4' + +* Extensions:: Extensions in GNU M4 +* Incompatibilities:: Facilities in System V m4 not in GNU M4 +* Other Incompatibilities:: Other incompatibilities + +Correct version of some examples + +* Improved exch:: Solution for `exch' +* Improved forloop:: Solution for `forloop' +* Improved foreach:: Solution for `foreach' +* Improved cleardivert:: Solution for `cleardivert' +* Improved fatal_error:: Solution for `fatal_error' + +How to make copies of this manual + +* GNU Free Documentation License:: License for copying this manual + +Indices of concepts and macros + +* Concept index:: Index for many concepts +* Macro index:: Index for all `m4' macros + + +File: m4.info, Node: Preliminaries, Next: Invoking m4, Prev: Top, Up: Top + +1 Introduction and preliminaries +******************************** + +This first chapter explains what GNU `m4' is, where `m4' comes from, +how to read and use this documentation, how to call the `m4' program, +and how to report bugs about it. It concludes by giving tips for +reading the remainder of the manual. + + The following chapters then detail all the features of the `m4' +language. + +* Menu: + +* Intro:: Introduction to `m4' +* History:: Historical references +* Bugs:: Problems and bugs +* Manual:: Using this manual + + +File: m4.info, Node: Intro, Next: History, Up: Preliminaries + +1.1 Introduction to `m4' +======================== + +`m4' is a macro processor, in the sense that it copies its input to the +output, expanding macros as it goes. Macros are either builtin or +user-defined, and can take any number of arguments. Besides just doing +macro expansion, `m4' has builtin functions for including named files, +running shell commands, doing integer arithmetic, manipulating text in +various ways, performing recursion, etc.... `m4' can be used either as +a front-end to a compiler, or as a macro processor in its own right. + + The `m4' macro processor is widely available on all UNIXes, and has +been standardized by POSIX. Usually, only a small percentage of users +are aware of its existence. However, those who find it often become +committed users. The popularity of GNU Autoconf, which requires GNU +`m4' for _generating_ `configure' scripts, is an incentive for many to +install it, while these people will not themselves program in `m4'. +GNU `m4' is mostly compatible with the System V, Release 3 version, +except for some minor differences. *Note Compatibility::, for more +details. + + Some people find `m4' to be fairly addictive. They first use `m4' +for simple problems, then take bigger and bigger challenges, learning +how to write complex sets of `m4' macros along the way. Once really +addicted, users pursue writing of sophisticated `m4' applications even +to solve simple problems, devoting more time debugging their `m4' +scripts than doing real work. Beware that `m4' may be dangerous for +the health of compulsive programmers. + + +File: m4.info, Node: History, Next: Bugs, Prev: Intro, Up: Preliminaries + +1.2 Historical references +========================= + +`GPM' was an important ancestor of `m4'. See C. Stratchey: "A General +Purpose Macro generator", Computer Journal 8,3 (1965), pp. 225 ff. +`GPM' is also succinctly described into David Gries classic "Compiler +Construction for Digital Computers". + + The classic B. Kernighan and P.J. Plauger: "Software Tools", +Addison-Wesley, Inc. (1976) describes and implements a Unix +macro-processor language, which inspired Dennis Ritchie to write `m3', +a macro processor for the AP-3 minicomputer. + + Kernighan and Ritchie then joined forces to develop the original +`m4', as described in "The M4 Macro Processor", Bell Laboratories +(1977). It had only 21 builtin macros. + + While `GPM' was more _pure_, `m4' is meant to deal with the true +intricacies of real life: macros can be recognized without being +pre-announced, skipping whitespace or end-of-lines is easier, more +constructs are builtin instead of derived, etc. + + Originally, the Kernighan and Plauger macro-processor, and then +`m3', formed the engine for the Rational FORTRAN preprocessor, that is, +the `Ratfor' equivalent of `cpp'. Later, `m4' was used as a frontend +for `Ratfor', `C' and `Cobol'. + + Rene' Seindal released his implementation of `m4', GNU `m4', in +1990, with the aim of removing the artificial limitations in many of +the traditional `m4' implementations, such as maximum line length, +macro size, or number of macros. + + The late Professor A. Dain Samples described and implemented a +further evolution in the form of `M5': "User's Guide to the M5 Macro +Language: 2nd edition", Electronic Announcement on comp.compilers +newsgroup (1992). + + Franc,ois Pinard took over maintenance of GNU `m4' in 1992, until +1994 when he released GNU `m4' 1.4, which was the stable release for 10 +years. It was at this time that GNU Autoconf decided to require GNU +`m4' as its underlying engine, since all other implementations of `m4' +had too many limitations. + + More recently, in 2004, Paul Eggert released 1.4.1 and 1.4.2 which +addressed some long standing bugs in the venerable 1.4 release. Then +in 2005 Gary V. Vaughan collected together the many patches to GNU `m4' +1.4 that were floating around the net and released 1.4.3 and 1.4.4. +And in 2006, Eric Blake joined the team and prepared patches for the +release of 1.4.5, 1.4.6, 1.4.7, and 1.4.8. + + Meanwhile, development has continued on new features for `m4', such +as dynamic module loading and additional builtins. When complete, GNU +`m4' 2.0 will start a new series of releases. + + +File: m4.info, Node: Bugs, Next: Manual, Prev: History, Up: Preliminaries + +1.3 Problems and bugs +===================== + +If you have problems with GNU M4 or think you've found a bug, please +report it. Before reporting a bug, make sure you've actually found a +real bug. Carefully reread the documentation and see if it really says +you can do what you're trying to do. If it's not clear whether you +should be able to do something or not, report that too; it's a bug in +the documentation! + + Before reporting a bug or trying to fix it yourself, try to isolate +it to the smallest possible input file that reproduces the problem. +Then send us the input file and the exact results `m4' gave you. Also +say what you expected to occur; this will help us decide whether the +problem was really in the documentation. + + Once you've got a precise problem, send e-mail to (Internet) +. Please include the version number of `m4' you are +using. You can get this information with the command `m4 --version'. +Also provide details about the platform you are executing on. + + Non-bug suggestions are always welcome as well. If you have +questions about things that are unclear in the documentation or are +just obscure features, please report them too. + + +File: m4.info, Node: Manual, Prev: Bugs, Up: Preliminaries + +1.4 Using this manual +===================== + +This manual contains a number of examples of `m4' input and output, and +a simple notation is used to distinguish input, output and error +messages from `m4'. Examples are set out from the normal text, and +shown in a fixed width font, like this + + This is an example of an example! + + To distinguish input from output, all output from `m4' is prefixed +by the string `=>', and all error messages by the string `error-->'. +Thus + + Example of input line + =>Output line from m4 + error-->and an error message + + The sequence `^D' in an example indicates the end of the input file. +The majority of these examples are self-contained, and you can run them +with similar results by invoking `m4 -d'. In fact, the testsuite that +is bundled in the GNU M4 package consists of the examples in this +document! + + As each of the predefined macros in `m4' is described, a prototype +call of the macro will be shown, giving descriptive names to the +arguments, e.g., + + -- Composite: example (STRING, [COUNT = `1'], [ARGUMENT]...) + This is a sample prototype. There is not really a macro named + `example', but this documents that if there were, it would be a + Composite macro, rather than a Builtin. It requires at least one + argument, STRING. Remember that in `m4', there must not be a + space between the macro name and the opening parenthesis, unless + it was intended to call the macro without any arguments. The + brackets around COUNT and ARGUMENT show that these arguments are + optional. If COUNT is omitted, the macro behaves as if count were + `1', whereas if ARGUMENT is omitted, the macro behaves as if it + were the empty string. A blank argument is not the same as an + omitted argument. For example, `example(`a')', `example(`a',`1')', + and `example(`a',`1',)' would behave identically with COUNT set to + `1'; while `example(`a',)' and `example(`a',`')' would explicitly + pass the empty string for COUNT. The ellipses (`...') show that + the macro processes additional arguments after ARGUMENT, rather + than ignoring them. + + All macro arguments in `m4' are strings, but some are given special +interpretation, e.g., as numbers, file names, regular expressions, etc. +The documentation for each macro will state how the parameters are +interpreted, and what happens if the argument cannot be parsed +according to the desired interpretation. Unless specified otherwise, a +parameter specified to be a number is parsed as a decimal, even if the +argument has leading zeros; and parsing the empty string as a number +results in 0 rather than an error, although a warning will be issued. + + This document consistently writes and uses "builtin", without a +hyphen, as if it were an English word. This is how the `builtin' +primitive is spelled within `m4'. + + +File: m4.info, Node: Invoking m4, Next: Syntax, Prev: Preliminaries, Up: Top + +2 Invoking `m4' +*************** + +The format of the `m4' command is: + + `m4' [OPTION...] [FILE...] + + All options begin with `-', or if long option names are used, with +`--'. A long option name need not be written completely, any +unambiguous prefix is sufficient. POSIX requires `m4' to recognize +arguments intermixed with files, even when `POSIXLY_CORRECT' is set in +the environment. Most options take effect at startup regardless of +their position, but some are documented below as taking effect after +any files that occurred earlier in the command line. The argument `--' +is a marker to denote the end of options. + + With short options, options that do not take arguments may be +combined into a single command line argument with subsequent options, +options with mandatory arguments may be provided either as a single +command line argument or as two arguments, and options with optional +arguments must be provided as a single argument. In other words, `m4 +-QPDfoo -d a -d+f' is equivalent to `m4 -Q -P -D foo -d -d+f -- ./a', +although the latter form is considered canonical. + + With long options, options with mandatory arguments may be provided +with an equal sign (`=') in a single argument, or as two arguments, and +options with optional arguments must be provided as a single argument. +In other words, `m4 --def foo --debug a' is equivalent to `m4 +--define=foo --debug= -- ./a', although the latter form is considered +canonical (not to mention more robust, in case a future version of `m4' +introduces an option named `--default'). + + `m4' understands the following options, grouped by functionality. + +* Menu: + +* Operation modes:: Command line options for operation modes +* Preprocessor features:: Command line options for preprocessor features +* Limits control:: Command line options for limits control +* Frozen state:: Command line options for frozen state +* Debugging options:: Command line options for debugging +* Command line files:: Specifying input files on the command line + + +File: m4.info, Node: Operation modes, Next: Preprocessor features, Up: Invoking m4 + +2.1 Command line options for operation modes +============================================ + +Several options control the overall operation of `m4': + +`--help' + Print a help summary on standard output, then immediately exit + `m4' without reading any input files or performing any other + actions. + +`--version' + Print the version number of the program on standard output, then + immediately exit `m4' without reading any input files or + performing any other actions. + +`-E' +`--fatal-warnings' + Stop execution and exit `m4' once the first warning has been + issued, considering all of them to be fatal. + +`-i' +`--interactive' +`-e' + Makes this invocation of `m4' interactive. This means that all + output will be unbuffered, and interrupts will be ignored. The + spelling `-e' exists for compatibility with other `m4' + implementations, and issues a warning because it may be withdrawn + in a future version of GNU M4. + +`-P' +`--prefix-builtins' + Internally modify _all_ builtin macro names so they all start with + the prefix `m4_'. For example, using this option, one should write + `m4_define' instead of `define', and `m4___file__' instead of + `__file__'. This option has no effect if `-R' is also specified. + +`-Q' +`--quiet' +`--silent' + Suppress warnings, such as missing or superfluous arguments in + macro calls, or treating the empty string as zero. + +`-W REGEXP' +`--word-regexp=REGEXP' + Use REGEXP as an alternative syntax for macro names. This + experimental option will not be present in all GNU `m4' + implementations (*note Changeword::). + + +File: m4.info, Node: Preprocessor features, Next: Limits control, Prev: Operation modes, Up: Invoking m4 + +2.2 Command line options for preprocessor features +================================================== + +Several options allow `m4' to behave more like a preprocessor. Macro +definitions and deletions can be made on the command line, the search +path can be altered, and the output file can track where the input came +from. These features occur with the following options: + +`-D NAME[=VALUE]' +`--define=NAME[=VALUE]' + This enters NAME into the symbol table, before any input files are + read. If `=VALUE' is missing, the value is taken to be the empty + string. The VALUE can be any string, and the macro can be defined + to take arguments, just as if it was defined from within the + input. This option may be given more than once; order with + respect to file names is significant, and redefining the same NAME + loses the previous value. + +`-I DIRECTORY' +`--include=DIRECTORY' + Make `m4' search DIRECTORY for included files that are not found + in the current working directory. *Note Search Path::, for more + details. This option may be given more than once. + +`-s' +`--synclines' + Generate synchronization lines, for use by the C preprocessor or + other similar tools. Order is significant with respect to file + names. This option is useful, for example, when `m4' is used as a + front end to a compiler. Source file name and line number + information is conveyed by directives of the form `#line LINENUM + "FILE"', which are inserted as needed into the middle of the + output. Such directives mean that the following line originated + or was expanded from the contents of input file FILE at line + LINENUM. The `"FILE"' part is often omitted when the file name + did not change from the previous directive. + + Synchronization directives are always given on complete lines by + themselves. When a synchronization discrepancy occurs in the + middle of an output line, the associated synchronization directive + is delayed until the beginning of the next generated line. + +`-U NAME' +`--undefine=NAME' + This deletes any predefined meaning NAME might have. Obviously, + only predefined macros can be deleted in this way. This option + may be given more than once; undefining a NAME that does not have a + definition is silently ignored. Order is significant with respect + to file names. + + +File: m4.info, Node: Limits control, Next: Frozen state, Prev: Preprocessor features, Up: Invoking m4 + +2.3 Command line options for limits control +=========================================== + +There are some limits within `m4' that can be tuned. For +compatibility, `m4' also accepts some options that control limits in +other implementations, but which are automatically unbounded (limited +only by your hardware and operating system constraints) in GNU `m4'. + +`-G' +`--traditional' + Suppress all the extensions made in this implementation, compared + to the System V version. *Note Compatibility::, for a list of + these. + +`-H NUM' +`--hashsize=NUM' + Make the internal hash table for symbol lookup be NUM entries big. + For better performance, the number should be prime, but this is not + checked. The default is 509 entries. It should not be necessary + to increase this value, unless you define an excessive number of + macros. + +`-L NUM' +`--nesting-limit=NUM' + Artificially limit the nesting of macro calls to NUM levels, + stopping program execution if this limit is ever exceeded. When + not specified, nesting is limited to 1024 levels. A value of zero + means unlimited; but then heavily nested code could potentially + cause a stack overflow. + + The precise effect of this option might be more correctly + associated with textual nesting than dynamic recursion. It has + been useful when some complex `m4' input was generated by + mechanical means. Most users would never need this option. If + shown to be obtrusive, this option (which is still experimental) + might well disappear. + + This option does _not_ have the ability to break endless + rescanning loops, since these do not necessarily consume much + memory or stack space. Through clever usage of rescanning loops, + one can request complex, time-consuming computations from `m4' + with useful results. Putting limitations in this area would break + `m4' power. There are many pathological cases: + `define(`a', `a')a' is only the simplest example (but *note + Compatibility::). Expecting GNU `m4' to detect these would be a + little like expecting a compiler system to detect and diagnose + endless loops: it is a quite _hard_ problem in general, if not + undecidable! + +`-B NUM' +`-S NUM' +`-T NUM' + These options are present for compatibility with System V `m4', but + do nothing in this implementation. They may disappear in future + releases, and issue a warning to that effect. + +`-N NUM' +`--diversions=NUM' + These options are present only for compatibility with previous + versions of GNU `m4', and were controlling the number of possible + diversions which could be used at the same time. They do nothing, + because there is no fixed limit anymore. They may disappear in + future releases, and issue a warning to that effect. + + +File: m4.info, Node: Frozen state, Next: Debugging options, Prev: Limits control, Up: Invoking m4 + +2.4 Command line options for frozen state +========================================= + +GNU `m4' comes with a feature of freezing internal state (*note Frozen +files::). This can be used to speed up `m4' execution when reusing a +common initialization script. + +`-F FILE' +`--freeze-state=FILE' + Once execution is finished, write out the frozen state on the + specified FILE. It is conventional, but not required, for FILE to + end in `.m4f'. + +`-R FILE' +`--reload-state=FILE' + Before execution starts, recover the internal state from the + specified frozen FILE. The options `-D', `-U', and `-t' take + effect after state is reloaded, but before the input files are + read. + + +File: m4.info, Node: Debugging options, Next: Command line files, Prev: Frozen state, Up: Invoking m4 + +2.5 Command line options for debugging +====================================== + +Finally, there are several options for aiding in debugging `m4' scripts. + +`-d[FLAGS]' +`--debug[=FLAGS]' + Set the debug-level according to the flags FLAGS. The debug-level + controls the format and amount of information presented by the + debugging functions. *Note Debug Levels::, for more details on + the format and meaning of FLAGS. If omitted, FLAGS defaults to + `aeq'. + +`--debugfile=FILE' +`-o FILE' +`--error-output=FILE' + Redirect `dumpdef' output, debug messages, and trace output to the + named FILE. Warnings, error messages, and `errprint' output are + still printed to standard error. If unspecified, debug output goes + to standard error; if empty, debug output is discarded. *Note + Debug Output::, for more details. The spellings `-o' and + `--error-output' are misleading and inconsistent with other GNU + tools; for now they are silently accepted as synonyms of + `--debugfile', but in a future version of M4, using them will + cause a warning to be issued. + +`-l NUM' +`--arglength=NUM' + Restrict the size of the output generated by macro tracing to NUM + characters per trace line. If unspecified or zero, output is + unlimited. *Note Debug Levels::, for more details. + +`-t NAME' +`--trace=NAME' + This enables tracing for the macro NAME, at any point where it is + defined. NAME need not be defined when this option is given. + This option may be given more than once, and order is significant + with respect to file names. *Note Trace::, for more details. + + +File: m4.info, Node: Command line files, Prev: Debugging options, Up: Invoking m4 + +2.6 Specifying input files on the command line +============================================== + +The remaining arguments on the command line are taken to be input file +names. If no names are present, standard input is read. A file name +of `-' is taken to mean standard input. It is conventional, but not +required, for input files to end in `.m4'. + + The input files are read in the sequence given. Standard input can +be read more than once, so the file name `-' may appear multiple times +on the command line; this makes a difference when input is from a +terminal or other special file type. It is an error if an input file +ends in the middle of argument collection, a comment, or a quoted +string. + + The options `--define' (`-D'), `--undefine' (`-U'), `--synclines' +(`-s'), and `--trace' (`-t') only take effect after processing input +from any file names that occur earlier on the command line. + + If none of the input files invoked `m4exit' (*note M4exit::), the +exit status of `m4' will be 0 for success, 1 for general failure (such +as problems with reading an input file), and 63 for version mismatch +(*note Using frozen files::). + + If you need to read a file whose name starts with a `-', you can +specify it as `./-file', or use `--' to mark the end of options. + + +File: m4.info, Node: Syntax, Next: Macros, Prev: Invoking m4, Up: Top + +3 Lexical and syntactic conventions +*********************************** + +As `m4' reads its input, it separates it into "tokens". A token is +either a name, a quoted string, or any single character, that is not a +part of either a name or a string. Input to `m4' can also contain +comments. GNU `m4' does not yet understand locales; all operations are +byte-oriented rather than character-oriented. However, `m4' is +eight-bit clean, so you can use non-ASCII characters in quoted strings +(*note Changequote::), comments (*note Changecom::), and macro names +(*note Indir::), with the exception of the NUL character (the zero byte +`'\0''). + +* Menu: + +* Names:: Macro names +* Quoted strings:: Quoting input to `m4' +* Comments:: Comments in `m4' input +* Other tokens:: Other kinds of input tokens +* Input processing:: How `m4' copies input to output + + +File: m4.info, Node: Names, Next: Quoted strings, Up: Syntax + +3.1 Macro names +=============== + +A name is any sequence of letters, digits, and the character `_' +(underscore), where the first character is not a digit. `m4' will use +the longest such sequence found in the input. If a name has a macro +definition, it will be subject to macro expansion (*note Macros::). +Names are case-sensitive. + + Examples of legal names are: `foo', `_tmp', and `name01'. + + +File: m4.info, Node: Quoted strings, Next: Comments, Prev: Names, Up: Syntax + +3.2 Quoting input to `m4' +========================= + +A quoted string is a sequence of characters surrounded by quote +strings, defaulting to ``' and `'', where the nested begin and end +quotes within the string are balanced. The value of a string token is +the text, with one level of quotes stripped off. Thus + + `' + => + +is the empty string, and double-quoting turns into single-quoting. + + ``quoted'' + =>`quoted' + + The quote characters can be changed at any time, using the builtin +macro `changequote'. *Note Changequote::, for more information. + + +File: m4.info, Node: Comments, Next: Other tokens, Prev: Quoted strings, Up: Syntax + +3.3 Comments in `m4' input +========================== + +Comments in `m4' are normally delimited by the characters `#' and +newline. All characters between the comment delimiters are ignored, +but the entire comment (including the delimiters) is passed through to +the output--comments are _not_ discarded by `m4'. + + Comments cannot be nested, so the first newline after a `#' ends the +comment. The commenting effect of the begin-comment string can be +inhibited by quoting it. + + `quoted text' # `commented text' + =>quoted text # `commented text' + `quoting inhibits' `#' `comments' + =>quoting inhibits # comments + + The comment delimiters can be changed to any string at any time, +using the builtin macro `changecom'. *Note Changecom::, for more +information. + + +File: m4.info, Node: Other tokens, Next: Input processing, Prev: Comments, Up: Syntax + +3.4 Other kinds of input tokens +=============================== + +Any character, that is neither a part of a name, nor of a quoted string, +nor a comment, is a token by itself. When not in the context of macro +expansion, all of these tokens are just copied to output. However, +during macro expansion, whitespace characters (space, tab, newline, +formfeed, carriage return, vertical tab), parentheses (`(' and `)'), +comma (`,'), and dollar (`$') have additional roles, explained later. + + +File: m4.info, Node: Input processing, Prev: Other tokens, Up: Syntax + +3.5 How `m4' copies input to output +=================================== + +As `m4' reads the input token by token, it will copy each token +directly to the output immediately. + + The exception is when it finds a word with a macro definition. In +that case `m4' will calculate the macro's expansion, possibly reading +more input to get the arguments. It then inserts the expansion in front +of the remaining input. In other words, the resulting text from a macro +call will be read and parsed into tokens again. + + `m4' expands a macro as soon as possible. If it finds a macro call +when collecting the arguments to another, it will expand the second +call first. For a running example, examine how `m4' handles this input: + + format(`Result is %d', eval(`2**15')) + +First, `m4' sees that the token `format' is a macro name, so it +collects the tokens `(', ``Result is %d'', `,', and ` ', before +encountering another potential macro. Sure enough, `eval' is a macro +name, so the nested argument collection picks up `(', ``2**15'', and +`)', invoking the eval macro with the lone argument of `2**15'. The +expansion of `eval(2**15)' is `32768', which is then rescanned as the +five tokens `3', `2', `7', `6', and `8'; and combined with the next +`)', the format macro now has all its arguments, as if the user had +typed: + + format(`Result is %d', 32768) + +The format macro expands to `Result is 32768', and we have another +round of scanning for the tokens `Result', ` ', `is', ` ', `3', `2', +`7', `6', and `8'. None of these are macros, so the final output is + + =>Result is 32768 + + The order in which `m4' expands the macros can be explored using the +trace facilities of GNU `m4' (*note Trace::). + + This process continues until there are no more macro calls to expand +and all the input has been consumed. + + +File: m4.info, Node: Macros, Next: Definitions, Prev: Syntax, Up: Top + +4 How to invoke macros +********************** + +This chapter covers macro invocation, macro arguments and how macro +expansion is treated. + +* Menu: + +* Invocation:: Macro invocation +* Inhibiting Invocation:: Preventing macro invocation +* Macro Arguments:: Macro arguments +* Quoting Arguments:: On Quoting Arguments to macros +* Macro expansion:: Expanding macros + + +File: m4.info, Node: Invocation, Next: Inhibiting Invocation, Up: Macros + +4.1 Macro invocation +==================== + +Macro invocations has one of the forms + + name + +which is a macro invocation without any arguments, or + + name(arg1, arg2, ..., argN) + +which is a macro invocation with N arguments. Macros can have any +number of arguments. All arguments are strings, but different macros +might interpret the arguments in different ways. + + The opening parenthesis _must_ follow the NAME directly, with no +spaces in between. If it does not, the macro is called with no +arguments at all. + + For a macro call to have no arguments, the parentheses _must_ be +left out. The macro call + + name() + +is a macro call with one argument, which is the empty string, not a call +with no arguments. + + +File: m4.info, Node: Inhibiting Invocation, Next: Macro Arguments, Prev: Invocation, Up: Macros + +4.2 Preventing macro invocation +=============================== + +An innovation of the `m4' language, compared to some of its +predecessors (like Stratchey's `GPM', for example), is the ability to +recognize macro calls without resorting to any special, prefixed +invocation character. While generally useful, this feature might +sometimes be the source of spurious, unwanted macro calls. So, GNU +`m4' offers several mechanisms or techniques for inhibiting the +recognition of names as macro calls. + + First of all, many builtin macros cannot meaningfully be called +without arguments. For any of these macros, whenever an opening +parenthesis does not immediately follow their name, the builtin macro +call is not triggered. This solves the most usual cases, like for +`include' or `eval'. Later in this document, the sentence "This macro +is recognized only with parameters" refers to this specific provision. + + There is also a command line option (`--prefix-builtins', or `-P', +*note Invoking m4: Operation modes.) that renames all builtin macros +with a prefix of `m4_' at startup. The option has no effect whatsoever +on user defined macros. For example, with this option, one has to +write `m4_dnl' and even `m4_m4exit'. It also has no effect on whether +a macro requires parameters. + + Another alternative is to redefine problematic macros to a name less +likely to cause conflicts, *Note Definitions::. + + If your version of GNU `m4' has the `changeword' feature compiled +in, it offers far more flexibility in specifying the syntax of macro +names, both builtin or user-defined. *Note Changeword::, for more +information on this experimental feature. + + Of course, the simplest way to prevent a name from being interpreted +as a call to an existing macro is to quote it. The remainder of this +section studies a little more deeply how quoting affects macro +invocation, and how quoting can be used to inhibit macro invocation. + + Even if quoting is usually done over the whole macro name, it can +also be done over only a few characters of this name (provided, of +course, that the unquoted portions are not also a macro). It is also +possible to quote the empty string, but this works only _inside_ the +name. For example: + + `divert' + =>divert + `d'ivert + =>divert + di`ver't + =>divert + div`'ert + =>divert + +all yield the string `divert'. While in both: + + `'divert + => + divert`' + => + +the `divert' builtin macro will be called, which expands to the empty +string. + + The output of macro evaluations is always rescanned. The following +example would yield the string `de', exactly as if `m4' has been given +`substr(`abcde', `3', `2')' as input: + + define(`x', `substr(ab') + => + define(`y', `cde, `3', `2')') + => + x`'y + =>de + + Unquoted strings on either side of a quoted string are subject to +being recognized as macro names. In the following example, quoting the +empty string allows for the second `macro' to be recognized as such: + + define(`macro', `m') + => + macro(`m')macro + =>mmacro + macro(`m')`'macro + =>mm + + Quoting may prevent recognizing as a macro name the concatenation of +a macro expansion with the surrounding characters. In this example: + + define(`macro', `di$1') + => + macro(`v')`ert' + =>divert + macro(`v')ert + => + +the input will produce the string `divert'. When the quotes were +removed, the `divert' builtin was called instead. + + +File: m4.info, Node: Macro Arguments, Next: Quoting Arguments, Prev: Inhibiting Invocation, Up: Macros + +4.3 Macro arguments +=================== + +When a name is seen, and it has a macro definition, it will be expanded +as a macro. + + If the name is followed by an opening parenthesis, the arguments +will be collected before the macro is called. If too few arguments are +supplied, the missing arguments are taken to be the empty string. +However, some builtins are documented to behave differently for a +missing optional argument than for an explicit empty string. If there +are too many arguments, the excess arguments are ignored. Unquoted +leading whitespace is stripped off all arguments, but whitespace +generated by a macro expansion or occuring after a macro that expanded +to an empty string remains intact. Whitespace includes space, tab, +newline, carriage return, vertical tab, and formfeed. + + define(`macro', `$1') + => + macro( unquoted leading space lost) + =>unquoted leading space lost + macro(` quoted leading space kept') + => quoted leading space kept + macro( + divert `unquoted space kept after expansion') + => unquoted space kept after expansion + macro(macro(` + ')`whitespace from expansion kept') + => + =>whitespace from expansion kept + macro(`unquoted trailing whitespace kept' + ) + =>unquoted trailing whitespace kept + => + + Normally `m4' will issue warnings if a builtin macro is called with +an inappropriate number of arguments, but it can be suppressed with the +`--quiet' command line option (or `--silent', or `-Q', *note Invoking +m4: Operation modes.). For user defined macros, there is no check of +the number of arguments given. + + Macros are expanded normally during argument collection, and whatever +commas, quotes and parentheses that might show up in the resulting +expanded text will serve to define the arguments as well. Thus, if FOO +expands to `, b, c', the macro call + + bar(a foo, d) + +is a macro call with four arguments, which are `a ', `b', `c' and `d'. +To understand why the first argument contains whitespace, remember that +unquoted leading whitespace is never part of an argument, but trailing +whitespace always is. + + It is possible for a macro's definition to change during argument +collection, in which case the expansion uses the definition that was in +effect at the time the opening `(' was seen. + + define(`f', `1') + => + f(define(`f', `2')) + =>1 + f + =>2 + + It is an error if the end of file occurs while collecting arguments. + + hello world + =>hello world + define( + ^D + error-->m4:stdin:2: ERROR: end of file in argument list + + +File: m4.info, Node: Quoting Arguments, Next: Macro expansion, Prev: Macro Arguments, Up: Macros + +4.4 On Quoting Arguments to macros +================================== + +Each argument has unquoted leading whitespace removed. Within each +argument, all unquoted parentheses must match. For example, if FOO is +a macro, + + foo(() (`(') `(') + +is a macro call, with one argument, whose value is `() (() ('. Commas +separate arguments, except when they occur inside quotes, comments, or +unquoted parentheses. *Note Pseudo Arguments::, for examples. + + It is common practice to quote all arguments to macros, unless you +are sure you want the arguments expanded. Thus, in the above example +with the parentheses, the `right' way to do it is like this: + + foo(`() (() (') + + It is, however, in certain cases necessary or convenient to leave out +quotes for some arguments, and there is nothing wrong in doing it. It +just makes life a bit harder, if you are not careful. For consistency, +this manual follows the rule of thumb that each layer of parentheses +introduces another layer of single quoting, except when showing the +consequences of quoting rules. This is done even when the quoted string +cannot be a macro, such as with integers when you have not changed the +syntax via `changeword' (*note Changeword::). + + +File: m4.info, Node: Macro expansion, Prev: Quoting Arguments, Up: Macros + +4.5 Macro expansion +=================== + +When the arguments, if any, to a macro call have been collected, the +macro is expanded, and the expansion text is pushed back onto the input +(unquoted), and reread. The expansion text from one macro call might +therefore result in more macros being called, if the calls are included, +completely or partially, in the first macro calls' expansion. + + Taking a very simple example, if FOO expands to `bar', and BAR +expands to `Hello world', the input + + foo + +will expand first to `bar', and when this is reread and expanded, into +`Hello world'. + + +File: m4.info, Node: Definitions, Next: Conditionals, Prev: Macros, Up: Top + +5 How to define new macros +************************** + +Macros can be defined, redefined and deleted in several different ways. +Also, it is possible to redefine a macro without losing a previous +value, and bring back the original value at a later time. + +* Menu: + +* Define:: Defining a new macro +* Arguments:: Arguments to macros +* Pseudo Arguments:: Special arguments to macros +* Undefine:: Deleting a macro +* Defn:: Renaming macros +* Pushdef:: Temporarily redefining macros + +* Indir:: Indirect call of macros +* Builtin:: Indirect call of builtins + + +File: m4.info, Node: Define, Next: Arguments, Up: Definitions + +5.1 Defining a macro +==================== + +The normal way to define or redefine macros is to use the builtin +`define': + + -- Builtin: define (NAME, [EXPANSION]) + Defines NAME to expand to EXPANSION. If EXPANSION is not given, + it is taken to be empty. + + The expansion of `define' is void. The macro `define' is + recognized only with parameters. + + The following example defines the macro FOO to expand to the text +`Hello World.'. + + define(`foo', `Hello world.') + => + foo + =>Hello world. + + The empty line in the output is there because the newline is not a +part of the macro definition, and it is consequently copied to the +output. This can be avoided by use of the macro `dnl'. *Note Dnl::, +for details. + + The first argument to `define' should be quoted; otherwise, if the +macro is already defined, you will be defining a different macro. This +example shows the problems with underquoting, since we did not want to +redefine `one': + + define(foo, one) + => + define(foo, two) + => + one + =>two + + GNU `m4' normally replaces only the _topmost_ definition of a macro +if it has several definitions from `pushdef' (*note Pushdef::). Some +other implementations of `m4' replace all definitions of a macro with +`define'. *Note Incompatibilities::, for more details. + + As a GNU extension, the first argument to `define' does not have to +be a simple word. It can be any text string, even the empty string. A +macro with a non-standard name cannot be invoked in the normal way, as +the name is not recognized. It can only be referenced by the builtins +`Indir' (*note Indir::) and `Defn' (*note Defn::). + + Arrays and associative arrays can be simulated by using this trick. + + define(`array', `defn(format(``array[%d]'', `$1'))') + => + define(`array_set', `define(format(``array[%d]'', `$1'), `$2')') + => + array_set(`4', `array element no. 4') + => + array_set(`17', `array element no. 17') + => + array(`4') + =>array element no. 4 + array(eval(`10 + 7')) + =>array element no. 17 + + Change the `%d' to `%s' and it is an associative array. + + +File: m4.info, Node: Arguments, Next: Pseudo Arguments, Prev: Define, Up: Definitions + +5.2 Arguments to macros +======================= + +Macros can have arguments. The Nth argument is denoted by `$n' in the +expansion text, and is replaced by the Nth actual argument, when the +macro is expanded. Replacement of arguments happens before rescanning, +regardless of how many nesting levels of quoting appear in the +expansion. Here is an example of a macro with two arguments. It +simply exchanges the order of the two arguments. + + define(`exch', `$2, $1') + => + exch(`arg1', `arg2') + =>arg2, arg1 + + This can be used, for example, if you like the arguments to `define' +to be reversed. + + define(`exch', `$2, $1') + => + define(exch(``expansion text'', ``macro'')) + => + macro + =>expansion text + + *Note Quoting Arguments::, for an explanation of the double quotes. +(You should try and improve this example so that clients of `exch' do +not have to double quote; or *note Answers: Improved exch.). + + GNU `m4' allows the number following the `$' to consist of one or +more digits, allowing macros to have any number of arguments. This is +not so in UNIX implementations of `m4', which only recognize one digit. + + As a special case, the zeroth argument, `$0', is always the name of +the macro being expanded. + + define(`test', ``Macro name: $0'') + => + test + =>Macro name: test + + If you want quoted text to appear as part of the expansion text, +remember that quotes can be nested in quoted strings. Thus, in + + define(`foo', `This is macro `foo'.') + => + foo + =>This is macro foo. + +The `foo' in the expansion text is _not_ expanded, since it is a quoted +string, and not a name. + + +File: m4.info, Node: Pseudo Arguments, Next: Undefine, Prev: Arguments, Up: Definitions + +5.3 Special arguments to macros +=============================== + +There is a special notation for the number of actual arguments supplied, +and for all the actual arguments. + + The number of actual arguments in a macro call is denoted by `$#' in +the expansion text. Thus, a macro to display the number of arguments +given can be + + define(`nargs', `$#') + => + nargs + =>0 + nargs() + =>1 + nargs(`arg1', `arg2', `arg3') + =>3 + nargs(`commas can be quoted, like this') + =>1 + nargs(arg1#inside comments, commas do not separate arguments + still arg1) + =>1 + nargs((unquoted parentheses, like this, group arguments)) + =>1 + + The notation `$*' can be used in the expansion text to denote all +the actual arguments, unquoted, with commas in between. For example + + define(`echo', `$*') + => + echo(arg1, arg2, arg3 , arg4) + =>arg1,arg2,arg3 ,arg4 + + Often each argument should be quoted, and the notation `$@' handles +that. It is just like `$*', except that it quotes each argument. A +simple example of that is: + + define(`echo', `$@') + => + echo(arg1, arg2, arg3 , arg4) + =>arg1,arg2,arg3 ,arg4 + + Where did the quotes go? Of course, they were eaten, when the +expanded text were reread by `m4'. To show the difference, try + + define(`echo1', `$*') + => + define(`echo2', `$@') + => + define(`foo', `This is macro `foo'.') + => + echo1(foo) + =>This is macro This is macro foo.. + echo1(`foo') + =>This is macro foo. + echo2(foo) + =>This is macro foo. + echo2(`foo') + =>foo + +*Note Trace::, if you do not understand this. As another example of the +difference, remember that comments encountered in arguments are passed +untouched to the macro, and that quoting disables comments. + + define(`echo1', `$*') + => + define(`echo2', `$@') + => + define(`foo', `bar') + => + echo1(#foo'foo + foo) + =>#foo'foo + =>bar + echo2(#foo'foo + foo) + =>#foobar + =>bar' + + A `$' sign in the expansion text, that is not followed by anything +`m4' understands, is simply copied to the macro expansion, as any other +text is. + + define(`foo', `$$$ hello $$$') + => + foo + =>$$$ hello $$$ + + If you want a macro to expand to something like `$12', the judicious +use of nested quoting can put a safe character between the `$' and the +next character, relying on the rescanning to remove the nested quote. +This will prevent `m4' from interpreting the `$' sign as a reference to +an argument. + + define(`foo', `no nested quote: $1') + => + foo(`arg') + =>no nested quote: arg + define(`foo', `nested quote around $: `$'1') + => + foo(`arg') + =>nested quote around $: $1 + define(`foo', `nested empty quote after $: $`'1') + => + foo(`arg') + =>nested empty quote after $: $1 + define(`foo', `nested quote around next character: $`1'') + => + foo(`arg') + =>nested quote around next character: $1 + define(`foo', `nested quote around both: `$1'') + => + foo(`arg') + =>nested quote around both: arg + + +File: m4.info, Node: Undefine, Next: Defn, Prev: Pseudo Arguments, Up: Definitions + +5.4 Deleting a macro +==================== + +A macro definition can be removed with `undefine': + + -- Builtin: undefine (NAME...) + For each argument, remove the macro NAME. The macro names must + necessarily be quoted, since they will be expanded otherwise. + + The expansion of `undefine' is void. The macro `undefine' is + recognized only with parameters. + + foo bar blah + =>foo bar blah + define(`foo', `some')define(`bar', `other')define(`blah', `text') + => + foo bar blah + =>some other text + undefine(`foo') + => + foo bar blah + =>foo other text + undefine(`bar', `blah') + => + foo bar blah + =>foo bar blah + + Undefining a macro inside that macro's expansion is safe; the macro +still expands to the definition that was in effect at the `('. + + define(`f', ``$0':$1') + => + f(f(f(undefine(`f')`hello world'))) + =>f:f:f:hello world + f(`bye') + =>f(bye) + + It is not an error for NAME to have no macro definition. In that +case, `undefine' does nothing. + + +File: m4.info, Node: Defn, Next: Pushdef, Prev: Undefine, Up: Definitions + +5.5 Renaming macros +=================== + +It is possible to rename an already defined macro. To do this, you need +the builtin `defn': + + -- Builtin: defn (NAME) + Expands to the _quoted definition_ of NAME. If the argument is + not a defined macro, the expansion is void. + + If NAME is a user-defined macro, the quoted definition is simply + the quoted expansion text. If, instead, NAME is a builtin, the + expansion is a special token, which points to the builtin's + internal definition. This token is only meaningful as the second + argument to `define' (and `pushdef'), and is silently converted to + an empty string in most other contexts. + + The macro `defn' is recognized only with parameters. + + Its normal use is best understood through an example, which shows +how to rename `undefine' to `zap': + + define(`zap', defn(`undefine')) + => + zap(`undefine') + => + undefine(`zap') + =>undefine(zap) + + In this way, `defn' can be used to copy macro definitions, and also +definitions of builtin macros. Even if the original macro is removed, +the other name can still be used to access the definition. + + The fact that macro definitions can be transferred also explains why +you should use `$0', rather than retyping a macro's name in its +definition: + + define(`foo', `This is `$0'') + => + define(`bar', defn(`foo')) + => + bar + =>This is bar + + Macros used as string variables should be referred through `defn', +to avoid unwanted expansion of the text: + + define(`string', `The macro dnl is very useful + ') + => + string + =>The macro + defn(`string') + =>The macro dnl is very useful + => + + However, it is important to remember that `m4' rescanning is purely +textual. If an unbalanced end-quote string occurs in a macro +definition, the rescan will see that embedded quote as the termination +of the quoted string, and the remainder of the macro's definition will +be rescanned unquoted. Thus it is a good idea to avoid unbalanced +end-quotes in macro definitions or arguments to macros. + + define(`foo', a'a) + => + define(`a', `A') + => + define(`echo', `$@') + => + foo + =>A'A + defn(`foo') + =>aA' + echo(foo) + =>AA' + + Using `defn' to generate special tokens for builtin macros outside +of expected contexts can sometimes trigger warnings. But most of the +time, such tokens are silently converted to the empty string. + + defn(`defn') + => + define(defn(`divnum'), `cannot redefine a builtin token') + error-->m4:stdin:2: Warning: define: invalid macro name ignored + => + divnum + =>0 + + +File: m4.info, Node: Pushdef, Next: Indir, Prev: Defn, Up: Definitions + +5.6 Temporarily redefining macros +================================= + +It is possible to redefine a macro temporarily, reverting to the +previous definition at a later time. This is done with the builtins +`pushdef' and `popdef': + + -- Builtin: pushdef (NAME, [EXPANSION]) + -- Builtin: popdef (NAME...) + Analogous to `define' and `undefine'. + + These macros work in a stack-like fashion. A macro is temporarily + redefined with `pushdef', which replaces an existing definition of + NAME, while saving the previous definition, before the new one is + installed. If there is no previous definition, `pushdef' behaves + exactly like `define'. + + If a macro has several definitions (of which only one is + accessible), the topmost definition can be removed with `popdef'. + If there is no previous definition, `popdef' behaves like + `undefine'. + + The expansion of both `pushdef' and `popdef' is void. The macros + `pushdef' and `popdef' are recognized only with parameters. + + define(`foo', `Expansion one.') + => + foo + =>Expansion one. + pushdef(`foo', `Expansion two.') + => + foo + =>Expansion two. + pushdef(`foo', `Expansion three.') + => + pushdef(`foo', `Expansion four.') + => + popdef(`foo') + => + foo + =>Expansion three. + popdef(`foo', `foo') + => + foo + =>Expansion one. + popdef(`foo') + => + foo + =>foo + + If a macro with several definitions is redefined with `define', the +topmost definition is _replaced_ with the new definition. If it is +removed with `undefine', _all_ the definitions are removed, and not +only the topmost one. + + define(`foo', `Expansion one.') + => + foo + =>Expansion one. + pushdef(`foo', `Expansion two.') + => + foo + =>Expansion two. + define(`foo', `Second expansion two.') + => + foo + =>Second expansion two. + undefine(`foo') + => + foo + =>foo + + Local variables within macros are made with `pushdef' and `popdef'. +At the start of the macro a new definition is pushed, within the macro +it is manipulated and at the end it is popped, revealing the former +definition. + + It is possible to temporarily redefine a builtin with `pushdef' and +`defn'. + + +File: m4.info, Node: Indir, Next: Builtin, Prev: Pushdef, Up: Definitions + +5.7 Indirect call of macros +=========================== + +Any macro can be called indirectly with `indir': + + -- Builtin: indir (NAME, [ARGS...]) + Results in a call to the macro NAME, which is passed the rest of + the arguments ARGS. If NAME is not defined, an error message is + printed, and the expansion is void. + + The macro `indir' is recognized only with parameters. + + This can be used to call macros with computed or "invalid" names +(`define' allows such names to be defined): + + define(`$$internal$macro', `Internal macro (name `$0')') + => + $$internal$macro + =>$$internal$macro + indir(`$$internal$macro') + =>Internal macro (name $$internal$macro) + + The point is, here, that larger macro packages can have private +macros defined, that will not be called by accident. They can _only_ be +called through the builtin `indir'. + + One other point to observe is that argument collection occurs before +`indir' invokes NAME, so if argument collection changes the value of +NAME, that will be reflected in the final expansion. This is different +than the behavior when invoking macros directly, where the definition +that was in effect before argument collection is used. + + define(`f', `1') + => + f(define(`f', `2')) + =>1 + indir(`f', define(`f', `3')) + =>3 + indir(`f', undefine(`f')) + error-->m4:stdin:4: undefined macro `f' + => + + When handed the result of `defn' (*note Defn::) as one of its +arguments, `indir' defers to the invoked NAME for whether a token +representing a builtin is recognized or flattened to the empty string. + + indir(defn(`defn'), `divnum') + error-->m4:stdin:1: Warning: indir: invalid macro name ignored + => + indir(`define', defn(`defn'), `divnum') + error-->m4:stdin:2: Warning: define: invalid macro name ignored + => + indir(`define', `foo', defn(`divnum')) + => + foo + =>0 + indir(`divert', defn(`foo')) + error-->m4:stdin:5: empty string treated as 0 in builtin `divert' + => + + +File: m4.info, Node: Builtin, Prev: Indir, Up: Definitions + +5.8 Indirect call of builtins +============================= + +Builtin macros can be called indirectly with `builtin': + + -- Builtin: builtin (NAME, [ARGS...]) + Results in a call to the builtin NAME, which is passed the rest of + the arguments ARGS. If NAME does not name a builtin, an error + message is printed, and the expansion is void. + + The macro `builtin' is recognized only with parameters. + + This can be used even if NAME has been given another definition that +has covered the original, or been undefined so that no macro maps to +the builtin. + + pushdef(`define', `hidden') + => + undefine(`undefine') + => + define(`foo', `bar') + =>hidden + foo + =>foo + builtin(`define', `foo', defn(`divnum')) + => + foo + =>0 + builtin(`define', `foo', `BAR') + => + foo + =>BAR + undefine(`foo') + =>undefine(foo) + foo + =>BAR + builtin(`undefine', `foo') + => + foo + =>foo + + The NAME argument only matches the original name of the builtin, +even when the `--prefix-builtins' option (or `-P', *note Invoking m4: +Operation modes.) is in effect. This is different from `indir', which +only tracks current macro names. + + Note that `indir' and `builtin' can be used to invoke builtins +without arguments, even when they normally require parameters to be +recognized; but it will provoke a warning, and result in a void +expansion. + + builtin + =>builtin + builtin() + error-->m4:stdin:2: undefined builtin `' + => + builtin(`builtin') + error-->m4:stdin:3: Warning: too few arguments to builtin `builtin' + => + builtin(`builtin',) + error-->m4:stdin:4: undefined builtin `' + => + + +File: m4.info, Node: Conditionals, Next: Debugging, Prev: Definitions, Up: Top + +6 Conditionals, loops, and recursion +************************************ + +Macros, expanding to plain text, perhaps with arguments, are not quite +enough. We would like to have macros expand to different things, based +on decisions taken at run-time. For that, we need some kind of +conditionals. Also, we would like to have some kind of loop construct, +so we could do something a number of times, or while some condition is +true. + +* Menu: + +* Ifdef:: Testing if a macro is defined +* Ifelse:: If-else construct, or multibranch +* Shift:: Recursion in `m4' +* Forloop:: Iteration by counting +* Foreach:: Iteration by list contents + + +File: m4.info, Node: Ifdef, Next: Ifelse, Up: Conditionals + +6.1 Testing if a macro is defined +================================= + +There are two different builtin conditionals in `m4'. The first is +`ifdef': + + -- Builtin: ifdef (NAME, STRING-1, [STRING-2]) + If NAME is defined as a macro, `ifdef' expands to STRING-1, + otherwise to STRING-2. If STRING-2 is omitted, it is taken to be + the empty string (according to the normal rules). + + The macro `ifdef' is recognized only with parameters. + + ifdef(`foo', ``foo' is defined', ``foo' is not defined') + =>foo is not defined + define(`foo', `') + => + ifdef(`foo', ``foo' is defined', ``foo' is not defined') + =>foo is defined + ifdef(`no_such_macro', `yes', `no', `extra argument') + error-->m4:stdin:4: Warning: excess arguments to builtin `ifdef' ignored + =>no + + +File: m4.info, Node: Ifelse, Next: Shift, Prev: Ifdef, Up: Conditionals + +6.2 If-else construct, or multibranch +===================================== + +The other conditional, `ifelse', is much more powerful. It can be used +as a way to introduce a long comment, as an if-else construct, or as a +multibranch, depending on the number of arguments supplied: + + -- Builtin: ifelse (COMMENT) + -- Builtin: ifelse (STRING-1, STRING-2, EQUAL, [NOT-EQUAL]) + -- Builtin: ifelse (STRING-1, STRING-2, EQUAL-1, STRING-3, STRING-4, + EQUAL-2, ...) + Used with only one argument, the `ifelse' simply discards it and + produces no output. + + If called with three or four arguments, `ifelse' expands into + EQUAL, if STRING-1 and STRING-2 are equal (character for + character), otherwise it expands to NOT-EQUAL. A final fifth + argument is ignored, after triggering a warning. + + If called with six or more arguments, and STRING-1 and STRING-2 + are equal, `ifelse' expands into EQUAL-1, otherwise the first + three arguments are discarded and the processing starts again. + + The macro `ifelse' is recognized only with parameters. + + Using only one argument is a common `m4' idiom for introducing a +block comment, as an alternative to repeatedly using `dnl'. This +special usage is recognized by GNU `m4', so that in this case, the +warning about missing arguments is never triggered. + + ifelse(`some comments') + => + ifelse(`foo', `bar') + error-->m4:stdin:2: Warning: too few arguments to builtin `ifelse' + => + + Using three or four arguments provides decision points. + + ifelse(`foo', `bar', `true') + => + ifelse(`foo', `foo', `true') + =>true + define(`foo', `bar') + => + ifelse(foo, `bar', `true', `false') + =>true + ifelse(foo, `foo', `true', `false') + =>false + + Notice how the first argument was used unquoted; it is common to +compare the expansion of a macro with a string. With this macro, you +can now reproduce the behavior of many of the builtins, where the macro +is recognized only with arguments. + + define(`foo', `ifelse(`$#', `0', ``$0'', `arguments:$#')') + => + foo + =>foo + foo() + =>arguments:1 + foo(`a', `b', `c') + =>arguments:3 + + However, `ifelse' can take more than four arguments. If given more +than four arguments, `ifelse' works like a `case' or `switch' statement +in traditional programming languages. If STRING-1 and STRING-2 are +equal, `ifelse' expands into EQUAL-1, otherwise the procedure is +repeated with the first three arguments discarded. This calls for an +example: + + ifelse(`foo', `bar', `third', `gnu', `gnats') + error-->m4:stdin:1: Warning: excess arguments to builtin `ifelse' ignored + =>gnu + ifelse(`foo', `bar', `third', `gnu', `gnats', `sixth') + => + ifelse(`foo', `bar', `third', `gnu', `gnats', `sixth', `seventh') + =>seventh + ifelse(`foo', `bar', `3', `gnu', `gnats', `6', `7', `8') + error-->m4:stdin:4: Warning: excess arguments to builtin `ifelse' ignored + =>7 + + Naturally, the normal case will be slightly more advanced than these +examples. A common use of `ifelse' is in macros implementing loops of +various kinds. + + +File: m4.info, Node: Shift, Next: Forloop, Prev: Ifelse, Up: Conditionals + +6.3 Recursion in `m4' +===================== + +There is no direct support for loops in `m4', but macros can be +recursive. There is no limit on the number of recursion levels, other +than those enforced by your hardware and operating system. + + Loops can be programmed using recursion and the conditionals +described previously. + + There is a builtin macro, `shift', which can, among other things, be +used for iterating through the actual arguments to a macro: + + -- Builtin: shift (ARG1, ...) + Takes any number of arguments, and expands to all its arguments + except ARG1, separated by commas, with each argument quoted. + + The macro `shift' is recognized only with parameters. + + shift + =>shift + shift(`bar') + => + shift(`foo', `bar', `baz') + =>bar,baz + + An example of the use of `shift' is this macro: + + -- Composite: reverse (...) + Takes any number of arguments, and reverses their order. + + It is implemented as: + + define(`reverse', `ifelse(`$#', `0', , `$#', `1', ``$1'', + `reverse(shift($@)), `$1'')') + => + reverse + => + reverse(`foo') + =>foo + reverse(`foo', `bar', `gnats', `and gnus') + =>and gnus, gnats, bar, foo + + While not a very interesting macro, it does show how simple loops +can be made with `shift', `ifelse' and recursion. It also shows that +`shift' is usually used with `$@'. Sometimes, a recursive algorithm +requires adding quotes to each element: + + -- Composite: quote (...) + -- Composite: dquote (...) + -- Composite: dquote_elt (...) + Takes any number of arguments, and adds quoting. With `quote', + only one level of quoting is added, effectively removing whitespace + after commas and turning multiple arguments into a single string. + With `dquote', two levels of quoting are added, one around each + element, and one around the list. And with `dquote_elt', two + levels of quoting are added around each element. + + An actual implementation of these three macros is distributed as +`m4-1.4.8/examples/quote.m4' in this package. First, let's examine +their usage: + + include(`quote.m4') + => + -quote-dquote-dquote_elt- + =>---- + -quote()-dquote()-dquote_elt()- + =>--`'-`'- + -quote(`1')-dquote(`1')-dquote_elt(`1')- + =>-1-`1'-`1'- + -quote(`1', `2')-dquote(`1', `2')-dquote_elt(`1', `2')- + =>-1,2-`1',`2'-`1',`2'- + define(`n', `$#')dnl + -n(quote(`1', `2'))-n(dquote(`1', `2'))-n(dquote_elt(`1', `2'))- + =>-1-1-2- + dquote(dquote_elt(`1', `2')) + =>``1'',``2'' + dquote_elt(dquote(`1', `2')) + =>``1',`2'' + + The last two lines show that when given two arguments, `dquote' +results in one string, while `dquote_elt' results in two. Now, examine +the implementation. Note that `quote' and `dquote_elt' make decisions +based on their number of arguments, so that when called without +arguments, they result in nothing instead of a quoted empty string; +this is so that it is possible to distinquish between no arguments and +an empty first argument. `dquote', on the other hand, results in a +string no matter what, since it is still possible to tell whether it +was invoked without arguments based on the resulting string. + + undivert(`quote.m4')dnl + =>divert(`-1') + =># quote(args) - convert args to single-quoted string + =>define(`quote', `ifelse(`$#', `0', `', ``$*'')') + =># dquote(args) - convert args to quoted list of quoted strings + =>define(`dquote', ``$@'') + =># dquote_elt(args) - convert args to list of double-quoted strings + =>define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''', + => ```$1'',$0(shift($@))')') + =>divert`'dnl + + +File: m4.info, Node: Forloop, Next: Foreach, Prev: Shift, Up: Conditionals + +6.4 Iteration by counting +========================= + +Here is an example of a loop macro that implements a simple for loop. + + -- Composite: forloop (ITERATOR, START, END, TEXT) + Takes the name in ITERATOR, which must be a valid macro name, and + successively assign it each integer value from START to END, + inclusive. For each assignment to ITERATOR, append TEXT to the + expansion of the `forloop'. TEXT may refer to ITERATOR. Any + definition of ITERATOR prior to this invocation is restored. + + It can, for example, be used for simple counting: + + include(`forloop.m4') + => + forloop(`i', `1', `8', `i ') + =>1 2 3 4 5 6 7 8 + + For-loops can be nested, like: + + include(`forloop.m4') + => + forloop(`i', `1', `4', `forloop(`j', `1', `8', ` (i, j)') + ') + => (1, 1) (1, 2) (1, 3) (1, 4) (1, 5) (1, 6) (1, 7) (1, 8) + => (2, 1) (2, 2) (2, 3) (2, 4) (2, 5) (2, 6) (2, 7) (2, 8) + => (3, 1) (3, 2) (3, 3) (3, 4) (3, 5) (3, 6) (3, 7) (3, 8) + => (4, 1) (4, 2) (4, 3) (4, 4) (4, 5) (4, 6) (4, 7) (4, 8) + => + + The implementation of the `forloop' macro is fairly straightforward. +The `forloop' macro itself is simply a wrapper, which saves the +previous definition of the first argument, calls the internal macro +`_forloop', and re-establishes the saved definition of the first +argument. + + The macro `_forloop' expands the fourth argument once, and tests to +see if the iterator has reached the final value. If it has not +finished, it increments the iterator (using the predefined macro +`incr', *note Incr::), and recurses. + + Here is an actual implementation of `forloop', distributed as +`m4-1.4.8/examples/forloop.m4' in this package: + + undivert(`forloop.m4')dnl + =>divert(`-1') + =># forloop(var, from, to, stmt) - simple version + =>define(`forloop', `pushdef(`$1', `$2')_forloop($@)popdef(`$1')') + =>define(`_forloop', + => `$4`'ifelse($1, `$3', `', `define(`$1', incr($1))$0($@)')') + =>divert`'dnl + + Notice the careful use of quotes. Certain macro arguments are left +unquoted, each for its own reason. Try to find out _why_ these +arguments are left unquoted, and see what happens if they are quoted. +(As presented, these two macros are useful but not very robust for +general use. They lack even basic error handling for cases like START +less than END, END not numeric, or ITERATOR not being a macro name. +See if you can improve these macros; or *note Answers: Improved +forloop.). + + +File: m4.info, Node: Foreach, Prev: Forloop, Up: Conditionals + +6.5 Iteration by list contents +============================== + +Here is an example of a loop macro that implements list iteration. + + -- Composite: foreach (ITERATOR, PAREN-LIST, TEXT) + -- Composite: foreachq (ITERATOR, QUOTE-LIST, TEXT) + Takes the name in ITERATOR, which must be a valid macro name, and + successively assign it each value from PAREN-LIST or QUOTE-LIST. + In `foreach', PAREN-LIST is a comma-separated list of elements + contained in parentheses. In `foreachq', QUOTE-LIST is a + comma-separated list of elements contained in a quoted string. + For each assignment to ITERATOR, append TEXT to the overall + expansion. TEXT may refer to ITERATOR. Any definition of + ITERATOR prior to this invocation is restored. + + As an example, this displays each word in a list inside of a +sentence, using an implementation of `foreach' distributed as +`m4-1.4.8/examples/foreach.m4', and `foreachq' in +`m4-1.4.8/examples/foreachq.m4'. + + include(`foreach.m4') + => + foreach(`x', (foo, bar, foobar), `Word was: x + ')dnl + =>Word was: foo + =>Word was: bar + =>Word was: foobar + include(`foreachq.m4') + => + foreachq(`x', `foo, bar, foobar', `Word was: x + ')dnl + =>Word was: foo + =>Word was: bar + =>Word was: foobar + + It is possible to be more complex; each element of the PAREN-LIST or +QUOTE-LIST can itself be a list, to pass as further arguments to a +helper macro. This example generates a shell case statement: + + include(`foreach.m4') + => + define(`_case', ` $1) + $2=" $1";; + ')dnl + define(`_cat', `$1$2')dnl + case $`'1 in + =>case $1 in + foreach(`x', `(`(`a', `vara')', `(`b', `varb')', `(`c', `varc')')', + `_cat(`_case', x)')dnl + => a) + => vara=" a";; + => b) + => varb=" b";; + => c) + => varc=" c";; + esac + =>esac + + The implementation of the `foreach' macro is a bit more involved; it +is a wrapper around two helper macros. First, `_arg1' is needed to +grab the first element of a list. Second, `_foreach' implements the +recursion, successively walking through the original list. Here is a +simple implementation of `foreach': + + undivert(`foreach.m4')dnl + =>divert(`-1') + =># foreach(x, (item_1, item_2, ..., item_n), stmt) + =># parenthesized list, simple version + =>define(`foreach', `pushdef(`$1')_foreach($@)popdef(`$1')') + =>define(`_arg1', `$1') + =>define(`_foreach', `ifelse(`$2', `()', `', + => `define(`$1', _arg1$2)$3`'$0(`$1', (shift$2), `$3')')') + =>divert`'dnl + + Unfortunately, that implementation is not robust to macro names as +list elements. Each iteration of `_foreach' is stripping another layer +of quotes, leading to erratic results if list elements are not already +fully expanded. The first cut at implementing `foreachq' takes this +into account. Also, when using quoted elements in a PAREN-LIST, the +overall list must be quoted. A QUOTE-LIST has the nice property of +requiring fewer characters to create a list containing the same quoted +elements. To see the difference between the two macros, we attempt to +pass double-quoted macro names in a list, expecting the macro name on +output after one layer of quotes is removed during list iteration and +the final layer removed during the final rescan: + + define(`a', `1')define(`b', `2')define(`c', `3') + => + include(`foreach.m4') + => + include(`foreachq.m4') + => + foreach(`x', `(``a'', ``(b'', ``c)'')', `x + ') + =>1 + =>(2)1 + => + =>, x + =>) + foreachq(`x', ```a'', ``(b'', ``c)''', `x + ')dnl + =>a + =>(b + =>c) + + Obviously, `foreachq' did a better job; here is its implementation: + + undivert(`foreachq.m4')dnl + =>include(`quote.m4')dnl + =>divert(`-1') + =># foreachq(x, `item_1, item_2, ..., item_n', stmt) + =># quoted list, simple version + =>define(`foreachq', `pushdef(`$1')_foreachq($@)popdef(`$1')') + =>define(`_arg1', `$1') + =>define(`_foreachq', `ifelse(quote($2), `', `', + => `define(`$1', `_arg1($2)')$3`'$0(`$1', `shift($2)', `$3')')') + =>divert`'dnl + + Notice that `_foreachq' had to use the helper macro `quote' defined +earlier (*note Shift::), to ensure that the embedded `ifelse' call does +not go haywire if a list element contains a comma. Unfortunately, this +implementation of `foreachq' has its own severe flaw. Whereas the +`foreach' implementation was linear, this macro is quadratic in the +number of list elements, and is much more likely to trip up the limit +set by the command line option `--nesting-limit' (or `-L', *note +Invoking m4: Limits control.). (It is possible to have robust +iteration with linear behavior for either list style. See if you can +learn from the best elements of both of these implementations to create +robust macros; or *note Answers: Improved foreach.). + + +File: m4.info, Node: Debugging, Next: Input Control, Prev: Conditionals, Up: Top + +7 How to debug macros and input +******************************* + +When writing macros for `m4', they often do not work as intended on the +first try (as is the case with most programming languages). +Fortunately, there is support for macro debugging in `m4'. + +* Menu: + +* Dumpdef:: Displaying macro definitions +* Trace:: Tracing macro calls +* Debug Levels:: Controlling debugging output +* Debug Output:: Saving debugging output + + +File: m4.info, Node: Dumpdef, Next: Trace, Up: Debugging + +7.1 Displaying macro definitions +================================ + +If you want to see what a name expands into, you can use the builtin +`dumpdef': + + -- Builtin: dumpdef ([NAMES...]) + Accepts any number of arguments. If called without any arguments, + it displays the definitions of all known names, otherwise it + displays the definitions of the NAMES given. The output is + printed to the current debug file (usually standard error), and is + sorted by name. If an unknown name is encountered, a warning is + printed. + + The expansion of `dumpdef' is void. + + define(`foo', `Hello world.') + => + dumpdef(`foo') + error-->foo: `Hello world.' + => + dumpdef(`define') + error-->define: + => + + The last example shows how builtin macros definitions are displayed. +The definition that is dumped corresponds to what would occur if the +macro were to be called at that point, even if other definitions are +still live due to redefining a macro during argument collection. + + pushdef(`f', ``$0'1')pushdef(`f', ``$0'2') + => + f(popdef(`f')dumpdef(`f')) + error-->f: ``$0'1' + =>f2 + f(popdef(`f')dumpdef(`f')) + error-->m4:stdin:3: undefined macro `f' + =>f1 + + *Note Debug Levels::, for information on controlling the details of +the display. + + +File: m4.info, Node: Trace, Next: Debug Levels, Prev: Dumpdef, Up: Debugging + +7.2 Tracing macro calls +======================= + +It is possible to trace macro calls and expansions through the builtins +`traceon' and `traceoff': + + -- Builtin: traceon ([NAMES...]) + -- Builtin: traceoff ([NAMES...]) + When called without any arguments, `traceon' and `traceoff' will + turn tracing on and off, respectively, for all defined macros. + + When called with arguments, only the macros listed in NAMES are + affected, whether or not they are currently defined. + + The expansion of `traceon' and `traceoff' is void. + + Whenever a traced macro is called and the arguments have been +collected, the call is displayed. If the expansion of the macro call +is not void, the expansion can be displayed after the call. The output +is printed to the current debug file (defaulting to standard error, +*note Debug Output::). + + define(`foo', `Hello World.') + => + define(`echo', `$@') + => + traceon(`foo', `echo') + => + foo + error-->m4trace: -1- foo -> `Hello World.' + =>Hello World. + echo(`gnus', `and gnats') + error-->m4trace: -1- echo(`gnus', `and gnats') -> ``gnus',`and gnats'' + =>gnus,and gnats + + The number between dashes is the depth of the expansion. It is one +most of the time, signifying an expansion at the outermost level, but it +increases when macro arguments contain unquoted macro calls. The +maximum number that will appear between dashes is controlled by the +option `--nesting-limit' (*note Invoking m4: Limits control.). + + Tracing by name is an attribute that is preserved whether the macro +is defined or not. This allows the `-t' option to select macros to +trace before those macros are defined. + + traceoff(`foo') + => + traceon(`foo') + => + foo + =>foo + define(`foo', `bar') + => + foo + error-->m4trace: -1- foo -> `bar' + =>bar + undefine(`foo') + => + ifdef(`foo', `yes', `no') + =>no + indir(`foo') + error-->m4:stdin:8: undefined macro `foo' + => + define(`foo', `blah') + => + foo + error-->m4trace: -1- foo -> `blah' + =>blah + traceoff + => + foo + =>blah + + Tracing even works on builtins. However, `defn' (*note Defn::) does +not transfer tracing status. + + traceon(`eval', `m4_divnum') + => + define(`m4_eval', defn(`eval')) + => + define(`m4_divnum', defn(`divnum')) + => + eval(divnum) + error-->m4trace: -1- eval(`0') -> `0' + =>0 + m4_eval(m4_divnum) + error-->m4trace: -2- m4_divnum -> `0' + =>0 + + *Note Debug Levels::, for information on controlling the details of +the display. + + +File: m4.info, Node: Debug Levels, Next: Debug Output, Prev: Trace, Up: Debugging + +7.3 Controlling debugging output +================================ + +The `-d' option to `m4' (*note Invoking m4: Debugging options.) +controls the amount of details presented, when using the macros +described in the preceding sections. + + The FLAGS following the option can be one or more of the following: + +`a' + Show the actual arguments in each macro call. This applies to all + macro calls if the `t' flag is used, otherwise only the macros + covered by calls of `traceon'. + +`c' + Show several trace lines for each macro call. A line is shown + when the macro is seen, but before the arguments are collected; a + second line when the arguments have been collected and a third + line after the call has completed. + +`e' + Show the expansion of each macro call, if it is not void. This + applies to all macro calls if the `t' flag is used, otherwise only + the macros covered by calls of `traceon'. + +`f' + Show the name of the current input file in each trace output line. + +`i' + Print a message each time the current input file is changed, + giving file name and input line number. + +`l' + Show the current input line number in each trace output line. + +`p' + Print a message when a named file is found through the path search + mechanism (*note Search Path::), giving the actual file name used. + +`q' + Quote actual arguments and macro expansions in the display with the + current quotes. + +`t' + Trace all macro calls made in this invocation of `m4'. + +`x' + Add a unique `macro call id' to each line of the trace output. + This is useful in connection with the `c' flag above. + +`V' + A shorthand for all of the above flags. + + If no flags are specified with the `-d' option, the default is +`aeq'. The examples throughout this manual assume the default flags. + + There is a builtin macro `debugmode', which allows on-the-fly +control of the debugging output format: + + -- Builtin: debugmode ([FLAGS]) + The argument FLAGS should be a subset of the letters listed above. + As special cases, if the argument starts with a `+', the flags are + added to the current debug flags, and if it starts with a `-', they + are removed. If no argument is present, all debugging flags are + cleared (as if no `-d' was given), and with an empty argument the + flags are reset to the default of `aeq'. + + The expansion of `debugmode' is void. + + define(`foo', `FOO') + => + traceon(`foo') + => + debugmode() + => + foo + error-->m4trace: -1- foo -> `FOO' + =>FOO + debugmode + => + foo + error-->m4trace: -1- foo + =>FOO + debugmode(`+l') + => + foo + error-->m4trace:8: -1- foo + =>FOO + + +File: m4.info, Node: Debug Output, Prev: Debug Levels, Up: Debugging + +7.4 Saving debugging output +=========================== + +Debug and tracing output can be redirected to files using either the +`--debugfile' option to `m4' (*note Invoking m4: Debugging options.), +or with the builtin macro `debugfile': + + -- Builtin: debugfile ([FILE]) + Sends all further debug and trace output to FILE, opened in append + mode. If FILE is the empty string, debug and trace output are + discarded. If `debugfile' is called without any arguments, debug + and trace output are sent to standard error. This does not affect + warnings, error messages, or `errprint' output, which are always + sent to standard error. If FILE cannot be opened, the current + debug file is unchanged, and an error is issued. + + The expansion of `debugfile' is void. + + traceon(`divnum') + => + divnum(`extra') + error-->m4:stdin:2: Warning: excess arguments to builtin `divnum' ignored + error-->m4trace: -1- divnum(`extra') -> `0' + =>0 + debugfile() + => + divnum(`extra') + error-->m4:stdin:4: Warning: excess arguments to builtin `divnum' ignored + =>0 + debugfile + => + divnum + error-->m4trace: -1- divnum -> `0' + =>0 + + +File: m4.info, Node: Input Control, Next: File Inclusion, Prev: Debugging, Up: Top + +8 Input control +*************** + +This chapter describes various builtin macros for controlling the input +to `m4'. + +* Menu: + +* Dnl:: Deleting whitespace in input +* Changequote:: Changing the quote characters +* Changecom:: Changing the comment delimiters +* Changeword:: Changing the lexical structure of words +* M4wrap:: Saving text until end of input + + +File: m4.info, Node: Dnl, Next: Changequote, Up: Input Control + +8.1 Deleting whitespace in input +================================ + +The builtin `dnl' stands for "Discard to Next Line": + + -- Builtin: dnl + All characters, up to and including the next newline, are discarded + without performing any macro expansion. A warning is issued if + the end of the file is encountered without a newline. + + The expansion of `dnl' is void. + + It is often used in connection with `define', to remove the newline +that follows the call to `define'. Thus + + define(`foo', `Macro `foo'.')dnl A very simple macro, indeed. + foo + =>Macro foo. + + The input up to and including the next newline is discarded, as +opposed to the way comments are treated (*note Comments::). + + Usually, `dnl' is immediately followed by an end of line or some +other whitespace. GNU `m4' will produce a warning diagnostic if `dnl' +is followed by an open parenthesis. In this case, `dnl' will collect +and process all arguments, looking for a matching close parenthesis. +All predictable side effects resulting from this collection will take +place. `dnl' will return no output. The input following the matching +close parenthesis up to and including the next newline, on whatever +line containing it, will still be discarded. + + dnl(`args are ignored, but side effects occur', + define(`foo', `like this')) while this text is ignored: undefine(`foo') + error-->m4:stdin:1: Warning: excess arguments to builtin `dnl' ignored + See how `foo' was defined, foo? + =>See how foo was defined, like this? + + If the end of file is encountered without a newline character, a +warning is issued and dnl stops consuming input. + + m4wrap(`m4wrap(`2 hi + ')0 hi dnl 1 hi') + => + define(`hi', `HI') + => + ^D + error-->m4:stdin:1: Warning: end of file treated as newline + =>0 HI 2 HI + + +File: m4.info, Node: Changequote, Next: Changecom, Prev: Dnl, Up: Input Control + +8.2 Changing the quote characters +================================= + +The default quote delimiters can be changed with the builtin +`changequote': + + -- Builtin: changequote ([START = ``'], [END = `'']) + This sets START as the new begin-quote delimiter and END as the + new end-quote delimiter. If both arguments are missing, the + default quotes (``' and `'') are used. If START is void, then + quoting is disabled. Otherwise, if END is missing or void, the + default end-quote delimiter (`'') is used. The quote delimiters + can be of any length. + + The expansion of `changequote' is void. + + changequote(`[', `]') + => + define([foo], [Macro [foo].]) + => + foo + =>Macro foo. + + The quotation strings can safely contain eight-bit characters. If +no single character is appropriate, START and END can be of any length. +Other implementations cap the delimiter length to five characters, but +GNU has no inherent limit. + + changequote(`[[[', `]]]') + => + define([[[foo]]], [[[Macro [[[[[foo]]]]].]]]) + => + foo + =>Macro [[foo]]. + + Calling `changequote' with START as the empty string will +effectively disable the quoting mechanism, leaving no way to quote text. +However, using an empty string is not portable, as some other +implementations of `m4' revert to the default quoting, while others +preserve the prior non-empty delimiter. If START is not empty, then an +empty END will use the default end-quote delimiter of `'', as +otherwise, it would be impossible to end a quoted string. Again, this +is not portable, as some other `m4' implementations reuse START as the +end-quote delimiter, while others preserve the previous non-empty +value. Omitting both arguments restores the default begin-quote and +end-quote delimiters; fortunately this behavior is portable to all +implementations of `m4'. + + define(`foo', `Macro `FOO'.') + => + changequote(`', `') + => + foo + =>Macro `FOO'. + `foo' + =>`Macro `FOO'.' + changequote(`,) + => + foo + =>Macro FOO. + + There is no way in `m4' to quote a string containing an unmatched +begin-quote, except using `changequote' to change the current quotes. + + If the quotes should be changed from, say, `[' to `[[', temporary +quote characters have to be defined. To achieve this, two calls of +`changequote' must be made, one for the temporary quotes and one for +the new quotes. + + Macros are recognized in preference to the begin-quote string, so if +a prefix of START can be recognized as part of a potential macro name, +the quoting mechanism is effectively disabled. Unless you use +`changeword' (*note Changeword::), this means that START should not +begin with a letter, digit, or `_' (underscore). However, even though +quoted strings are not recognized, the quote characters can still be +discerned in macro expansion and in trace output. + + define(`echo', `$@') + => + define(`hi', `HI') + => + changequote(`q', `Q') + => + q hi Q hi + =>q HI Q HI + echo(hi) + =>qHIQ + changequote + => + changequote(`-', `EOF') + => + - hi EOF hi + => hi HI + changequote + => + changequote(`1', `2') + => + hi1hi2 + =>hi1hi2 + hi 1hi2 + =>HI hi + + Quotes are recognized in preference to argument collection. In +particular, if START is a single `(', then argument collection is +effectively disabled. For portability with other implementations, it +is a good idea to avoid `(', `,', and `)' as the first character in +START. + + define(`echo', `$#:$@:') + => + define(`hi', `HI') + => + changequote(`(',`)') + => + echo(hi) + =>0::hi + changequote + => + changequote(`((', `))') + => + echo(hi) + =>1:HI: + echo((hi)) + =>0::hi + changequote + => + changequote(`,', `)') + => + echo(hi,hi)bye) + =>1:HIhibye: + + If END is a prefix of START, the end-quote will be recognized in +preference to a nested begin-quote. In particular, changing the quotes +to have the same string for START and END disables nesting of quotes. +When quote nesting is disabled, it is impossible to double-quote +strings across macro expansions, so using the same string is not done +very often. + + define(`hi', `HI') + => + changequote(`""', `"') + => + ""hi"""hi" + =>hihi + ""hi" ""hi" + =>hi hi + ""hi"" "hi" + =>hi" "HI" + changequote + => + `hi`hi'hi' + =>hi`hi'hi + changequote(`"', `"') + => + "hi"hi"hi" + =>hiHIhi + + It is an error if the end of file occurs within a quoted string. + + `hello world' + =>hello world + `dangling quote + ^D + error-->m4:stdin:2: ERROR: end of file in string + + +File: m4.info, Node: Changecom, Next: Changeword, Prev: Changequote, Up: Input Control + +8.3 Changing the comment delimiters +=================================== + +The default comment delimiters can be changed with the builtin macro +`changecom': + + -- Builtin: changecom ([START], [END = `']) + This sets START as the new begin-comment delimiter and END as the + new end-comment delimiter. If both arguments are missing, or + START is void, then comments are disabled. Otherwise, if END is + missing or void, the default end-comment delimiter of newline is + used. The comment delimiters can be of any length. + + The expansion of `changecom' is void. + + define(`comment', `COMMENT') + => + # A normal comment + =># A normal comment + changecom(`/*', `*/') + => + # Not a comment anymore + =># Not a COMMENT anymore + But: /* this is a comment now */ while this is not a comment + =>But: /* this is a comment now */ while this is not a COMMENT + + Note how comments are copied to the output, much as if they were +quoted strings. If you want the text inside a comment expanded, quote +the begin-comment delimiter. + + Calling `changecom' without any arguments, or with START as the +empty string, will effectively disable the commenting mechanism. To +restore the original comment start of `#', you must explicitly ask for +it. If START is not empty, then an empty END will use the default +end-comment delimiter of newline, as otherwise, it would be impossible +to end a comment. However, this is not portable, as some other `m4' +implementations preserve the previous non-empty delimiters instead. + + define(`comment', `COMMENT') + => + changecom + => + # Not a comment anymore + =># Not a COMMENT anymore + changecom(`#', `') + => + # comment again + =># comment again + + The comment strings can safely contain eight-bit characters. If no +single character is appropriate, START and END can be of any length. +Other implementations cap the delimiter length to five characters, but +GNU has no inherent limit. + + Comments are recognized in preference to macros. However, this is +not compatible with other implementations, where macros and even quoting +takes precedence over comments, so it may change in a future release. +For portability, this means that START should not begin with a letter, +digit, or `_' (underscore), and that neither the start-quote nor the +start-comment string should be a prefix of the other. + + define(`hi', `HI') + => + define(`hi1hi2', `hello') + => + changecom(`q', `Q') + => + q hi Q hi + =>q hi Q HI + changecom(`1', `2') + => + hi1hi2 + =>hello + hi 1hi2 + =>HI 1hi2 + + Comments are recognized in preference to argument collection. In +particular, if START is a single `(', then argument collection is +effectively disabled. For portability with other implementations, it +is a good idea to avoid `(', `,', and `)' as the first character in +START. + + define(`echo', `$#:$@:') + => + define(`hi', `HI') + => + changecom(`(',`)') + => + echo(hi) + =>0::(hi) + changecom + => + changecom(`((', `))') + => + echo(hi) + =>1:HI: + echo((hi)) + =>0::((hi)) + changecom(`,', `)') + => + echo(hi,hi)bye) + =>1:HI,hi)bye: + + It is an error if the end of file occurs within a comment. + + changecom(`/*', `*/') + => + /*dangling comment + ^D + error-->m4:stdin:2: ERROR: end of file in comment + + +File: m4.info, Node: Changeword, Next: M4wrap, Prev: Changecom, Up: Input Control + +8.4 Changing the lexical structure of words +=========================================== + + The macro `changeword' and all associated functionality is + experimental. It is only available if the `--enable-changeword' + option was given to `configure', at GNU `m4' installation time. + The functionality will go away in the future, to be replaced by + other new features that are more efficient at providing the same + capabilities. _Do not rely on it_. Please direct your comments + about it the same way you would do for bugs. + + A file being processed by `m4' is split into quoted strings, words +(potential macro names) and simple tokens (any other single character). +Initially a word is defined by the following regular expression: + + [_a-zA-Z][_a-zA-Z0-9]* + + Using `changeword', you can change this regular expression: + + -- Optional builtin: changeword (REGEX) + Changes the regular expression for recognizing macro names to be + REGEX. If REGEX is empty, use `[_a-zA-Z][_a-zA-Z0-9]*'. REGEX + must obey the constraint that every prefix of the desired final + pattern is also accepted by the regular expression. If REGEX + contains grouping parentheses, the macro invoked is the portion + that matched the first group, rather than the entire matching + string. + + The expansion of `changeword' is void. The macro `changeword' is + recognized only with parameters. + + Relaxing the lexical rules of `m4' might be useful (for example) if +you wanted to apply translations to a file of numbers: + + ifdef(`changeword', `', `errprint(` skipping: no changeword support + ')m4exit(`77')')dnl + changeword(`[_a-zA-Z0-9]+') + => + define(`1', `0')1 + =>0 + + Tightening the lexical rules is less useful, because it will +generally make some of the builtins unavailable. You could use it to +prevent accidental call of builtins, for example: + + ifdef(`changeword', `', `errprint(` skipping: no changeword support + ')m4exit(`77')')dnl + define(`_indir', defn(`indir')) + => + changeword(`_[_a-zA-Z0-9]*') + => + esyscmd(`foo') + =>esyscmd(foo) + _indir(`esyscmd', `echo hi') + =>hi + => + + Because `m4' constructs its words a character at a time, there is a +restriction on the regular expressions that may be passed to +`changeword'. This is that if your regular expression accepts `foo', +it must also accept `f' and `fo'. + + ifdef(`changeword', `', `errprint(` skipping: no changeword support + ')m4exit(`77')')dnl + define(`foo + ', `bar + ') + => + dnl This example wants to recognize changeword, dnl, and `foo\n'. + dnl First, we check that our regexp will match. + regexp(`changeword', `[cd][a-z]*\|foo[ + ]') + =>0 + regexp(`foo + ', `[cd][a-z]*\|foo[ + ]') + =>0 + regexp(`f', `[cd][a-z]*\|foo[ + ]') + =>-1 + foo + =>foo + changeword(`[cd][a-z]*\|foo[ + ]') + => + dnl Even though `foo\n' matches, we forgot to allow `f'. + foo + =>foo + changeword(`[cd][a-z]*\|fo*[ + ]?') + => + dnl Now we can call `foo\n'. + foo + =>bar + + `changeword' has another function. If the regular expression +supplied contains any grouped subexpressions, then text outside the +first of these is discarded before symbol lookup. So: + + ifdef(`changeword', `', `errprint(` skipping: no changeword support + ')m4exit(`77')')dnl + ifdef(`__unix__', , + `errprint(` skipping: syscmd does not have unix semantics + ')m4exit(`77')')dnl + changecom(`/*', `*/')dnl + define(`foo', `bar')dnl + changeword(`#\([_a-zA-Z0-9]*\)') + => + #esyscmd(`echo foo \#foo') + =>foo bar + => + + `m4' now requires a `#' mark at the beginning of every macro +invocation, so one can use `m4' to preprocess plain text without losing +various words like `divert'. + + In `m4', macro substitution is based on text, while in TeX, it is +based on tokens. `changeword' can throw this difference into relief. +For example, here is the same idea represented in TeX and `m4'. First, +the TeX version: + + \def\a{\message{Hello}} + \catcode`\@=0 + \catcode`\\=12 + @a + @bye + =>Hello + +Then, the `m4' version: + + ifdef(`changeword', `', `errprint(` skipping: no changeword support + ')m4exit(`77')')dnl + define(`a', `errprint(`Hello')')dnl + changeword(`@\([_a-zA-Z0-9]*\)') + => + @a + =>errprint(Hello) + + In the TeX example, the first line defines a macro `a' to print the +message `Hello'. The second line defines <@> to be usable instead of +<\> as an escape character. The third line defines <\> to be a normal +printing character, not an escape. The fourth line invokes the macro +`a'. So, when TeX is run on this file, it displays the message `Hello'. + + When the `m4' example is passed through `m4', it outputs +`errprint(Hello)'. The reason for this is that TeX does lexical +analysis of macro definition when the macro is _defined_. `m4' just +stores the text, postponing the lexical analysis until the macro is +_used_. + + You should note that using `changeword' will slow `m4' down by a +factor of about seven, once it is changed to something other than the +default regular expression. You can invoke `changeword' with the empty +string to restore the default word definition, and regain the parsing +speed. + + +File: m4.info, Node: M4wrap, Prev: Changeword, Up: Input Control + +8.5 Saving text until end of input +================================== + +It is possible to `save' some text until the end of the normal input has +been seen. Text can be saved, to be read again by `m4' when the normal +input has been exhausted. This feature is normally used to initiate +cleanup actions before normal exit, e.g., deleting temporary files. + + To save input text, use the builtin `m4wrap': + + -- Builtin: m4wrap (STRING, ...) + Stores STRING in a safe place, to be reread when end of input is + reached. As a GNU extension, additional arguments are + concatenated with a space to the STRING. + + The expansion of `m4wrap' is void. The macro `m4wrap' is + recognized only with parameters. + + define(`cleanup', `This is the `cleanup' action. + ') + => + m4wrap(`cleanup') + => + This is the first and last normal input line. + =>This is the first and last normal input line. + ^D + =>This is the cleanup action. + + The saved input is only reread when the end of normal input is seen, +and not if `m4exit' is used to exit `m4'. + + It is safe to call `m4wrap' from saved text, but then the order in +which the saved text is reread is undefined. If `m4wrap' is not used +recursively, the saved pieces of text are reread in the opposite order +in which they were saved (LIFO--last in, first out). However, this +behavior is likely to change in a future release, to match POSIX, so +you should not depend on this order. + + Here is an example of implementing a factorial function using +`m4wrap': + + define(`f', `ifelse(`$1', `0', `Answer: 0!=1 + ', eval(`$1>1'), `0', `Answer: $2$1=eval(`$2$1') + ', `m4wrap(`f(decr(`$1'), `$2$1*')')')') + => + f(`10') + => + ^D + =>Answer: 10*9*8*7*6*5*4*3*2*1=3628800 + + Invocations of `m4wrap' at the same recursion level are concatenated +and rescanned as usual: + + define(`aa', `AA + ') + => + m4wrap(`a')m4wrap(`a') + => + ^D + =>AA + +however, the transition between recursion levels behaves like an end of +file condition between two input files. + + m4wrap(`m4wrap(`)')len(abc') + => + ^D + error-->m4:stdin:1: ERROR: end of file in argument list + + +File: m4.info, Node: File Inclusion, Next: Diversions, Prev: Input Control, Up: Top + +9 File inclusion +**************** + +`m4' allows you to include named files at any point in the input. + +* Menu: + +* Include:: Including named files +* Search Path:: Searching for include files + + +File: m4.info, Node: Include, Next: Search Path, Up: File Inclusion + +9.1 Including named files +========================= + +There are two builtin macros in `m4' for including files: + + -- Builtin: include (FILE) + -- Builtin: sinclude (FILE) + Both macros cause the file named FILE to be read by `m4'. When + the end of the file is reached, input is resumed from the previous + input file. + + The expansion of `include' and `sinclude' is therefore the + contents of FILE. + + If FILE does not exist (or cannot be read), the expansion is void, + and `include' will fail with an error while `sinclude' is silent. + The empty string counts as a file that does not exist. + + The macros `include' and `sinclude' are recognized only with + parameters. + + include(`none') + error-->m4:stdin:1: cannot open `none': No such file or directory + => + include() + error-->m4:stdin:2: cannot open `': No such file or directory + => + sinclude(`none') + => + sinclude() + => + + The rest of this section assumes that `m4' is invoked with the `-I' +option (*note Invoking m4: Preprocessor features.) pointing to the +`m4-1.4.8/examples' directory shipped as part of the GNU `m4' package. +The file `m4-1.4.8/examples/incl.m4' in the distribution contains the +lines: + + Include file start + foo + Include file end + + Normally file inclusion is used to insert the contents of a file +into the input stream. The contents of the file will be read by `m4' +and macro calls in the file will be expanded: + + define(`foo', `FOO') + => + include(`incl.m4') + =>Include file start + =>FOO + =>Include file end + => + + The fact that `include' and `sinclude' expand to the contents of the +file can be used to define macros that operate on entire files. Here +is an example, which defines `bar' to expand to the contents of +`incl.m4': + + define(`bar', include(`incl.m4')) + => + This is `bar': >>bar<< + =>This is bar: >>Include file start + =>foo + =>Include file end + =><< + + This use of `include' is not trivial, though, as files can contain +quotes, commas, and parentheses, which can interfere with the way the +`m4' parser works. GNU `m4' seamlessly concatenates the file contents +with the next character, even if the included file ended in the middle +of a comment, string, or macro call. These conditions are only treated +as end of file errors if specified as input files on the command line. + + In GNU `m4', an alternative method of reading files is using +`undivert' (*note Undivert::) on a named file. + + +File: m4.info, Node: Search Path, Prev: Include, Up: File Inclusion + +9.2 Searching for include files +=============================== + +GNU `m4' allows included files to be found in other directories than +the current working directory. + + If the `--prepend-include' or `-B' command-line option was provided +(*note Invoking m4: Preprocessor features.), those directories are +searched first, in reverse order that those options were listed on the +command line. Then `m4' looks in the current working directory. Next +comes the directories specified with the `--include' or `-I' option, in +the order found on the command line. Finally, if the `M4PATH' +environment variable is set, it is expected to contain a +colon-separated list of directories, which will be searched in order. + + If the automatic search for include-files causes trouble, the `p' +debug flag (*note Debug Levels::) can help isolate the problem. + + +File: m4.info, Node: Diversions, Next: Text handling, Prev: File Inclusion, Up: Top + +10 Diverting and undiverting output +*********************************** + +Diversions are a way of temporarily saving output. The output of `m4' +can at any time be diverted to a temporary file, and be reinserted into +the output stream, "undiverted", again at a later time. + + Numbered diversions are counted from 0 upwards, diversion number 0 +being the normal output stream. The number of simultaneous diversions +is limited mainly by the memory used to describe them, because GNU `m4' +tries to keep diversions in memory. However, there is a limit to the +overall memory usable by all diversions taken altogether (512K, +currently). When this maximum is about to be exceeded, a temporary +file is opened to receive the contents of the biggest diversion still +in memory, freeing this memory for other diversions. When creating the +temporary file, `m4' honors the value of the environment variable +`TMPDIR', and falls back to `/tmp'. So, it is theoretically possible +that the number and aggregate size of diversions is limited only by +available disk space. + + Diversions make it possible to generate output in a different order +than the input was read. It is possible to implement topological +sorting dependencies. For example, GNU Autoconf makes use of +diversions under the hood to ensure that the expansion of a prerequisite +macro appears in the output prior to the expansion of a dependent macro, +regardless of which order the two macros were invoked in the user's +input file. + +* Menu: + +* Divert:: Diverting output +* Undivert:: Undiverting output +* Divnum:: Diversion numbers +* Cleardivert:: Discarding diverted text + + +File: m4.info, Node: Divert, Next: Undivert, Up: Diversions + +10.1 Diverting output +===================== + +Output is diverted using `divert': + + -- Builtin: divert ([NUMBER = `0']) + The current diversion is changed to NUMBER. If NUMBER is left out + or empty, it is assumed to be zero. If NUMBER cannot be parsed, + the diversion is unchanged. + + The expansion of `divert' is void. + + When all the `m4' input will have been processed, all existing +diversions are automatically undiverted, in numerical order. + + divert(`1') + This text is diverted. + divert + => + This text is not diverted. + =>This text is not diverted. + ^D + => + =>This text is diverted. + + Several calls of `divert' with the same argument do not overwrite +the previous diverted text, but append to it. Diversions are printed +after any wrapped text is expanded. + + define(`text', `TEXT') + => + divert(`1')`diverted text.' + divert + => + m4wrap(`Wrapped text preceeds ') + => + ^D + =>Wrapped TEXT preceeds diverted text. + + If output is diverted to a negative diversion, it is simply +discarded. This can be used to suppress unwanted output. A common +example of unwanted output is the trailing newlines after macro +definitions. Here is a common programming idiom in `m4' for avoiding +them. + + divert(`-1') + define(`foo', `Macro `foo'.') + define(`bar', `Macro `bar'.') + divert + => + + Traditional implementations only supported ten diversions. But as a +GNU extension, diversion numbers can be as large as positive integers +will allow, rather than treating a multi-digit diversion number as a +request to discard text. + + divert(eval(`1<<28'))world + divert(`2')hello + ^D + =>hello + =>world + + Note that `divert' is an English word, but also an active macro +without arguments. When processing plain text, the word might appear in +normal text and be unintentionally swallowed as a macro invocation. One +way to avoid this is to use the `-P' option to rename all builtins +(*note Invoking m4: Operation modes.). Another is to write a wrapper +that requires a parameter to be recognized. + + We decided to divert the stream for irrigation. + =>We decided to the stream for irrigation. + define(`divert', `ifelse(`$#', `0', ``$0'', `builtin(`$0', $@)')') + => + divert(`-1') + Ignored text. + divert(`0') + => + We decided to divert the stream for irrigation. + =>We decided to divert the stream for irrigation. + + +File: m4.info, Node: Undivert, Next: Divnum, Prev: Divert, Up: Diversions + +10.2 Undiverting output +======================= + +Diverted text can be undiverted explicitly using the builtin `undivert': + + -- Builtin: undivert ([DIVERSIONS...]) + Undiverts the numeric DIVERSIONS given by the arguments, in the + order given. If no arguments are supplied, all diversions are + undiverted, in numerical order. + + As a GNU extension, DIVERSIONS may contain non-numeric strings, + which are treated as the names of files to copy into the output + without expansion. A warning is issued if a file could not be + opened. + + The expansion of `undivert' is void. + + divert(`1') + This text is diverted. + divert + => + This text is not diverted. + =>This text is not diverted. + undivert(`1') + => + =>This text is diverted. + => + + Notice the last two blank lines. One of them comes from the newline +following `undivert', the other from the newline that followed the +`divert'! A diversion often starts with a blank line like this. + + When diverted text is undiverted, it is _not_ reread by `m4', but +rather copied directly to the current output, and it is therefore not +an error to undivert into a diversion. Undiverting the empty string is +the same as specifying diversion 0; in either case nothing happens +since the output has already been flushed. + + divert(`1')diverted text + divert + => + undivert() + => + undivert(`0') + => + undivert + =>diverted text + => + + When a diversion has been undiverted, the diverted text is discarded, +and it is not possible to bring back diverted text more than once. + + divert(`1') + This text is diverted first. + divert(`0')undivert(`1')dnl + => + =>This text is diverted first. + undivert(`1') + => + divert(`1') + This text is also diverted but not appended. + divert(`0')undivert(`1')dnl + => + =>This text is also diverted but not appended. + + Attempts to undivert the current diversion are silently ignored. +Thus, when the current diversion is not 0, the current diversion does +not get rearranged among the other diversions. + + divert(`1')one + divert(`2')two + divert(`3')three + divert(`2')undivert`'dnl + divert`'undivert`'dnl + =>two + =>one + =>three + + GNU `m4' allows named files to be undiverted. Given a non-numeric +argument, the contents of the file named will be copied, uninterpreted, +to the current output. This complements the builtin `include' (*note +Include::). To illustrate the difference, the file +`m4-1.4.8/examples/foo' contains the word `bar': + + define(`bar', `BAR') + => + undivert(`foo') + =>bar + => + include(`foo') + =>BAR + => + + If the file is not found (or cannot be read), an error message is +issued, and the expansion is void. + + +File: m4.info, Node: Divnum, Next: Cleardivert, Prev: Undivert, Up: Diversions + +10.3 Diversion numbers +====================== + +The current diversion is tracked by the builtin `divnum': + + -- Builtin: divnum + Expands to the number of the current diversion. + + Initial divnum + =>Initial 0 + divert(`1') + Diversion one: divnum + divert(`2') + Diversion two: divnum + ^D + => + =>Diversion one: 1 + => + =>Diversion two: 2 + + +File: m4.info, Node: Cleardivert, Prev: Divnum, Up: Diversions + +10.4 Discarding diverted text +============================= + +Often it is not known, when output is diverted, whether the diverted +text is actually needed. Since all non-empty diversion are brought back +on the main output stream when the end of input is seen, a method of +discarding a diversion is needed. If all diversions should be +discarded, the easiest is to end the input to `m4' with `divert(`-1')' +followed by an explicit `undivert': + + divert(`1') + Diversion one: divnum + divert(`2') + Diversion two: divnum + divert(`-1') + undivert + ^D + +No output is produced at all. + + Clearing selected diversions can be done with the following macro: + + -- Composite: cleardivert ([DIVERSIONS...]) + Discard the contents of each of the listed numeric DIVERSIONS. + + define(`cleardivert', + `pushdef(`_n', divnum)divert(`-1')undivert($@)divert(_n)popdef(`_n')') + => + + It is called just like `undivert', but the effect is to clear the +diversions, given by the arguments. (This macro has a nasty bug! You +should try to see if you can find it and correct it; or *note Answers: +Improved cleardivert.). + + +File: m4.info, Node: Text handling, Next: Arithmetic, Prev: Diversions, Up: Top + +11 Macros for text handling +*************************** + +There are a number of builtins in `m4' for manipulating text in various +ways, extracting substrings, searching, substituting, and so on. + +* Menu: + +* Len:: Calculating length of strings +* Index macro:: Searching for substrings +* Regexp:: Searching for regular expressions +* Substr:: Extracting substrings +* Translit:: Translating characters +* Patsubst:: Substituting text by regular expression +* Format:: Formatting strings (printf-like) + + +File: m4.info, Node: Len, Next: Index macro, Up: Text handling + +11.1 Calculating length of strings +================================== + +The length of a string can be calculated by `len': + + -- Builtin: len (STRING) + Expands to the length of STRING, as a decimal number. + + The macro `len' is recognized only with parameters. + + len() + =>0 + len(`abcdef') + =>6 + + +File: m4.info, Node: Index macro, Next: Regexp, Prev: Len, Up: Text handling + +11.2 Searching for substrings +============================= + +Searching for substrings is done with `index': + + -- Builtin: index (STRING, SUBSTRING) + Expands to the index of the first occurrence of SUBSTRING in + STRING. The first character in STRING has index 0. If SUBSTRING + does not occur in STRING, `index' expands to `-1'. + + The macro `index' is recognized only with parameters. + + index(`gnus, gnats, and armadillos', `nat') + =>7 + index(`gnus, gnats, and armadillos', `dag') + =>-1 + + Omitting SUBSTRING evokes a warning, but still produces output. + + index(`abc') + error-->m4:stdin:1: Warning: too few arguments to builtin `index' + =>0 + + +File: m4.info, Node: Regexp, Next: Substr, Prev: Index macro, Up: Text handling + +11.3 Searching for regular expressions +====================================== + +Searching for regular expressions is done with the builtin `regexp': + + -- Builtin: regexp (STRING, REGEXP, [REPLACEMENT]) + Searches for REGEXP in STRING. The syntax for regular expressions + is the same as in GNU Emacs. *Note Syntax of Regular Expressions: + (emacs)Regexps. + + If REPLACEMENT is omitted, `regexp' expands to the index of the + first match of REGEXP in STRING. If REGEXP does not match + anywhere in STRING, it expands to -1. + + If REPLACEMENT is supplied, and there was a match, `regexp' + changes the expansion to this argument, with `\N' substituted by + the text matched by the Nth parenthesized sub-expression of + REGEXP, up to nine sub-expressions. The escape `\&' is replaced + by the text of the entire regular expression matched. For all + other characters, `\' treats the next character literally. A + warning is issued if there were fewer sub-expressions than the + `\N' requested, or if there is a trailing `\'. If there was no + match, `regexp' expands to the empty string. + + The macro `regexp' is recognized only with parameters. + + regexp(`GNUs not Unix', `\<[a-z]\w+') + =>5 + regexp(`GNUs not Unix', `\-1 + regexp(`GNUs not Unix', `\w\(\w+\)$', `*** \& *** \1 ***') + =>*** Unix *** nix *** + regexp(`GNUs not Unix', `\ + + Here are some more examples on the handling of backslash: + + regexp(`abc', `\(b\)', `\\\10\a') + =>\b0a + regexp(`abc', `b', `\1\') + error-->m4:stdin:2: Warning: sub-expression 1 not present + error-->m4:stdin:2: Warning: trailing \ ignored in replacement + => + regexp(`abc', `\(\(d\)?\)\(c\)', `\1\2\3\4\5\6') + error-->m4:stdin:3: Warning: sub-expression 4 not present + error-->m4:stdin:3: Warning: sub-expression 5 not present + error-->m4:stdin:3: Warning: sub-expression 6 not present + =>c + + Omitting REGEXP evokes a warning, but still produces output. + + regexp(`abc') + error-->m4:stdin:1: Warning: too few arguments to builtin `regexp' + =>0 + + +File: m4.info, Node: Substr, Next: Translit, Prev: Regexp, Up: Text handling + +11.4 Extracting substrings +========================== + +Substrings are extracted with `substr': + + -- Builtin: substr (STRING, FROM, [LENGTH]) + Expands to the substring of STRING, which starts at index FROM, + and extends for LENGTH characters, or to the end of STRING, if + LENGTH is omitted. The starting index of a string is always 0. + The expansion is empty if there is an error parsing FROM or + LENGTH, if FROM is beyond the end of STRING, or if LENGTH is + negative. + + The macro `substr' is recognized only with parameters. + + substr(`gnus, gnats, and armadillos', `6') + =>gnats, and armadillos + substr(`gnus, gnats, and armadillos', `6', `5') + =>gnats + + Omitting FROM evokes a warning, but still produces output. + + substr(`abc') + error-->m4:stdin:1: Warning: too few arguments to builtin `substr' + =>abc + substr(`abc',) + error-->m4:stdin:2: empty string treated as 0 in builtin `substr' + =>abc + + +File: m4.info, Node: Translit, Next: Patsubst, Prev: Substr, Up: Text handling + +11.5 Translating characters +=========================== + +Character translation is done with `translit': + + -- Builtin: translit (STRING, CHARS, [REPLACEMENT]) + Expands to STRING, with each character that occurs in CHARS + translated into the character from REPLACEMENT with the same index. + + If REPLACEMENT is shorter than CHARS, the excess characters of + CHARS are deleted from the expansion; if CHARS is shorter, the + excess characters in REPLACEMENT are silently ignored. If + REPLACEMENT is omitted, all characters in STRING that are present + in CHARS are deleted from the expansion. If a character appears + more than once in CHARS, only the first instance is used in making + the translation. Only a single translation pass is made, even if + characters in REPLACEMENT also appear in CHARS. + + As a GNU extension, both CHARS and REPLACEMENT can contain + character-ranges, e.g., `a-z' (meaning all lowercase letters) or + `0-9' (meaning all digits). To include a dash `-' in CHARS or + REPLACEMENT, place it first or last in the entire string, or as + the last character of a range. Back-to-back ranges can share a + common endpoint. It is not an error for the last character in the + range to be `larger' than the first. In that case, the range runs + backwards, i.e., `9-0' means the string `9876543210'. The + expansion of a range is dependent on the underlying encoding of + characters, so using ranges is not always portable between + machines. + + The macro `translit' is recognized only with parameters. + + translit(`GNUs not Unix', `A-Z') + =>s not nix + translit(`GNUs not Unix', `a-z', `A-Z') + =>GNUS NOT UNIX + translit(`GNUs not Unix', `A-Z', `z-a') + =>tmfs not fnix + translit(`+,-12345', `+--1-5', `<;>a-c-a') + =><;>abcba + translit(`abcdef', `aabdef', `bcged') + =>bgced + + In the ASCII encoding, the first example deletes all uppercase +letters, the second converts lowercase to uppercase, and the third +`mirrors' all uppercase letters, while converting them to lowercase. +The two first cases are by far the most common, even though they are not +portable to EBCDIC or other encodings. The fourth example shows a +range ending in `-', as well as back-to-back ranges. The final example +shows that `a' is mapped to `b', not `c'; the resulting `b' is not +further remapped to `g'; the `d' and `e' are swapped, and the `f' is +discarded. + + Omitting CHARS evokes a warning, but still produces output. + + translit(`abc') + error-->m4:stdin:1: Warning: too few arguments to builtin `translit' + =>abc + + +File: m4.info, Node: Patsubst, Next: Format, Prev: Translit, Up: Text handling + +11.6 Substituting text by regular expression +============================================ + +Global substitution in a string is done by `patsubst': + + -- Builtin: patsubst (STRING, REGEXP, [REPLACEMENT]) + Searches STRING for matches of REGEXP, and substitutes REPLACEMENT + for each match. The syntax for regular expressions is the same as + in GNU Emacs (*note Regexp::). + + The parts of STRING that are not covered by any match of REGEXP + are copied to the expansion. Whenever a match is found, the + search proceeds from the end of the match, so a character from + STRING will never be substituted twice. If REGEXP matches a + string of zero length, the start position for the search is + incremented, to avoid infinite loops. + + When a replacement is to be made, REPLACEMENT is inserted into the + expansion, with `\N' substituted by the text matched by the Nth + parenthesized sub-expression of PATSUBST, for up to nine + sub-expressions. The escape `\&' is replaced by the text of the + entire regular expression matched. For all other characters, `\' + treats the next character literally. A warning is issued if there + were fewer sub-expressions than the `\N' requested, or if there is + a trailing `\'. + + The REPLACEMENT argument can be omitted, in which case the text + matched by REGEXP is deleted. + + The macro `patsubst' is recognized only with parameters. + + patsubst(`GNUs not Unix', `^', `OBS: ') + =>OBS: GNUs not Unix + patsubst(`GNUs not Unix', `\<', `OBS: ') + =>OBS: GNUs OBS: not OBS: Unix + patsubst(`GNUs not Unix', `\w*', `(\&)') + =>(GNUs)() (not)() (Unix)() + patsubst(`GNUs not Unix', `\w+', `(\&)') + =>(GNUs) (not) (Unix) + patsubst(`GNUs not Unix', `[A-Z][a-z]+') + =>GN not + patsubst(`GNUs not Unix', `not', `NOT\') + error-->m4:stdin:6: Warning: trailing \ ignored in replacement + =>GNUs NOT Unix + + Here is a slightly more realistic example, which capitalizes +individual word or whole sentences, by substituting calls of the macros +`upcase' and `downcase' into the strings. + + -- Composite: upcase (TEXT) + -- Composite: downcase (TEXT) + -- Composite: capitalize (TEXT) + Expand to TEXT, but with capitalization changed: `upcase' changes + all letters to upper case, `downcase' changes all letters to lower + case, and `capitalize' changes the first character of each word to + upper case and the remaining characters to lower case. + + define(`upcase', `translit(`$*', `a-z', `A-Z')')dnl + define(`downcase', `translit(`$*', `A-Z', `a-z')')dnl + define(`capitalize1', + `regexp(`$1', `^\(\w\)\(\w*\)', + `upcase(`\1')`'downcase(`\2')')')dnl + define(`capitalize', + `patsubst(`$1', `\w+', `capitalize1(`\&')')')dnl + capitalize(`GNUs not Unix') + =>Gnus Not Unix + + While `regexp' replaces the whole input with the replacement as soon +as there is a match, `patsubst' replaces each _occurrence_ of a match +and preserves non-matching pieces: + + define(`patreg', + `patsubst($@) + regexp($@)')dnl + patreg(`bar foo baz Foo', `foo\|Foo', `FOO') + =>bar FOO baz FOO + =>FOO + patreg(`aba abb 121', `\(.\)\(.\)\1', `\2\1\2') + =>bab abb 212 + =>bab + + Omitting REGEXP evokes a warning, but still produces output. + + patsubst(`abc') + error-->m4:stdin:1: Warning: too few arguments to builtin `patsubst' + =>abc + + +File: m4.info, Node: Format, Prev: Patsubst, Up: Text handling + +11.7 Formatted output +===================== + +Formatted output can be made with `format': + + -- Builtin: format (FORMAT-STRING, ...) + Works much like the C function `printf'. The first argument + FORMAT-STRING can contain `%' specifications which are satisfied + by additional arguments, and the expansion of `format' is the + formatted string. + + The macro `format' is recognized only with parameters. + + Its use is best described by a few examples: + + define(`foo', `The brown fox jumped over the lazy dog') + => + format(`The string "%s" uses %d characters', foo, len(foo)) + =>The string "The brown fox jumped over the lazy dog" uses 38 characters + format(`%.0f', `56789.9876') + =>56790 + len(format(`%-*X', `300', `1')) + =>300 + + Using the `forloop' macro defined earlier (*note Forloop::), this +example shows how `format' can be used to produce tabular output. + + include(`forloop.m4') + => + forloop(`i', `1', `10', `format(`%6d squared is %10d + ', i, eval(i**2))') + => 1 squared is 1 + => 2 squared is 4 + => 3 squared is 9 + => 4 squared is 16 + => 5 squared is 25 + => 6 squared is 36 + => 7 squared is 49 + => 8 squared is 64 + => 9 squared is 81 + => 10 squared is 100 + => + + The builtin `format' is modeled after the ANSI C `printf' function, +and supports these `%' specifiers: `c', `s', `d', `o', `x', `X', `u', +`e', `E', `f', `F', `g', `G', and `%'; it supports field widths and +precisions, and the modifiers `+', `-', ` ', `0', `#', `h' and `l'. +For more details on the functioning of `printf', see the C Library +Manual. + + For now, unrecognized specifiers are silently ignored, but it is +anticipated that a future release of GNU `m4' will support more +specifiers, and give warnings when problems are encountered. Likewise, +escape sequences are not yet recognized. + + +File: m4.info, Node: Arithmetic, Next: Shell commands, Prev: Text handling, Up: Top + +12 Macros for doing arithmetic +****************************** + +Integer arithmetic is included in `m4', with a C-like syntax. As +convenient shorthands, there are builtins for simple increment and +decrement operations. + +* Menu: + +* Incr:: Decrement and increment operators +* Eval:: Evaluating integer expressions + + +File: m4.info, Node: Incr, Next: Eval, Up: Arithmetic + +12.1 Decrement and increment operators +====================================== + +Increment and decrement of integers are supported using the builtins +`incr' and `decr': + + -- Builtin: incr (NUMBER) + -- Builtin: decr (NUMBER) + Expand to the numerical value of NUMBER, incremented or + decremented, respectively, by one. Except for the empty string, + the expansion is empty if NUMBER could not be parsed. + + The macros `incr' and `decr' are recognized only with parameters. + + incr(`4') + =>5 + decr(`7') + =>6 + incr() + error-->m4:stdin:3: empty string treated as 0 in builtin `incr' + =>1 + decr() + error-->m4:stdin:4: empty string treated as 0 in builtin `decr' + =>-1 + + +File: m4.info, Node: Eval, Prev: Incr, Up: Arithmetic + +12.2 Evaluating integer expressions +=================================== + +Integer expressions are evaluated with `eval': + + -- Builtin: eval (EXPRESSION, [RADIX = `10'], [WIDTH]) + Expands to the value of EXPRESSION. The expansion is empty if an + error is encountered while parsing the arguments. If specified, + RADIX and WIDTH control the format of the output. + + The macro `eval' is recognized only with parameters. + + Expressions can contain the following operators, listed in order of +decreasing precedence. + +`+ -' + Unary plus and minus + +`**' + Exponentiation + +`* / %' + Multiplication, division and modulo + +`+ -' + Addition and subtraction + +`<< >>' + Shift left or right + +`== != > >= < <=' + Relational operators + +`!' + Logical negation + +`~' + Bitwise negation + +`&' + Bitwise and + +`^' + Bitwise exclusive-or + +`|' + Bitwise or + +`&&' + Logical and + +`||' + Logical or + + All operators, except exponentiation, are left associative. + + Note that some older `m4' implementations use `^' as an alternate +operator for exponentiation, although POSIX requires the C behavior of +bitwise exclusive-or. On the other hand, the precedence of `~' and `!' +are different in GNU `m4' than they are in C, matching the precedence +in traditional `m4' implementations. This behavior is likely to change +in a future version to match POSIX, so use parentheses to force the +desired precedence. + + Within EXPRESSION, (but not RADIX or WIDTH), numbers without a +special prefix are decimal. A simple `0' prefix introduces an octal +number. `0x' introduces a hexadecimal number. `0b' introduces a +binary number. `0r' introduces a number expressed in any radix between +1 and 36: the prefix should be immediately followed by the decimal +expression of the radix, a colon, then the digits making the number. +For radix 1, leading zeros are ignored and all remaining digits must be +`1'; for all other radices, the digits are `0', `1', `2', .... Beyond +`9', the digits are `a', `b' ... up to `z'. Lower and upper case +letters can be used interchangeably in numbers prefixes and as number +digits. + + Parentheses may be used to group subexpressions whenever needed. +For the relational operators, a true relation returns `1', and a false +relation return `0'. + + Here are a few examples of use of `eval'. + + eval(`-3 * 5') + =>-15 + eval(index(`Hello world', `llo') >= 0) + =>1 + eval(`0r1:0111 + 0b100 + 0r3:12') + =>12 + define(`square', `eval(`('$1`)**2')') + => + square(`9') + =>81 + square(square(`5')`+1') + =>676 + define(`foo', `666') + => + eval(`foo/6') + error-->m4:stdin:8: bad expression in eval: foo/6 + => + eval(foo/6) + =>111 + + As the last two lines show, `eval' does not handle macro names, even +if they expand to a valid expression (or part of a valid expression). +Therefore all macros must be expanded before they are passed to `eval'. + + All evaluation is done with 32-bit signed integers, assuming +2's-complement with wrap-around. The shift operators are defined in GNU +`m4' by doing an implicit bit-wise and of the right-hand operand with +0x1f, and sign-extension with right shift. + + eval(0x80000000 / -1) + =>-2147483648 + eval(0x80000000 % -1) + =>0 + eval(0x7fffffff) + =>2147483647 + incr(eval(0x7fffffff)) + =>-2147483648 + eval(-4 >> 33) + =>-2 + + If RADIX is specified, it specifies the radix to be used in the +expansion. The default radix is 10; this is also the case if RADIX is +the empty string. It is an error if the radix is outside the range of +1 through 36, inclusive. The result of `eval' is always taken to be +signed. No radix prefix is output, and for radices greater than 10, +the digits are lower case. The WIDTH argument specifies the minimum +output width, excluding any negative sign. The result is zero-padded +to extend the expansion to the requested width. It is an error if the +width is negative. On error, the expansion of `eval' is empty. + + eval(`666', `10') + =>666 + eval(`666', `11') + =>556 + eval(`666', `6') + =>3030 + eval(`666', `6', `10') + =>0000003030 + eval(`-666', `6', `10') + =>-0000003030 + eval(`10', `', `0') + =>10 + `0r1:'eval(`10', `1', `11') + =>0r1:01111111111 + eval(`10', `16') + =>a + + +File: m4.info, Node: Shell commands, Next: Miscellaneous, Prev: Arithmetic, Up: Top + +13 Macros for running shell commands +************************************ + +There are a few builtin macros in `m4' that allow you to run shell +commands from within `m4'. + + Note that the definition of a valid shell command is system +dependent. On UNIX systems, this is the typical `/bin/sh'. But on +other systems, such as native Windows, the shell has a different syntax +of commands that it understands. Some examples in this chapter assume +`/bin/sh', and also demonstrate how to quit early with a known exit +value if this is not the case. + +* Menu: + +* Platform macros:: Determining the platform +* Syscmd:: Executing simple commands +* Esyscmd:: Reading the output of commands +* Sysval:: Exit status +* Mkstemp:: Making temporary files + + +File: m4.info, Node: Platform macros, Next: Syscmd, Up: Shell commands + +13.1 Determining the platform +============================= + +Sometimes it is desirable for an input file to know which platform `m4' +is running on. GNU `m4' provides several macros that are predefined to +expand to the empty string; checking for their existence will confirm +platform details. + + -- Optional builtin: __gnu__ + -- Optional builtin: __os2__ + -- Optional builtin: os2 + -- Optional builtin: __unix__ + -- Optional builtin: unix + -- Optional builtin: __windows__ + -- Optional builtin: windows + Each of these macros is conditionally defined as needed to + describe the environment of `m4'. If defined, each macro expands + to the empty string. + + When GNU extensions are in effect (that is, when you did not use the +`-G' option, *note Invoking m4: Limits control.), GNU `m4' will define +the macro `__gnu__' to expand to the empty string. + + __gnu__ + => + ifdef(`__gnu__', `Extensions are active') + =>Extensions are active + + On UNIX systems, GNU `m4' will define `__unix__' by default, or +`unix' when the `-G' option is specified. + + On native Windows systems, GNU `m4' will define `__windows__' by +default, or `windows' when the `-G' option is specified. + + On OS/2 systems, GNU `m4' will define `__os2__' by default, or `os2' +when the `-G' option is specified. + + If GNU `m4' does not provide a platform macro for your system, +please report that as a bug. + + define(`provided', `0') + => + ifdef(`__unix__', `define(`provided', incr(provided))') + => + ifdef(`__windows__', `define(`provided', incr(provided))') + => + ifdef(`__os2__', `define(`provided', incr(provided))') + => + provided + =>1 + + +File: m4.info, Node: Syscmd, Next: Esyscmd, Prev: Platform macros, Up: Shell commands + +13.2 Executing simple commands +============================== + +Any shell command can be executed, using `syscmd': + + -- Builtin: syscmd (SHELL-COMMAND) + Executes SHELL-COMMAND as a shell command. + + The expansion of `syscmd' is void, _not_ the output from + SHELL-COMMAND! Output or error messages from SHELL-COMMAND are + not read by `m4'. *Note Esyscmd::, if you need to process the + command output. + + Prior to executing the command, `m4' flushes its buffers. The + default standard input, output and error of SHELL-COMMAND are the + same as those of `m4'. + + The macro `syscmd' is recognized only with parameters. + + define(`foo', `FOO') + => + syscmd(`echo foo') + =>foo + => + + Note how the expansion of `syscmd' keeps the trailing newline of the +command, as well as using the newline that appeared after the macro. + + As an example of SHELL-COMMAND using the same standard input as +`m4', the command line `echo "m4wrap(\`syscmd(\`cat')')" | m4' will +tell `m4' to read all of its input before executing the wrapped text, +then hand a valid (albeit emptied) pipe as standard input for the `cat' +subcommand. Therefore, you should be careful when using standard input +(either by specifying no files, or by passing `-' as a file name on the +command line, *note Invoking m4: Command line files.), and also +invoking subcommands via `syscmd' or `esyscmd' that consume data from +standard input. When standard input is a seekable file, the subprocess +will pick up with the next character not yet processed by `m4'; when it +is a pipe or other non-seekable file, there is no guarantee how much +data will already be buffered by `m4' and thus unavailable to the child. + + +File: m4.info, Node: Esyscmd, Next: Sysval, Prev: Syscmd, Up: Shell commands + +13.3 Reading the output of commands +=================================== + +If you want `m4' to read the output of a shell command, use `esyscmd': + + -- Builtin: esyscmd (SHELL-COMMAND) + Expands to the standard output of the shell command SHELL-COMMAND. + + Prior to executing the command, `m4' flushes its buffers. The + default standard input and standard error of SHELL-COMMAND are the + same as those of `m4'. The error output of SHELL-COMMAND is not a + part of the expansion: it will appear along with the error output + of `m4'. + + The macro `esyscmd' is recognized only with parameters. + + define(`foo', `FOO') + => + esyscmd(`echo foo') + =>FOO + => + + Note how the expansion of `esyscmd' keeps the trailing newline of +the command, as well as using the newline that appeared after the macro. + + Just as with `syscmd', care must be exercised when sharing standard +input between `m4' and the child process of `esyscmd'. + + +File: m4.info, Node: Sysval, Next: Mkstemp, Prev: Esyscmd, Up: Shell commands + +13.4 Exit status +================ + +To see whether a shell command succeeded, use `sysval': + + -- Builtin: sysval + Expands to the exit status of the last shell command run with + `syscmd' or `esyscmd'. Expands to 0 if no command has been run + yet. + + syscmd(`false') + => + ifelse(sysval, `0', `zero', `non-zero') + =>non-zero + syscmd(`exit 2') + => + sysval + =>2 + syscmd(`true') + => + sysval + =>0 + esyscmd(`false') + => + ifelse(sysval, `0', `zero', `non-zero') + =>non-zero + esyscmd(`exit 2') + => + sysval + =>2 + esyscmd(`true') + => + sysval + =>0 + + `sysval' results in 127 if there was a problem executing the +command, for example, if the system-imposed argument length is exceeded, +or if there were not enough resources to fork. It is not possible to +distinguish between failed execution and successful execution that had +an exit status of 127. + + On UNIX platforms, where it is possible to detect when command +execution is terminated by a signal, rather than a normal exit, the +result is the signal number shifted left by eight bits. + + dnl This test assumes kill is a shell builtin, and that signals are + dnl recognizable. + ifdef(`__unix__', , + `errprint(` skipping: syscmd does not have unix semantics + ')m4exit(`77')')dnl + syscmd(`kill -13 $$') + => + sysval + =>3328 + esyscmd(`kill -9 $$') + => + sysval + =>2304 + + +File: m4.info, Node: Mkstemp, Prev: Sysval, Up: Shell commands + +13.5 Making temporary files +=========================== + +Commands specified to `syscmd' or `esyscmd' might need a temporary +file, for output or for some other purpose. There is a builtin macro, +`mkstemp', for making a temporary file: + + -- Builtin: mkstemp (TEMPLATE) + -- Builtin: maketemp (TEMPLATE) + Expands to a name of a new, empty file, made from the string + TEMPLATE, which should end with the string `XXXXXX'. The six `X' + characters are then replaced with random characters matching the + regular expression `[a-zA-Z0-9._-]', in order to make the file + name unique. If fewer than six `X' characters are found at the end + of `template', the result will be longer than the template. The + created file will have access permissions as if by `chmod =rw,go=', + meaning that the current umask of the `m4' process is taken into + account, and at most only the current user can read and write the + file. + + The traditional behavior, standardized by POSIX, is that + `maketemp' merely replaces the trailing `X' with the process id, + without creating a file, and without ensuring that the resulting + string is a unique file name. In part, this means that using the + same TEMPLATE twice in the same input file will result in the same + expansion. This behavior is a security hole, as it is very easy + for another process to guess the name that will be generated, and + thus interfere with a subsequent use of `syscmd' trying to + manipulate that file name. Hence, POSIX has recommended that all + new implementations of `m4' provide the secure `mkstemp' builtin, + and that users of `m4' check for its existence. + + The macros `mkstemp' and `maketemp' are recognized only with + parameters. + + If you try this next example, you will most likely get different +output for the two file names, since the replacement characters are +randomly chosen: + + maketemp(`/tmp/fooXXXXXX') + =>/tmp/fooa07346 + ifdef(`mkstemp', `define(`maketemp', defn(`mkstemp'))', + `define(`mkstemp', defn(`maketemp'))dnl + errprint(`warning: potentially insecure maketemp implementation + ')') + => + mkstemp(`doc') + =>docQv83Uw + + Unless you use the `--traditional' command line option (or `-G', +*note Invoking m4: Limits control.), the GNU version of `maketemp' is +secure. This means that using the same template to multiple calls will +generate multiple files. However, we recommend that you use the new +`mkstemp' macro, introduced in GNU M4 1.4.8, which is secure even in +traditional mode. + + syscmd(`echo foo??????')dnl + =>foo?????? + define(`file1', maketemp(`fooXXXXXX'))dnl + ifelse(esyscmd(`echo foo??????'), `foo??????', `no file', `created') + =>created + define(`file2', maketemp(`fooXX'))dnl + define(`file3', mkstemp(`fooXXXXXX'))dnl + ifelse(len(file1), len(file2), `same length', `different') + =>same length + ifelse(file1, file2, `same', `different file') + =>different file + ifelse(file2, file3, `same', `different file') + =>different file + ifelse(file1, file3, `same', `different file') + =>different file + syscmd(`rm 'file1 file2 file3) + => + sysval + =>0 + + +File: m4.info, Node: Miscellaneous, Next: Frozen files, Prev: Shell commands, Up: Top + +14 Miscellaneous builtin macros +******************************* + +This chapter describes various builtins, that do not really belong in +any of the previous chapters. + +* Menu: + +* Errprint:: Printing error messages +* Location:: Printing current location +* M4exit:: Exiting from `m4' + + +File: m4.info, Node: Errprint, Next: Location, Up: Miscellaneous + +14.1 Printing error messages +============================ + +You can print error messages using `errprint': + + -- Builtin: errprint (MESSAGE, ...) + Prints MESSAGE and the rest of the arguments to standard error, + separated by spaces. Standard error is used, regardless of the + `--debugfile' option (*note Invoking m4: Debugging options.). + + The expansion of `errprint' is void. The macro `errprint' is + recognized only with parameters. + + errprint(`Invalid arguments to forloop + ') + error-->Invalid arguments to forloop + => + errprint(`1')errprint(`2',`3 + ') + error-->12 3 + => + + A trailing newline is _not_ printed automatically, so it should be +supplied as part of the argument, as in the example. Unfortunately, the +exact output of `errprint' is not very portable to other `m4' +implementations: POSIX requires that all arguments be printed, but some +implementations of `m4' only print the first. Furthermore, some BSD +implementations always append a newline for each `errprint' call, +regardless of whether the last argument already had one, and POSIX is +silent on whether this is acceptable. + + +File: m4.info, Node: Location, Next: M4exit, Prev: Errprint, Up: Miscellaneous + +14.2 Printing current location +============================== + +To make it possible to specify the location of an error, three utility +builtins exist: + + -- Builtin: __file__ + -- Builtin: __line__ + -- Builtin: __program__ + Expand to the quoted name of the current input file, the current + input line number in that file, and the quoted name of the current + invocation of `m4'. + + errprint(__program__:__file__:__line__: `input error + ') + error-->m4:stdin:1: input error + => + + Line numbers start at 1 for each file. If the file was found due to +the `-I' option or `M4PATH' environment variable, that is reflected in +the file name. The syncline option (`-s', *note Invoking m4: +Preprocessor features.), and the `f' and `l' flags of `debugmode' +(*note Debug Levels::), also use this notion of current file and line. +Redefining the three location macros has no effect on syncline, debug, +or warning message output. Assume this example is run in the +`m4-1.4.8/checks' directory of the GNU M4 package, using +`--include=../examples' in the command line to find the file `incl.m4' +mentioned earlier: + + define(`foo', ``$0' called at __file__:__line__') + => + foo + =>foo called at stdin:2 + include(`incl.m4') + =>Include file start + =>foo called at ../examples/incl.m4:2 + =>Include file end + => + + The location of macros invoked during the rescanning of macro +expansion text corresponds to the location in the file where the +expansion was triggered, regardless of how many newline characters the +expansion text contains. As of GNU M4 1.4.8, the location of text +wrapped with `m4wrap' (*note M4wrap::) is the point at which the +`m4wrap' was invoked. Previous versions, however, behaved as though +wrapped text came from line 0 of the file "". + + define(`echo', `$@') + => + define(`foo', `echo(__line__ + __line__)') + => + echo(__line__ + __line__) + =>4 + =>5 + m4wrap(`foo + ') + => + foo(errprint(__line__ + __line__ + )) + error-->8 + error-->9 + =>8 + =>8 + __line__ + =>11 + ^D + =>6 + =>6 + + The `__program__' macro behaves like `$0' in shell terminology. If +you invoke `m4' through an absolute path or a link with a different +spelling, rather than by relying on a `PATH' search for plain `m4', it +will affect how `__program__' expands. The intent is that you can use +it to produce error messages with the same formatting that `m4' +produces internally. It can also be used within `syscmd' (*note +Syscmd::) to pick the same version of `m4' that is currently running, +rather than whatever version of `m4' happens to be first in `PATH'. It +was first introduced in GNU M4 1.4.6. + + +File: m4.info, Node: M4exit, Prev: Location, Up: Miscellaneous + +14.3 Exiting from `m4' +====================== + +If you need to exit from `m4' before the entire input has been read, +you can use `m4exit': + + -- Builtin: m4exit ([CODE = `0']) + Causes `m4' to exit, with exit status CODE. If CODE is left out, + the exit status is zero. If CODE cannot be parsed, or is outside + the range of 0 to 255, the exit status is one. No further input + is read, and all wrapped and diverted text is discarded. + + m4wrap(`This text is lost due to `m4exit'.') + => + divert(`1') So is this. + divert + => + m4exit And this is never read. + + A common use of this is to abort processing: + + -- Composite: fatal_error (MESSAGE) + Abort processing with an error message and non-zero status. Prefix + MESSAGE with details about where the error occurred, and print the + resulting string to standard error. + + define(`fatal_error', + `errprint(__program__:__file__:__line__`: fatal error: $* + ')m4exit(`1')') + => + fatal_error(`this is a BAD one, buster') + error-->m4:stdin:4: fatal error: this is a BAD one, buster + + After this macro call, `m4' will exit with exit status 1. This macro +is only intended for error exits, since the normal exit procedures are +not followed, e.g., diverted text is not undiverted, and saved text +(*note M4wrap::) is not reread. (This macro could be made more robust +to earlier versions of `m4'. You should try to see if you can find +weaknesses and correct them; or *note Answers: Improved fatal_error.). + + Note that it is still possible for the exit status to be different +than what was requested by `m4exit'. If `m4' detects some other error, +such as a write error on standard output, the exit status will be +non-zero even if `m4exit' requested zero. + + If standard input is seekable, then the file will be positioned at +the next unread character. If it is a pipe or other non-seekable file, +then there are no guarantees how much data `m4' might have read into +buffers, and thus discarded. + + +File: m4.info, Node: Frozen files, Next: Compatibility, Prev: Miscellaneous, Up: Top + +15 Fast loading of frozen state +******************************* + +Some bigger `m4' applications may be built over a common base +containing hundreds of definitions and other costly initializations. +Usually, the common base is kept in one or more declarative files, +which files are listed on each `m4' invocation prior to the user's +input file, or else each input file uses `include'. + + Reading the common base of a big application, over and over again, +may be time consuming. GNU `m4' offers some machinery to speed up the +start of an application using lengthy common bases. + +* Menu: + +* Using frozen files:: Using frozen files +* Frozen file format:: Frozen file format + + +File: m4.info, Node: Using frozen files, Next: Frozen file format, Up: Frozen files + +15.1 Using frozen files +======================= + +Suppose a user has a library of `m4' initializations in `base.m4', +which is then used with multiple input files: + + m4 base.m4 input1.m4 + m4 base.m4 input2.m4 + m4 base.m4 input3.m4 + + Rather than spending time parsing the fixed contents of `base.m4' +every time, the user might rather execute: + + m4 -F base.m4f base.m4 + +once, and further execute, as often as needed: + + m4 -R base.m4f input1.m4 + m4 -R base.m4f input2.m4 + m4 -R base.m4f input3.m4 + +with the varying input. The first call, containing the `-F' option, +only reads and executes file `base.m4', defining various application +macros and computing other initializations. Once the input file +`base.m4' has been completely processed, GNU `m4' produces on +`base.m4f' a "frozen" file, that is, a file which contains a kind of +snapshot of the `m4' internal state. + + Later calls, containing the `-R' option, are able to reload the +internal state of `m4', from `base.m4f', _prior_ to reading any other +input files. This means instead of starting with a virgin copy of +`m4', input will be read after having effectively recovered the effect +of a prior run. In our example, the effect is the same as if file +`base.m4' has been read anew. However, this effect is achieved a lot +faster. + + Only one frozen file may be created or read in any one `m4' +invocation. It is not possible to recover two frozen files at once. +However, frozen files may be updated incrementally, through using `-R' +and `-F' options simultaneously. For example, if some care is taken, +the command: + + m4 file1.m4 file2.m4 file3.m4 file4.m4 + +could be broken down in the following sequence, accumulating the same +output: + + m4 -F file1.m4f file1.m4 + m4 -R file1.m4f -F file2.m4f file2.m4 + m4 -R file2.m4f -F file3.m4f file3.m4 + m4 -R file3.m4f file4.m4 + + Some care is necessary because not every effort has been made for +this to work in all cases. In particular, the trace attribute of +macros is not handled, nor the current setting of `changeword'. +Currently, `m4wrap' and `sysval' also have problems. Also, +interactions for some options of `m4', being used in one call and not +in the next, have not been fully analyzed yet. On the other end, you +may be confident that stacks of `pushdef' definitions are handled +correctly, as well as undefined or renamed builtins, and changed +strings for quotes or comments. And future releases of GNU M4 will +improve on the utility of frozen files. + + When an `m4' run is to be frozen, the automatic undiversion which +takes place at end of execution is inhibited. Instead, all positively +numbered diversions are saved into the frozen file. The active +diversion number is also transmitted. + + A frozen file to be reloaded need not reside in the current +directory. It is looked up the same way as an `include' file (*note +Search Path::). + + If the frozen file was generated with a newer version of `m4', and +contains directives that an older `m4' cannot parse, attempting to load +the frozen file with option `-R' will cause `m4' to exit with status 63 +to indicate version mismatch. + + +File: m4.info, Node: Frozen file format, Prev: Using frozen files, Up: Frozen files + +15.2 Frozen file format +======================= + +Frozen files are sharable across architectures. It is safe to write a +frozen file on one machine and read it on another, given that the +second machine uses the same or newer version of GNU `m4'. It is +conventional, but not required, to give a frozen file the suffix of +`.m4f'. + + These are simple (editable) text files, made up of directives, each +starting with a capital letter and ending with a newline (). +Wherever a directive is expected, the character `#' introduces a +comment line; empty lines are also ignored if they are not part of an +embedded string. In the following descriptions, each LEN refers to the +length of the corresponding strings STR in the next line of input. +Numbers are always expressed in decimal. There are no escape +characters. The directives are: + +`C LEN1 , LEN2 STR1 STR2 ' + Uses STR1 and STR2 as the begin-comment and end-comment strings. + If omitted, then `#' and are the comment delimiters. + +`D NUMBER, LEN STR ' + Selects diversion NUMBER, making it current, then copy STR in the + current diversion. NUMBER may be a negative number for a + non-existing diversion. To merely specify an active selection, + use this command with an empty STR. With 0 as the diversion + NUMBER, STR will be issued on standard output at reload time. GNU + `m4' will not produce the `D' directive with non-zero length for + diversion 0, but this can be done with manual edits. This + directive may appear more than once for the same diversion, in + which case the diversion is the concatenation of the various uses. + If omitted, then diversion 0 is current. + +`F LEN1 , LEN2 STR1 STR2 ' + Defines, through `pushdef', a definition for STR1 expanding to the + function whose builtin name is STR2. If the builtin does not + exist (for example, if the frozen file was produced by a copy of + `m4' compiled with changeword support, but the version of `m4' + reloading was compiled without it), the reload is silent, but any + subsequent use of the definition of STR1 will result in a warning. + This directive may appear more than once for the same name, and + its order, along with `T', is important. If omitted, you will + have no access to any builtins. + +`Q LEN1 , LEN2 STR1 STR2 ' + Uses STR1 and STR2 as the begin-quote and end-quote strings. If + omitted, then ``' and `'' are the quote delimiters. + +`T LEN1 , LEN2 STR1 STR2 ' + Defines, though `pushdef', a definition for STR1 expanding to the + text given by STR2. This directive may appear more than once for + the same name, and its order, along with `F', is important. + +`V NUMBER ' + Confirms the format of the file. `m4' 1.4.8 only creates and + understands frozen files where NUMBER is 1. This directive must + be the first non-comment in the file, and may not appear more than + once. + + +File: m4.info, Node: Compatibility, Next: Answers, Prev: Frozen files, Up: Top + +16 Compatibility with other versions of `m4' +******************************************** + +This chapter describes the differences between this implementation of +`m4', and the implementation found under UNIX, notably System V, +Release 3. + + There are also differences in BSD flavors of `m4'. No attempt is +made to summarize these here. + +* Menu: + +* Extensions:: Extensions in GNU M4 +* Incompatibilities:: Facilities in System V m4 not in GNU M4 +* Other Incompatibilities:: Other incompatibilities + + +File: m4.info, Node: Extensions, Next: Incompatibilities, Up: Compatibility + +16.1 Extensions in GNU `m4' +=========================== + +This version of `m4' contains a few facilities that do not exist in +System V `m4'. These extra facilities are all suppressed by using the +`-G' command line option (*note Invoking m4: Limits control.), unless +overridden by other command line options. + + * In the `$'N notation for macro arguments, N can contain several + digits, while the System V `m4' only accepts one digit. This + allows macros in GNU `m4' to take any number of arguments, and not + only nine (*note Arguments::). + + This means that `define(`foo', `$11')' is ambiguous between + implementations. To portably choose between grabbing the first + parameter and appending 1 to the expansion, or grabbing the + eleventh parameter, you can do the following: + + define(`a1', `A1') + => + dnl First argument, concatenated with 1 + define(`_1', `$1')define(`first1', `_1($@)1') + => + dnl Eleventh argument, portable + define(`_9', `$9')define(`eleventh', `_9(shift(shift($@)))') + => + dnl Eleventh argument, GNU style + define(`Eleventh', `$11') + => + first1(`a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k') + =>A1 + eleventh(`a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k') + =>k + Eleventh(`a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k') + =>k + + * The `divert' (*note Divert::) macro can manage more than 9 + diversions. GNU `m4' treats all positive numbers as valid + diversions, rather than discarding diversions greater than 9. + + * Files included with `include' and `sinclude' are sought in a user + specified search path, if they are not found in the working + directory. The search path is specified by the `-I' option and the + `M4PATH' environment variable (*note Search Path::). + + * Arguments to `undivert' can be non-numeric, in which case the named + file will be included uninterpreted in the output (*note + Undivert::). + + * Formatted output is supported through the `format' builtin, which + is modeled after the C library function `printf' (*note Format::). + + * Searches and text substitution through regular expressions are + supported by the `regexp' (*note Regexp::) and `patsubst' (*note + Patsubst::) builtins. + + * The output of shell commands can be read into `m4' with `esyscmd' + (*note Esyscmd::). + + * There is indirect access to any builtin macro with `builtin' + (*note Builtin::). + + * Macros can be called indirectly through `indir' (*note Indir::). + + * The name of the program, the current input file, and the current + input line number are accessible through the builtins + `__program__', `__file__', and `__line__' (*note Location::). + + * The format of the output from `dumpdef' and macro tracing can be + controlled with `debugmode' (*note Debug Levels::). + + * The destination of trace and debug output can be controlled with + `debugfile' (*note Debug Output::). + + * The `maketemp' (*note Mkstemp::) macro behaves like `mkstemp', + creating a new file with a unique name on every invocation, rather + than following the insecure behavior of replacing the trailing `X' + characters with the `m4' process id. + + In addition to the above extensions, GNU `m4' implements the +following command line options: `-F', `-G', `-I', `-L', `-R', `-V', +`-W', `-d', `-i', `-l', `--debugfile' and `-t'. *Note Invoking m4::, +for a description of these options. + + Also, the debugging and tracing facilities in GNU `m4' are much more +extensive than in most other versions of `m4'. + + +File: m4.info, Node: Incompatibilities, Next: Other Incompatibilities, Prev: Extensions, Up: Compatibility + +16.2 Facilities in System V `m4' not in GNU `m4' +================================================ + +The version of `m4' from System V contains a few facilities that have +not been implemented in GNU `m4' yet. Additionally, POSIX requires +some behaviors that GNU `m4' has not implemented yet. Relying on these +behaviors is non-portable, as a future release of GNU `m4' may change. + + * System V `m4' supports multiple arguments to `defn', and POSIX + requires it. This is not yet implemented in GNU `m4'. + Unfortunately, this means it is not possible to mix builtins and + other text into a single macro; a helper macro is required. + + * POSIX requires an application to exit with non-zero status if it + wrote an error message to stderr. This has not yet been + consistently implemented for the various builtins that are + required to issue an error (such as `include' (*note Include::) + when a file is unreadable, `eval' (*note Eval::) when an argument + cannot be parsed, or using `m4exit' (*note M4exit::) with a + non-numeric argument). + + * Some traditional implementations only allow reading standard input + once, but GNU `m4' correctly handles multiple instances of `-' on + the command line. + + * POSIX requires `m4wrap' (*note M4wrap::) to act in FIFO (first-in, + first-out) order, but GNU `m4' currently uses LIFO order. + Furthermore, POSIX states that only the first argument to `m4wrap' + is saved for later evaluation, bug GNU `m4' saves and processes + all arguments, with output separated by spaces. + + However, it is possible to emulate POSIX behavior by including the + file `m4-1.4.8/examples/wrapfifo.m4' from the distribution: + + undivert(`wrapfifo.m4')dnl + =>dnl Redefine m4wrap to have FIFO semantics. + =>define(`_m4wrap_level', `0')dnl + =>define(`m4wrap', + =>`ifdef(`m4wrap'_m4wrap_level, + => `define(`m4wrap'_m4wrap_level, + => defn(`m4wrap'_m4wrap_level)`$1')', + => `builtin(`m4wrap', `define(`_m4wrap_level', + => incr(_m4wrap_level))dnl + =>m4wrap'_m4wrap_level)dnl + =>define(`m4wrap'_m4wrap_level, `$1')')')dnl + include(`wrapfifo.m4') + => + m4wrap(`a`'m4wrap(`c + ', `d')')m4wrap(`b') + => + ^D + =>abc + + * POSIX requires that all builtins that require arguments, but are + called without arguments, behave as though empty strings had been + passed. For example, `a`'define`'b' would expand to `ab'. But + GNU `m4' ignores certain builtins if they have missing arguments, + giving `adefineb' for the above example. + + * Traditional implementations handle `define(`f',`1')' (*note + Define::) by undefining the entire stack of previous definitions, + and if doing `undefine(`f')' first. GNU `m4' replaces just the top + definition on the stack, as if doing `popdef(`f')' followed by + `pushdef(`f',`1')'. + + * POSIX requires `syscmd' (*note Syscmd::) to evaluate command + output for macro expansion, but this appears to be a mistake in + POSIX since traditional implementations did not do this. GNU `m4' + follows traditional behavior in `syscmd', and provides the + extension `esyscmd' that provides the POSIX semantics. + + * At one point, POSIX required `changequote(ARG)' (*note + Changequote::) to use newline as the close quote, but this was a + bug, and the next version of POSIX is anticipated to state that + using empty strings or just one argument is unspecified. + Meanwhile, the GNU `m4' behavior of treating an empty end-quote + delimiter as `'' is not portable, as Solaris treats it as + repeating the start-quote delimiter, and BSD treats it as leaving + the previous end-quote delimiter unchanged. For predictable + results, never call changequote with just one argument, or with + empty strings for arguments. + + * At one point, POSIX required `changecom(ARG,)' (*note Changecom::) + to make it impossible to end a comment, but this is a bug, and the + next version of POSIX is anticipated to state that using empty + strings is unspecified. Meanwhile, the GNU `m4' behavior of + treating an empty end-comment delimiter as newline is not + portable, as BSD treats it as leaving the previous end-comment + delimiter unchanged. It is also impossible in BSD implementations + to disable comments, even though that is required by POSIX. For + predictable results, never call changecom with empty strings for + arguments. + + * Most implementations of `m4' give macros a higher precedence than + comments when parsing, meaning that if the start delimiter given to + `changecom' (*note Changecom::) starts with a macro name, comments + are effectively disabled. POSIX does not specify what the + precedence is, so the GNU `m4' parser recognizes comments, then + macros, then quoted strings. + + * Traditional implementations allow argument collection, but not + string and comment processing, to span file boundaries. Thus, if + `a.m4' contains `len(', and `b.m4' contains `abc)', `m4 a.m4 b.m4' + outputs `3' with traditional `m4', but gives an error message that + the end of file was encountered inside a macro with GNU `m4'. On + the other hand, traditional implementations do end of file + processing for files included with `include' or `sinclude' (*note + Include::), while GNU `m4' seamlessly integrates the content of + those files. Thus `include(`a.m4')include(`b.m4')' will output + `3' instead of giving an error. + + * Traditional `m4' treats `traceon' (*note Trace::) without + arguments as a global variable, independent of named macro tracing. + Also, once a macro is undefined, named tracing of that macro is + lost. On the other hand, when GNU `m4' encounters `traceon' + without arguments, it turns tracing on for all existing + definitions at the time, but does not trace future definitions; + `traceoff' without arguments turns tracing off for all definitions + regardless of whether they were also traced by name; and tracing + by name, such as with `-tfoo' at the command line or + `traceon(`foo')' in the input, is an attribute that is preserved + even if the macro is currently undefined. + + * POSIX requires `eval' (*note Eval::) to treat all operators with + the same precedence as C. However, GNU `m4' currently follows the + traditional precedence of other `m4' implementations, where + bitwise and logical negation (`~' and `!') have lower precedence + than equality operators, rather than equal precedence with other + unary operators. Use explicit parentheses to ensure proper + precedence. As extensions to POSIX, GNU `m4' treats the shift + operators `<<' and `>>' as well-defined on signed integers (even + though they are not in C), and adds the exponentiation operator + `**'. + + * POSIX requires `translit' (*note Translit::) to treat each + character of the second and third arguments literally, but GNU + `m4' treats `-' as a range operator. + + * POSIX requires `m4' to honor the locale environment variables of + `LANG', `LC_ALL', `LC_CTYPE', `LC_MESSAGES', and `NLSPATH', but + this has not yet been implemented in GNU `m4'. + + * POSIX states that only unquoted leading newlines and blanks (that + is, space and tab) are ignored when collecting macro arguments. + However, this appears to be a bug in POSIX, since most traditional + implementations also ignore all whitespace (formfeed, carriage + return, and vertical tab). GNU `m4' follows tradition and ignores + all leading unquoted whitespace. + + +File: m4.info, Node: Other Incompatibilities, Prev: Incompatibilities, Up: Compatibility + +16.3 Other incompatibilities +============================ + +There are a few other incompatibilities between this implementation of +`m4', and the System V version. + + * GNU `m4' implements sync lines differently from System V `m4', + when text is being diverted. GNU `m4' outputs the sync lines when + the text is being diverted, and System V `m4' when the diverted + text is being brought back. + + The problem is which lines and file names should be attached to + text that is being, or has been, diverted. System V `m4' regards + all the diverted text as being generated by the source line + containing the `undivert' call, whereas GNU `m4' regards the + diverted text as being generated at the time it is diverted. + + The sync line option is used mostly when using `m4' as a front end + to a compiler. If a diverted line causes a compiler error, the + error messages should most probably refer to the place where the + diversion were made, and not where it was inserted again. + + * GNU `m4' makes no attempt at prohibiting self-referential + definitions like: + + define(`x', `x') + => + define(`x', `x ') + => + + There is nothing inherently wrong with defining `x' to return `x'. + The wrong thing is to expand `x' unquoted. In `m4', one might + use macros to hold strings, as we do for variables in other + programming languages, further checking them with: + + ifelse(defn(`HOLDER'), `VALUE', ...) + + In cases like this one, an interdiction for a macro to hold its own + name would be a useless limitation. Of course, this leaves more + rope for the GNU `m4' user to hang himself! Rescanning hangs may + be avoided through careful programming, a little like for endless + loops in traditional programming languages. + + +File: m4.info, Node: Answers, Next: Copying This Manual, Prev: Compatibility, Up: Top + +17 Correct version of some examples +*********************************** + +Some of the examples in this manuals are buggy or not very robust, for +demonstration purposes. Improved versions of these composite macros are +presented here. + +* Menu: + +* Improved exch:: Solution for `exch' +* Improved forloop:: Solution for `forloop' +* Improved foreach:: Solution for `foreach' +* Improved cleardivert:: Solution for `cleardivert' +* Improved fatal_error:: Solution for `fatal_error' + + +File: m4.info, Node: Improved exch, Next: Improved forloop, Up: Answers + +17.1 Solution for `exch' +======================== + +The `exch' macro (*note Arguments::) as presented requires clients to +double quote their arguments. A nicer definition, which lets clients +follow the rule of thumb of one level of quoting per level of +parentheses, involves adding quotes in the definition of `exch', as +follows: + + define(`exch', ``$2', `$1'') + => + define(exch(`expansion text', `macro')) + => + macro + =>expansion text + + +File: m4.info, Node: Improved forloop, Next: Improved foreach, Prev: Improved exch, Up: Answers + +17.2 Solution for `forloop' +=========================== + +The `forloop' macro (*note Forloop::) as presented earlier can go into +an infinite loop if given an iterator that is not parsed as a macro +name. It does not do any sanity checking on its numeric bounds, and +only permits decimal numbers for bounds. Here is an improved version, +shipped as `m4-1.4.8/examples/forloop2.m4'; this version also optimizes +based on the fact that the starting bound does not need to be passed to +the helper `_forloop'. + + undivert(`forloop2.m4')dnl + =>divert(`-1') + =># forloop(var, from, to, stmt) - improved version: + =># works even if VAR is not a strict macro name + =># performs sanity check that FROM is larger than TO + =># allows complex numerical expressions in TO and FROM + =>define(`forloop', `ifelse(eval(`($3) >= ($2)'), `1', + => `pushdef(`$1', eval(`$2'))_forloop(`$1', + => eval(`$3'), `$4')popdef(`$1')')') + =>define(`_forloop', + => `$3`'ifelse(indir(`$1'), `$2', `', + => `define(`$1', incr(indir(`$1')))$0($@)')') + =>divert`'dnl + include(`forloop2.m4') + => + forloop(`i', `2', `1', `no iteration occurs') + => + forloop(`', `1', `2', ` odd iterator name') + => odd iterator name odd iterator name + forloop(`i', `5 + 5', `0xc', ` 0x`'eval(i, `16')') + => 0xa 0xb 0xc + forloop(`i', `a', `b', `non-numeric bounds') + error-->m4:stdin:6: bad expression in eval (bad input): (b) >= (a) + => + + Of course, it is possible to make even more improvements, such as +adding an optional step argument, or allowing iteration through +descending sequences. GNU Autoconf provides some of these additional +bells and whistles in its `m4_for' macro. + + +File: m4.info, Node: Improved foreach, Next: Improved cleardivert, Prev: Improved forloop, Up: Answers + +17.3 Solution for `foreach' +=========================== + +The `foreach' and `foreachq' macros (*note Foreach::) as presented +earlier each have flaws. First, we will examine and fix the quadratic +behavior of `foreachq': + + include(`foreachq.m4') + => + traceon(`shift')debugmode(`aq') + => + foreachq(`x', ``1', `2', `3', `4'', `x + ')dnl + =>1 + error-->m4trace: -3- shift(`1', `2', `3', `4') + error-->m4trace: -2- shift(`1', `2', `3', `4') + =>2 + error-->m4trace: -4- shift(`1', `2', `3', `4') + error-->m4trace: -3- shift(`2', `3', `4') + error-->m4trace: -3- shift(`1', `2', `3', `4') + error-->m4trace: -2- shift(`2', `3', `4') + =>3 + error-->m4trace: -5- shift(`1', `2', `3', `4') + error-->m4trace: -4- shift(`2', `3', `4') + error-->m4trace: -3- shift(`3', `4') + error-->m4trace: -4- shift(`1', `2', `3', `4') + error-->m4trace: -3- shift(`2', `3', `4') + error-->m4trace: -2- shift(`3', `4') + =>4 + error-->m4trace: -6- shift(`1', `2', `3', `4') + error-->m4trace: -5- shift(`2', `3', `4') + error-->m4trace: -4- shift(`3', `4') + error-->m4trace: -3- shift(`4') + + Each successive iteration was adding more quoted `shift' +invocations, and the entire list contents were passing through every +iteration. In general, when recursing, it is a good idea to make the +recursion use fewer arguments, rather than adding additional quoted +uses of `shift'. By doing so, `m4' uses less memory, invokes fewer +macros, is less likely to run into machine limits, and most +importantly, performs faster. The fixed version of `foreachq' can be +found in `m4-1.4.8/examples/foreachq2.m4': + + include(`foreachq2.m4') + => + undivert(`foreachq2.m4')dnl + =>include(`quote.m4')dnl + =>divert(`-1') + =># foreachq(x, `item_1, item_2, ..., item_n', stmt) + =># quoted list, improved version + =>define(`foreachq', `pushdef(`$1')_foreachq($@)popdef(`$1')') + =>define(`_arg1q', ``$1'') + =>define(`_rest', `ifelse(`$#', `1', `', `dquote(shift($@))')') + =>define(`_foreachq', `ifelse(`$2', `', `', + => `define(`$1', _arg1q($2))$3`'$0(`$1', _rest($2), `$3')')') + =>divert`'dnl + traceon(`shift')debugmode(`aq') + => + foreachq(`x', ``1', `2', `3', `4'', `x + ')dnl + =>1 + error-->m4trace: -3- shift(`1', `2', `3', `4') + =>2 + error-->m4trace: -3- shift(`2', `3', `4') + =>3 + error-->m4trace: -3- shift(`3', `4') + =>4 + + Note that the fixed version calls unquoted helper macros in +`_foreachq' to trim elements immediately; those helper macros in turn +must re-supply the layer of quotes lost in the macro invocation. +Contrast the use of `_arg1q', which quotes the first list element, with +`_arg1' of the earlier implementation that returned the first list +element directly. + + For a different approach, the improved version of `foreach', +available in `m4-1.4.8/examples/foreach2.m4', simply overquotes the +arguments to `_foreach' to begin with, using `dquote_elt'. Then +`_foreach' can just use `_arg1' to remove the extra layer of quoting +that was added up front: + + include(`foreach2.m4') + => + undivert(`foreach2.m4')dnl + =>include(`quote.m4')dnl + =>divert(`-1') + =># foreach(x, (item_1, item_2, ..., item_n), stmt) + =># parenthesized list, improved version + =>define(`foreach', `pushdef(`$1')_foreach(`$1', + => (dquote(dquote_elt$2)), `$3')popdef(`$1')') + =>define(`_arg1', `$1') + =>define(`_foreach', `ifelse(`$2', `(`')', `', + => `define(`$1', _arg1$2)$3`'$0(`$1', (dquote(shift$2)), `$3')')') + =>divert`'dnl + traceon(`shift')debugmode(`aq') + => + foreach(`x', `(`1', `2', `3', `4')', `x + ')dnl + error-->m4trace: -4- shift(`1', `2', `3', `4') + error-->m4trace: -4- shift(`2', `3', `4') + error-->m4trace: -4- shift(`3', `4') + =>1 + error-->m4trace: -3- shift(``1'', ``2'', ``3'', ``4'') + =>2 + error-->m4trace: -3- shift(``2'', ``3'', ``4'') + =>3 + error-->m4trace: -3- shift(``3'', ``4'') + =>4 + error-->m4trace: -3- shift(``4'') + + In summary, recursion over list elements is trickier than it +appeared at first glance, but provides a powerful idiom within `m4' +processing. As a final demonstration, both list styles are now able to +handle several scenarios that would wreak havoc on the original +implementations. This points out one other difference between the two +list styles. `foreach' evaluates unquoted list elements only once, in +preparation for calling `_foreach'. But `foreachq' evaluates unquoted +list elements twice while visiting the first list element, once in +`_arg1q' and once in `_rest'. When deciding which list style to use, +one must take into account whether repeating the side effects of +unquoted list elements will have any detrimental effects. + + include(`foreach2.m4') + => + include(`foreachq2.m4') + => + dnl 0-element list: + foreach(`x', `', `') / foreachq(`x', `', `') + => / + dnl 1-element list of empty element + foreach(`x', `()', `') / foreachq(`x', ``'', `') + =><> / <> + dnl 2-element list of empty elements + foreach(`x', `(`',`')', `') / foreachq(`x', ``',`'', `') + =><><> / <><> + dnl 1-element list of a comma + foreach(`x', `(`,')', `') / foreachq(`x', ``,'', `') + =><,> / <,> + dnl 2-element list of unbalanced parentheses + foreach(`x', `(`(', `)')', `') / foreachq(`x', ``(', `)'', `') + =><(><)> / <(><)> + define(`active', `ACT, IVE') + => + traceon(`active') + => + dnl list of unquoted macros; expansion occurs before recursion + foreach(`x', `(active, active)', ` + ')dnl + error-->m4trace: -4- active -> `ACT, IVE' + error-->m4trace: -4- active -> `ACT, IVE' + => + => + => + => + foreachq(`x', `active, active', ` + ')dnl + error-->m4trace: -3- active -> `ACT, IVE' + error-->m4trace: -3- active -> `ACT, IVE' + => + error-->m4trace: -3- active -> `ACT, IVE' + error-->m4trace: -3- active -> `ACT, IVE' + => + => + => + dnl list of quoted macros; expansion occurs during recursion + foreach(`x', `(`active', `active')', ` + ')dnl + error-->m4trace: -1- active -> `ACT, IVE' + => + error-->m4trace: -1- active -> `ACT, IVE' + => + foreachq(`x', ``active', `active'', ` + ')dnl + error-->m4trace: -1- active -> `ACT, IVE' + => + error-->m4trace: -1- active -> `ACT, IVE' + => + dnl list of double-quoted macro names; no expansion + foreach(`x', `(``active'', ``active'')', ` + ')dnl + => + => + foreachq(`x', ```active'', ``active''', ` + ')dnl + => + => + + +File: m4.info, Node: Improved cleardivert, Next: Improved fatal_error, Prev: Improved foreach, Up: Answers + +17.4 Solution for `cleardivert' +=============================== + +The `cleardivert' macro (*note Cleardivert::) cannot, as it stands, be +called without arguments to clear all pending diversions. That is +because using undivert with an empty string for an argument is different +than using it with no arguments at all. Compare the earlier definition +with one that takes the number of arguments into account: + + define(`cleardivert', + `pushdef(`_n', divnum)divert(`-1')undivert($@)divert(_n)popdef(`_n')') + => + divert(`1')one + divert + => + cleardivert + => + undivert + =>one + => + define(`cleardivert', + `pushdef(`_num', divnum)divert(`-1')ifelse(`$#', `0', + `undivert`'', `undivert($@)')divert(_num)popdef(`_num')') + => + divert(`2')two + divert + => + cleardivert + => + undivert + => + + +File: m4.info, Node: Improved fatal_error, Prev: Improved cleardivert, Up: Answers + +17.5 Solution for `fatal_error' +=============================== + +The `fatal_error' macro (*note M4exit::) is not robust to versions of +GNU M4 earlier than 1.4.8, where invoking `__file__' (*note Location::) +inside `m4wrap' would result in an empty string, and `__line__' +resulted in `0' even though all files start at line 1. Furthermore, +versions earlier than 1.4.6 did not support the `__program__' macro. +If you want `fatal_error' to work across the entire 1.4.x release +series, a better implementation would be: + + define(`fatal_error', + `errprint(ifdef(`__program__', `__program__', ``m4'')'dnl + `:ifelse(__line__, `0', `', + `__file__:__line__:')` fatal error: $* + ')m4exit(`1')') + => + m4wrap(`divnum(`demo of internal message') + fatal_error(`inside wrapped text')') + => + ^D + error-->m4:stdin:6: Warning: excess arguments to builtin `divnum' ignored + =>0 + error-->m4:stdin:6: fatal error: inside wrapped text + + +File: m4.info, Node: Copying This Manual, Next: Indices, Prev: Answers, Up: Top + +Appendix A How to make copies of this manual +******************************************** + +* Menu: + +* GNU Free Documentation License:: License for copying this manual + + +File: m4.info, Node: GNU Free Documentation License, Up: Copying This Manual + +A.1 GNU Free Documentation License +================================== + + Version 1.2, November 2002 + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 0. PREAMBLE + + The purpose of this License is to make a manual, textbook, or other + functional and useful document "free" in the sense of freedom: to + assure everyone the effective freedom to copy and redistribute it, + with or without modifying it, either commercially or + noncommercially. Secondarily, this License preserves for the + author and publisher a way to get credit for their work, while not + being considered responsible for modifications made by others. + + This License is a kind of "copyleft", which means that derivative + works of the document must themselves be free in the same sense. + It complements the GNU General Public License, which is a copyleft + license designed for free software. + + We have designed this License in order to use it for manuals for + free software, because free software needs free documentation: a + free program should come with manuals providing the same freedoms + that the software does. But this License is not limited to + software manuals; it can be used for any textual work, regardless + of subject matter or whether it is published as a printed book. + We recommend this License principally for works whose purpose is + instruction or reference. + + 1. APPLICABILITY AND DEFINITIONS + + This License applies to any manual or other work, in any medium, + that contains a notice placed by the copyright holder saying it + can be distributed under the terms of this License. Such a notice + grants a world-wide, royalty-free license, unlimited in duration, + to use that work under the conditions stated herein. The + "Document", below, refers to any such manual or work. Any member + of the public is a licensee, and is addressed as "you". You + accept the license if you copy, modify or distribute the work in a + way requiring permission under copyright law. + + A "Modified Version" of the Document means any work containing the + Document or a portion of it, either copied verbatim, or with + modifications and/or translated into another language. + + A "Secondary Section" is a named appendix or a front-matter section + of the Document that deals exclusively with the relationship of the + publishers or authors of the Document to the Document's overall + subject (or to related matters) and contains nothing that could + fall directly within that overall subject. (Thus, if the Document + is in part a textbook of mathematics, a Secondary Section may not + explain any mathematics.) The relationship could be a matter of + historical connection with the subject or with related matters, or + of legal, commercial, philosophical, ethical or political position + regarding them. + + The "Invariant Sections" are certain Secondary Sections whose + titles are designated, as being those of Invariant Sections, in + the notice that says that the Document is released under this + License. If a section does not fit the above definition of + Secondary then it is not allowed to be designated as Invariant. + The Document may contain zero Invariant Sections. If the Document + does not identify any Invariant Sections then there are none. + + The "Cover Texts" are certain short passages of text that are + listed, as Front-Cover Texts or Back-Cover Texts, in the notice + that says that the Document is released under this License. A + Front-Cover Text may be at most 5 words, and a Back-Cover Text may + be at most 25 words. + + A "Transparent" copy of the Document means a machine-readable copy, + represented in a format whose specification is available to the + general public, that is suitable for revising the document + straightforwardly with generic text editors or (for images + composed of pixels) generic paint programs or (for drawings) some + widely available drawing editor, and that is suitable for input to + text formatters or for automatic translation to a variety of + formats suitable for input to text formatters. A copy made in an + otherwise Transparent file format whose markup, or absence of + markup, has been arranged to thwart or discourage subsequent + modification by readers is not Transparent. An image format is + not Transparent if used for any substantial amount of text. A + copy that is not "Transparent" is called "Opaque". + + Examples of suitable formats for Transparent copies include plain + ASCII without markup, Texinfo input format, LaTeX input format, + SGML or XML using a publicly available DTD, and + standard-conforming simple HTML, PostScript or PDF designed for + human modification. Examples of transparent image formats include + PNG, XCF and JPG. Opaque formats include proprietary formats that + can be read and edited only by proprietary word processors, SGML or + XML for which the DTD and/or processing tools are not generally + available, and the machine-generated HTML, PostScript or PDF + produced by some word processors for output purposes only. + + The "Title Page" means, for a printed book, the title page itself, + plus such following pages as are needed to hold, legibly, the + material this License requires to appear in the title page. For + works in formats which do not have any title page as such, "Title + Page" means the text near the most prominent appearance of the + work's title, preceding the beginning of the body of the text. + + A section "Entitled XYZ" means a named subunit of the Document + whose title either is precisely XYZ or contains XYZ in parentheses + following text that translates XYZ in another language. (Here XYZ + stands for a specific section name mentioned below, such as + "Acknowledgements", "Dedications", "Endorsements", or "History".) + To "Preserve the Title" of such a section when you modify the + Document means that it remains a section "Entitled XYZ" according + to this definition. + + The Document may include Warranty Disclaimers next to the notice + which states that this License applies to the Document. These + Warranty Disclaimers are considered to be included by reference in + this License, but only as regards disclaiming warranties: any other + implication that these Warranty Disclaimers may have is void and + has no effect on the meaning of this License. + + 2. VERBATIM COPYING + + You may copy and distribute the Document in any medium, either + commercially or noncommercially, provided that this License, the + copyright notices, and the license notice saying this License + applies to the Document are reproduced in all copies, and that you + add no other conditions whatsoever to those of this License. You + may not use technical measures to obstruct or control the reading + or further copying of the copies you make or distribute. However, + you may accept compensation in exchange for copies. If you + distribute a large enough number of copies you must also follow + the conditions in section 3. + + You may also lend copies, under the same conditions stated above, + and you may publicly display copies. + + 3. COPYING IN QUANTITY + + If you publish printed copies (or copies in media that commonly + have printed covers) of the Document, numbering more than 100, and + the Document's license notice requires Cover Texts, you must + enclose the copies in covers that carry, clearly and legibly, all + these Cover Texts: Front-Cover Texts on the front cover, and + Back-Cover Texts on the back cover. Both covers must also clearly + and legibly identify you as the publisher of these copies. The + front cover must present the full title with all words of the + title equally prominent and visible. You may add other material + on the covers in addition. Copying with changes limited to the + covers, as long as they preserve the title of the Document and + satisfy these conditions, can be treated as verbatim copying in + other respects. + + If the required texts for either cover are too voluminous to fit + legibly, you should put the first ones listed (as many as fit + reasonably) on the actual cover, and continue the rest onto + adjacent pages. + + If you publish or distribute Opaque copies of the Document + numbering more than 100, you must either include a + machine-readable Transparent copy along with each Opaque copy, or + state in or with each Opaque copy a computer-network location from + which the general network-using public has access to download + using public-standard network protocols a complete Transparent + copy of the Document, free of added material. If you use the + latter option, you must take reasonably prudent steps, when you + begin distribution of Opaque copies in quantity, to ensure that + this Transparent copy will remain thus accessible at the stated + location until at least one year after the last time you + distribute an Opaque copy (directly or through your agents or + retailers) of that edition to the public. + + It is requested, but not required, that you contact the authors of + the Document well before redistributing any large number of + copies, to give them a chance to provide you with an updated + version of the Document. + + 4. MODIFICATIONS + + You may copy and distribute a Modified Version of the Document + under the conditions of sections 2 and 3 above, provided that you + release the Modified Version under precisely this License, with + the Modified Version filling the role of the Document, thus + licensing distribution and modification of the Modified Version to + whoever possesses a copy of it. In addition, you must do these + things in the Modified Version: + + A. Use in the Title Page (and on the covers, if any) a title + distinct from that of the Document, and from those of + previous versions (which should, if there were any, be listed + in the History section of the Document). You may use the + same title as a previous version if the original publisher of + that version gives permission. + + B. List on the Title Page, as authors, one or more persons or + entities responsible for authorship of the modifications in + the Modified Version, together with at least five of the + principal authors of the Document (all of its principal + authors, if it has fewer than five), unless they release you + from this requirement. + + C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. + + D. Preserve all the copyright notices of the Document. + + E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. + + F. Include, immediately after the copyright notices, a license + notice giving the public permission to use the Modified + Version under the terms of this License, in the form shown in + the Addendum below. + + G. Preserve in that license notice the full lists of Invariant + Sections and required Cover Texts given in the Document's + license notice. + + H. Include an unaltered copy of this License. + + I. Preserve the section Entitled "History", Preserve its Title, + and add to it an item stating at least the title, year, new + authors, and publisher of the Modified Version as given on + the Title Page. If there is no section Entitled "History" in + the Document, create one stating the title, year, authors, + and publisher of the Document as given on its Title Page, + then add an item describing the Modified Version as stated in + the previous sentence. + + J. Preserve the network location, if any, given in the Document + for public access to a Transparent copy of the Document, and + likewise the network locations given in the Document for + previous versions it was based on. These may be placed in + the "History" section. You may omit a network location for a + work that was published at least four years before the + Document itself, or if the original publisher of the version + it refers to gives permission. + + K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the + section all the substance and tone of each of the contributor + acknowledgements and/or dedications given therein. + + L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section + titles. + + M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. + + N. Do not retitle any existing section to be Entitled + "Endorsements" or to conflict in title with any Invariant + Section. + + O. Preserve any Warranty Disclaimers. + + If the Modified Version includes new front-matter sections or + appendices that qualify as Secondary Sections and contain no + material copied from the Document, you may at your option + designate some or all of these sections as invariant. To do this, + add their titles to the list of Invariant Sections in the Modified + Version's license notice. These titles must be distinct from any + other section titles. + + You may add a section Entitled "Endorsements", provided it contains + nothing but endorsements of your Modified Version by various + parties--for example, statements of peer review or that the text + has been approved by an organization as the authoritative + definition of a standard. + + You may add a passage of up to five words as a Front-Cover Text, + and a passage of up to 25 words as a Back-Cover Text, to the end + of the list of Cover Texts in the Modified Version. Only one + passage of Front-Cover Text and one of Back-Cover Text may be + added by (or through arrangements made by) any one entity. If the + Document already includes a cover text for the same cover, + previously added by you or by arrangement made by the same entity + you are acting on behalf of, you may not add another; but you may + replace the old one, on explicit permission from the previous + publisher that added the old one. + + The author(s) and publisher(s) of the Document do not by this + License give permission to use their names for publicity for or to + assert or imply endorsement of any Modified Version. + + 5. COMBINING DOCUMENTS + + You may combine the Document with other documents released under + this License, under the terms defined in section 4 above for + modified versions, provided that you include in the combination + all of the Invariant Sections of all of the original documents, + unmodified, and list them all as Invariant Sections of your + combined work in its license notice, and that you preserve all + their Warranty Disclaimers. + + The combined work need only contain one copy of this License, and + multiple identical Invariant Sections may be replaced with a single + copy. If there are multiple Invariant Sections with the same name + but different contents, make the title of each such section unique + by adding at the end of it, in parentheses, the name of the + original author or publisher of that section if known, or else a + unique number. Make the same adjustment to the section titles in + the list of Invariant Sections in the license notice of the + combined work. + + In the combination, you must combine any sections Entitled + "History" in the various original documents, forming one section + Entitled "History"; likewise combine any sections Entitled + "Acknowledgements", and any sections Entitled "Dedications". You + must delete all sections Entitled "Endorsements." + + 6. COLLECTIONS OF DOCUMENTS + + You may make a collection consisting of the Document and other + documents released under this License, and replace the individual + copies of this License in the various documents with a single copy + that is included in the collection, provided that you follow the + rules of this License for verbatim copying of each of the + documents in all other respects. + + You may extract a single document from such a collection, and + distribute it individually under this License, provided you insert + a copy of this License into the extracted document, and follow + this License in all other respects regarding verbatim copying of + that document. + + 7. AGGREGATION WITH INDEPENDENT WORKS + + A compilation of the Document or its derivatives with other + separate and independent documents or works, in or on a volume of + a storage or distribution medium, is called an "aggregate" if the + copyright resulting from the compilation is not used to limit the + legal rights of the compilation's users beyond what the individual + works permit. When the Document is included in an aggregate, this + License does not apply to the other works in the aggregate which + are not themselves derivative works of the Document. + + If the Cover Text requirement of section 3 is applicable to these + copies of the Document, then if the Document is less than one half + of the entire aggregate, the Document's Cover Texts may be placed + on covers that bracket the Document within the aggregate, or the + electronic equivalent of covers if the Document is in electronic + form. Otherwise they must appear on printed covers that bracket + the whole aggregate. + + 8. TRANSLATION + + Translation is considered a kind of modification, so you may + distribute translations of the Document under the terms of section + 4. Replacing Invariant Sections with translations requires special + permission from their copyright holders, but you may include + translations of some or all Invariant Sections in addition to the + original versions of these Invariant Sections. You may include a + translation of this License, and all the license notices in the + Document, and any Warranty Disclaimers, provided that you also + include the original English version of this License and the + original versions of those notices and disclaimers. In case of a + disagreement between the translation and the original version of + this License or a notice or disclaimer, the original version will + prevail. + + If a section in the Document is Entitled "Acknowledgements", + "Dedications", or "History", the requirement (section 4) to + Preserve its Title (section 1) will typically require changing the + actual title. + + 9. TERMINATION + + You may not copy, modify, sublicense, or distribute the Document + except as expressly provided for under this License. Any other + attempt to copy, modify, sublicense or distribute the Document 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. + + 10. FUTURE REVISIONS OF THIS LICENSE + + The Free Software Foundation may publish new, revised versions of + the GNU Free Documentation 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. See + `http://www.gnu.org/copyleft/'. + + Each version of the License is given a distinguishing version + number. If the Document specifies that a particular numbered + version of this License "or any later version" applies to it, you + have the option of following the terms and conditions either of + that specified version or of any later version that has been + published (not as a draft) by the Free Software Foundation. If + the Document does not specify a version number of this License, + you may choose any version ever published (not as a draft) by the + Free Software Foundation. + +ADDENDUM: How to use this License for your documents +==================================================== + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and license +notices just after the title page: + + Copyright (C) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. + + If you have Invariant Sections, Front-Cover Texts and Back-Cover +Texts, replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with + the Front-Cover Texts being LIST, and with the Back-Cover Texts + being LIST. + + If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + + If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, to +permit their use in free software. + + +File: m4.info, Node: Indices, Prev: Copying This Manual, Up: Top + +Appendix B Indices of concepts and macros +***************************************** + +* Menu: + +* Concept index:: Index for many concepts +* Macro index:: Index for all `m4' macros + + +File: m4.info, Node: Concept index, Next: Macro index, Up: Indices + +B.1 Index for many concepts +=========================== + +[index] +* Menu: + +* arguments to macros: Macro Arguments. (line 6) +* Arguments to macros: Arguments. (line 6) +* arguments to macros, special: Pseudo Arguments. (line 6) +* arguments, quoted macro: Quoting Arguments. (line 6) +* arithmetic: Arithmetic. (line 6) +* arrays: Define. (line 52) +* builtins, indirect call of: Builtin. (line 6) +* call of builtins, indirect: Builtin. (line 6) +* call of macros, indirect: Indir. (line 6) +* changing comment delimiters: Changecom. (line 6) +* changing the quote delimiters: Changequote. (line 6) +* characters, translating: Translit. (line 6) +* command line, file names on the: Command line files. (line 6) +* command line, macro definitions on the: Preprocessor features. + (line 6) +* command line, options: Invoking m4. (line 10) +* commands, exit status from shell: Sysval. (line 6) +* commands, running shell: Shell commands. (line 6) +* commands, running UNIX: Shell commands. (line 6) +* comment delimiters, changing: Changecom. (line 6) +* comments: Comments. (line 6) +* comments, copied to output: Changecom. (line 29) +* comparing strings: Ifelse. (line 6) +* compatibility: Compatibility. (line 6) +* conditionals: Ifdef. (line 6) +* controlling debugging output: Debug Levels. (line 6) +* counting loops: Forloop. (line 6) +* debugging output, controlling: Debug Levels. (line 6) +* debugging output, saving: Debug Output. (line 6) +* decrement operator: Incr. (line 6) +* defining new macros: Definitions. (line 6) +* definitions, displaying macro: Dumpdef. (line 6) +* deleting macros: Undefine. (line 6) +* deleting whitespace in input: Dnl. (line 6) +* discarding diverted text: Cleardivert. (line 6) +* displaying macro definitions: Dumpdef. (line 6) +* diversion numbers: Divnum. (line 6) +* diverted text, discarding: Cleardivert. (line 6) +* diverting output to files: Divert. (line 6) +* dumping into frozen file: Using frozen files. (line 6) +* error messages, printing: Errprint. (line 6) +* evaluation, of integer expressions: Eval. (line 6) +* executing shell commands: Shell commands. (line 6) +* executing UNIX commands: Shell commands. (line 6) +* exit status from shell commands: Sysval. (line 6) +* exiting from m4: M4exit. (line 6) +* expansion of macros: Macro expansion. (line 6) +* expansion, tracing macro: Trace. (line 6) +* expressions, evaluation of integer: Eval. (line 6) +* extracting substrings: Substr. (line 6) +* fast loading of frozen files: Using frozen files. (line 6) +* FDL, GNU Free Documentation License: GNU Free Documentation License. + (line 6) +* file format, frozen file: Frozen file format. (line 6) +* file inclusion <1>: Undivert. (line 81) +* file inclusion: File Inclusion. (line 6) +* file names, on the command line: Command line files. (line 6) +* files, diverting output to: Divert. (line 6) +* files, names of temporary: Mkstemp. (line 6) +* for each loops: Foreach. (line 6) +* for loops: Forloop. (line 6) +* formatted output: Format. (line 6) +* frozen file format: Frozen file format. (line 6) +* frozen files for fast loading: Using frozen files. (line 6) +* GNU extensions <1>: Indir. (line 6) +* GNU extensions <2>: Patsubst. (line 6) +* GNU extensions <3>: Extensions. (line 6) +* GNU extensions <4>: Regexp. (line 6) +* GNU extensions <5>: Using frozen files. (line 6) +* GNU extensions <6>: Format. (line 6) +* GNU extensions <7>: Undivert. (line 81) +* GNU extensions <8>: Divert. (line 54) +* GNU extensions <9>: Search Path. (line 6) +* GNU extensions <10>: Define. (line 41) +* GNU extensions <11>: Arguments. (line 32) +* GNU extensions <12>: Esyscmd. (line 6) +* GNU extensions <13>: Debug Output. (line 6) +* GNU extensions <14>: Mkstemp. (line 52) +* GNU extensions <15>: Builtin. (line 6) +* GNU extensions: Debug Levels. (line 59) +* included files, search path for: Search Path. (line 6) +* inclusion, of files <1>: Undivert. (line 81) +* inclusion, of files: File Inclusion. (line 6) +* increment operator: Incr. (line 6) +* indirect call of builtins: Builtin. (line 6) +* indirect call of macros: Indir. (line 6) +* initialization, frozen states: Using frozen files. (line 6) +* input tokens: Syntax. (line 6) +* input, saving: M4wrap. (line 6) +* integer arithmetic: Arithmetic. (line 6) +* integer expression evaluation: Eval. (line 6) +* iterating over lists: Foreach. (line 6) +* length of strings: Len. (line 6) +* lexical structure of words: Changeword. (line 6) +* License: Copying This Manual. (line 6) +* local variables: Pushdef. (line 75) +* loops: Shift. (line 10) +* loops, counting: Forloop. (line 6) +* loops, list iteration: Foreach. (line 6) +* macro definitions, on the command line: Preprocessor features. + (line 6) +* macro expansion, tracing: Trace. (line 6) +* macro invocation: Invocation. (line 6) +* macros, arguments to <1>: Macro Arguments. (line 6) +* macros, arguments to: Arguments. (line 6) +* macros, displaying definitions: Dumpdef. (line 6) +* macros, expansion of: Macro expansion. (line 6) +* macros, how to define new: Definitions. (line 6) +* macros, how to delete: Undefine. (line 6) +* macros, how to rename: Defn. (line 6) +* macros, indirect call of: Indir. (line 6) +* macros, quoted arguments to: Quoting Arguments. (line 6) +* macros, recursive: Shift. (line 6) +* macros, special arguments to: Pseudo Arguments. (line 6) +* macros, temporary redefinition of: Pushdef. (line 6) +* messages, printing error: Errprint. (line 6) +* multibranches: Ifelse. (line 66) +* names: Names. (line 6) +* options, command line: Invoking m4. (line 10) +* output, diverting to files: Divert. (line 6) +* output, formatted: Format. (line 6) +* output, saving debugging: Debug Output. (line 6) +* pattern substitution: Patsubst. (line 6) +* platform macro: Platform macros. (line 31) +* platform macros: Platform macros. (line 6) +* POSIXLY_CORRECT: Invoking m4. (line 10) +* printing error messages: Errprint. (line 6) +* quote delimiters, changing the: Changequote. (line 6) +* quoted macro arguments: Quoting Arguments. (line 6) +* quoted string: Quoted strings. (line 6) +* recursive macros: Shift. (line 6) +* redefinition of macros, temporary: Pushdef. (line 6) +* regular expressions <1>: Patsubst. (line 6) +* regular expressions: Regexp. (line 6) +* reloading a frozen file: Using frozen files. (line 6) +* renaming macros: Defn. (line 6) +* running shell commands: Shell commands. (line 6) +* running UNIX commands: Shell commands. (line 6) +* saving debugging output: Debug Output. (line 6) +* saving input: M4wrap. (line 6) +* search path for included files: Search Path. (line 6) +* shell commands, exit status from: Sysval. (line 6) +* shell commands, running: Shell commands. (line 6) +* special arguments to macros: Pseudo Arguments. (line 6) +* status of shell commands: Sysval. (line 6) +* status, setting m4 exit: M4exit. (line 6) +* strings, length of: Len. (line 6) +* substitution by regular expression: Patsubst. (line 6) +* substrings, extracting: Substr. (line 6) +* temporary file names: Mkstemp. (line 6) +* temporary redefinition of macros: Pushdef. (line 6) +* tokens: Syntax. (line 6) +* tracing macro expansion: Trace. (line 6) +* translating characters: Translit. (line 6) +* undefining macros: Undefine. (line 6) +* UNIX commands, exit status from: Sysval. (line 6) +* UNIX commands, running: Shell commands. (line 6) +* variables, local: Pushdef. (line 75) +* words, lexical structure of: Changeword. (line 6) + + +File: m4.info, Node: Macro index, Prev: Concept index, Up: Indices + +B.2 Index for all `m4' macros +============================= + +References are exclusively to the places where a builtin is introduced +the first time. + +[index] +* Menu: + +* __file__: Location. (line 10) +* __gnu__: Platform macros. (line 12) +* __line__: Location. (line 11) +* __os2__: Platform macros. (line 13) +* __program__: Location. (line 12) +* __unix__: Platform macros. (line 15) +* __windows__: Platform macros. (line 17) +* builtin: Builtin. (line 9) +* capitalize: Patsubst. (line 55) +* changecom: Changecom. (line 10) +* changequote: Changequote. (line 10) +* changeword: Changeword. (line 23) +* cleardivert: Cleardivert. (line 26) +* debugfile: Debug Output. (line 11) +* debugmode: Debug Levels. (line 63) +* decr: Incr. (line 11) +* define: Define. (line 10) +* defn: Defn. (line 10) +* divert: Divert. (line 9) +* divnum: Divnum. (line 9) +* dnl: Dnl. (line 9) +* downcase: Patsubst. (line 54) +* dquote: Shift. (line 53) +* dquote_elt: Shift. (line 54) +* dumpdef: Dumpdef. (line 10) +* errprint: Errprint. (line 9) +* esyscmd: Esyscmd. (line 9) +* eval: Eval. (line 9) +* example: Manual. (line 32) +* fatal_error: M4exit. (line 25) +* foreach: Foreach. (line 9) +* foreachq: Foreach. (line 10) +* forloop: Forloop. (line 9) +* format: Format. (line 9) +* ifdef: Ifdef. (line 10) +* ifelse: Ifelse. (line 12) +* include: Include. (line 9) +* incr: Incr. (line 10) +* index: Index macro. (line 9) +* indir: Indir. (line 9) +* len: Len. (line 9) +* m4exit: M4exit. (line 10) +* m4wrap: M4wrap. (line 14) +* maketemp: Mkstemp. (line 12) +* mkstemp: Mkstemp. (line 11) +* os2: Platform macros. (line 14) +* patsubst: Patsubst. (line 9) +* popdef: Pushdef. (line 12) +* pushdef: Pushdef. (line 11) +* quote: Shift. (line 52) +* regexp: Regexp. (line 9) +* reverse: Shift. (line 32) +* shift: Shift. (line 17) +* sinclude: Include. (line 10) +* substr: Substr. (line 9) +* syscmd: Syscmd. (line 9) +* sysval: Sysval. (line 9) +* traceoff: Trace. (line 11) +* traceon: Trace. (line 10) +* translit: Translit. (line 9) +* undefine: Undefine. (line 9) +* undivert: Undivert. (line 9) +* unix: Platform macros. (line 16) +* upcase: Patsubst. (line 53) +* windows: Platform macros. (line 18) + + + +Tag Table: +Node: Top840 +Node: Preliminaries9196 +Node: Intro9882 +Node: History11514 +Node: Bugs14146 +Node: Manual15408 +Node: Invoking m418340 +Node: Operation modes20488 +Node: Preprocessor features22199 +Node: Limits control24701 +Node: Frozen state27637 +Node: Debugging options28436 +Node: Command line files30175 +Node: Syntax31538 +Node: Names32539 +Node: Quoted strings33001 +Node: Comments33650 +Node: Other tokens34517 +Node: Input processing35095 +Node: Macros36982 +Node: Invocation37476 +Node: Inhibiting Invocation38277 +Node: Macro Arguments41848 +Node: Quoting Arguments44539 +Node: Macro expansion45861 +Node: Definitions46529 +Node: Define47314 +Node: Arguments49520 +Node: Pseudo Arguments51268 +Node: Undefine54483 +Node: Defn55613 +Node: Pushdef58346 +Node: Indir60680 +Node: Builtin62782 +Node: Conditionals64544 +Node: Ifdef65363 +Node: Ifelse66226 +Node: Shift69445 +Node: Forloop73244 +Node: Foreach75813 +Node: Debugging80792 +Node: Dumpdef81377 +Node: Trace82761 +Node: Debug Levels85449 +Node: Debug Output88255 +Node: Input Control89526 +Node: Dnl90063 +Node: Changequote91964 +Node: Changecom96760 +Node: Changeword100276 +Node: M4wrap105717 +Node: File Inclusion107978 +Node: Include108295 +Node: Search Path110895 +Node: Diversions111812 +Node: Divert113603 +Node: Undivert116129 +Node: Divnum119011 +Node: Cleardivert119475 +Node: Text handling120680 +Node: Len121403 +Node: Index macro121788 +Node: Regexp122559 +Node: Substr124808 +Node: Translit125859 +Node: Patsubst128581 +Node: Format132121 +Node: Arithmetic134188 +Node: Incr134637 +Node: Eval135412 +Node: Shell commands139821 +Node: Platform macros140743 +Node: Syscmd142487 +Node: Esyscmd144292 +Node: Sysval145340 +Node: Mkstemp146895 +Node: Miscellaneous150197 +Node: Errprint150630 +Node: Location151849 +Node: M4exit154651 +Node: Frozen files156738 +Node: Using frozen files157519 +Node: Frozen file format160759 +Node: Compatibility163822 +Node: Extensions164437 +Node: Incompatibilities168204 +Node: Other Incompatibilities176116 +Node: Answers178044 +Node: Improved exch178661 +Node: Improved forloop179199 +Node: Improved foreach181035 +Node: Improved cleardivert187974 +Node: Improved fatal_error188957 +Node: Copying This Manual190019 +Node: GNU Free Documentation License190275 +Node: Indices212669 +Node: Concept index212949 +Node: Macro index224692 + +End Tag Table diff --git a/src/bin/m4/doc/m4.texinfo b/src/bin/m4/doc/m4.texinfo new file mode 100644 index 0000000000..b886565480 --- /dev/null +++ b/src/bin/m4/doc/m4.texinfo @@ -0,0 +1,6026 @@ +\input texinfo @c -*- texinfo -*- +@comment ======================================================== +@comment %**start of header +@setfilename m4.info +@settitle GNU M4 macro processor +@setchapternewpage odd +@ifnothtml +@setcontentsaftertitlepage +@end ifnothtml +@finalout + +@include version.texi + +@c @tabchar{} +@c ---------- +@c The testsuite expects literal tab output in some examples, but +@c literal tabs in texinfo lead to formatting issues. +@macro tabchar +@ @c +@end macro + +@c @ovar{ARG} +@c ------------------- +@c The ARG is an optional argument. To be used for macro arguments in +@c their documentation. +@macro ovar{varname} +@r{[}@var{\varname\}@r{]} +@end macro + +@c @dvar{ARG, DEFAULT} +@c ------------------- +@c The ARG is an optional argument, defaulting to DEFAULT. To be used +@c for macro arguments in their documentation. +@macro dvar{varname, default} +@r{[}@var{\varname\} = @samp{\default\}@r{]} +@end macro + +@comment %**end of header +@comment ======================================================== + +@copying + +This manual is for @acronym{GNU} M4 (version @value{VERSION}, @value{UPDATED}), +a package containing an implementation of the m4 macro language. + +Copyright @copyright{} 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, +2006 Free Software Foundation, Inc. + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the @acronym{GNU} Free Documentation License, +Version 1.2 or any later version published by the Free Software +Foundation; with no Invariant Sections, no Front-Cover Texts, and no +Back-Cover Texts. A copy of the license is included in the section +entitled ``@acronym{GNU} Free Documentation License.'' +@end quotation +@end copying + +@dircategory GNU programming tools +@direntry +* M4: (m4). A powerful macro processor. +@end direntry + +@titlepage +@title GNU M4, version @value{VERSION} +@subtitle A powerful macro processor +@subtitle Edition @value{EDITION}, @value{UPDATED} +@author by Ren@'e Seindal + +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@contents + +@ifnottex +@node Top +@top GNU M4 +@insertcopying +@end ifnottex + +@acronym{GNU} @code{m4} is an implementation of the traditional UNIX macro +processor. It is mostly SVR4 compatible, although it has some +extensions (for example, handling more than 9 positional parameters +to macros). @code{m4} also has builtin functions for including +files, running shell commands, doing arithmetic, etc. Autoconf needs +@acronym{GNU} @code{m4} for generating @file{configure} scripts, but not for +running them. + +@acronym{GNU} @code{m4} was originally written by Ren@'e Seindal, with +subsequent changes by Fran@,{c}ois Pinard and other volunteers +on the Internet. All names and email addresses can be found in the +files @file{m4-@value{VERSION}/@/AUTHORS} and +@file{m4-@value{VERSION}/@/THANKS} from the @acronym{GNU} M4 +distribution. + +This is release @value{VERSION}. It is now considered stable: future +releases in the 1.4.x series are only meant to fix bugs, increase speed, +or improve documentation. However@dots{} + +An experimental feature, which would improve @code{m4} usefulness, +allows for changing the syntax for what is a @dfn{word} in @code{m4}. +You should use: +@comment ignore +@example +./configure --enable-changeword +@end example +@noindent +if you want this feature compiled in. The current implementation +slows down @code{m4} considerably and is hardly acceptable. In the +future, @code{m4} 2.0 will come with a different set of new features +that provide similar capabilities, but without the inefficiencies, so +changeword will go away and @emph{you should not count on it}. + +@menu +* Preliminaries:: Introduction and preliminaries +* Invoking m4:: Invoking @code{m4} +* Syntax:: Lexical and syntactic conventions + +* Macros:: How to invoke macros +* Definitions:: How to define new macros +* Conditionals:: Conditionals, loops, and recursion + +* Debugging:: How to debug macros and input + +* Input Control:: Input control +* File Inclusion:: File inclusion +* Diversions:: Diverting and undiverting output + +* Text handling:: Macros for text handling +* Arithmetic:: Macros for doing arithmetic +* Shell commands:: Macros for running shell commands +* Miscellaneous:: Miscellaneous builtin macros +* Frozen files:: Fast loading of frozen state + +* Compatibility:: Compatibility with other versions of @code{m4} +* Answers:: Correct version of some examples +* Copying This Manual:: How to make copies of this manual +* Indices:: Indices of concepts and macros + +@detailmenu + --- The Detailed Node Listing --- + +Introduction and preliminaries + +* Intro:: Introduction to @code{m4} +* History:: Historical references +* Bugs:: Problems and bugs +* Manual:: Using this manual + +Invoking @code{m4} + +* Operation modes:: Command line options for operation modes +* Preprocessor features:: Command line options for preprocessor features +* Limits control:: Command line options for limits control +* Frozen state:: Command line options for frozen state +* Debugging options:: Command line options for debugging +* Command line files:: Specifying input files on the command line + +Lexical and syntactic conventions + +* Names:: Macro names +* Quoted strings:: Quoting input to @code{m4} +* Comments:: Comments in @code{m4} input +* Other tokens:: Other kinds of input tokens +* Input processing:: How @code{m4} copies input to output + +How to invoke macros + +* Invocation:: Macro invocation +* Inhibiting Invocation:: Preventing macro invocation +* Macro Arguments:: Macro arguments +* Quoting Arguments:: On Quoting Arguments to macros +* Macro expansion:: Expanding macros + +How to define new macros + +* Define:: Defining a new macro +* Arguments:: Arguments to macros +* Pseudo Arguments:: Special arguments to macros +* Undefine:: Deleting a macro +* Defn:: Renaming macros +* Pushdef:: Temporarily redefining macros + +* Indir:: Indirect call of macros +* Builtin:: Indirect call of builtins + +Conditionals, loops, and recursion + +* Ifdef:: Testing if a macro is defined +* Ifelse:: If-else construct, or multibranch +* Shift:: Recursion in @code{m4} +* Forloop:: Iteration by counting +* Foreach:: Iteration by list contents + +How to debug macros and input + +* Dumpdef:: Displaying macro definitions +* Trace:: Tracing macro calls +* Debug Levels:: Controlling debugging output +* Debug Output:: Saving debugging output + +Input control + +* Dnl:: Deleting whitespace in input +* Changequote:: Changing the quote characters +* Changecom:: Changing the comment delimiters +* Changeword:: Changing the lexical structure of words +* M4wrap:: Saving text until end of input + +File inclusion + +* Include:: Including named files +* Search Path:: Searching for include files + +Diverting and undiverting output + +* Divert:: Diverting output +* Undivert:: Undiverting output +* Divnum:: Diversion numbers +* Cleardivert:: Discarding diverted text + +Macros for text handling + +* Len:: Calculating length of strings +* Index macro:: Searching for substrings +* Regexp:: Searching for regular expressions +* Substr:: Extracting substrings +* Translit:: Translating characters +* Patsubst:: Substituting text by regular expression +* Format:: Formatting strings (printf-like) + +Macros for doing arithmetic + +* Incr:: Decrement and increment operators +* Eval:: Evaluating integer expressions + +Macros for running shell commands + +* Platform macros:: Determining the platform +* Syscmd:: Executing simple commands +* Esyscmd:: Reading the output of commands +* Sysval:: Exit status +* Mkstemp:: Making temporary files + +Miscellaneous builtin macros + +* Errprint:: Printing error messages +* Location:: Printing current location +* M4exit:: Exiting from @code{m4} + +Fast loading of frozen state + +* Using frozen files:: Using frozen files +* Frozen file format:: Frozen file format + +Compatibility with other versions of @code{m4} + +* Extensions:: Extensions in @acronym{GNU} M4 +* Incompatibilities:: Facilities in System V m4 not in GNU M4 +* Other Incompatibilities:: Other incompatibilities + +Correct version of some examples + +* Improved exch:: Solution for @code{exch} +* Improved forloop:: Solution for @code{forloop} +* Improved foreach:: Solution for @code{foreach} +* Improved cleardivert:: Solution for @code{cleardivert} +* Improved fatal_error:: Solution for @code{fatal_error} + +How to make copies of this manual + +* GNU Free Documentation License:: License for copying this manual + +Indices of concepts and macros + +* Concept index:: Index for many concepts +* Macro index:: Index for all @code{m4} macros + +@end detailmenu +@end menu + +@node Preliminaries +@chapter Introduction and preliminaries + +This first chapter explains what @acronym{GNU} @code{m4} is, where @code{m4} +comes from, how to read and use this documentation, how to call the +@code{m4} program, and how to report bugs about it. It concludes by +giving tips for reading the remainder of the manual. + +The following chapters then detail all the features of the @code{m4} +language. + +@menu +* Intro:: Introduction to @code{m4} +* History:: Historical references +* Bugs:: Problems and bugs +* Manual:: Using this manual +@end menu + +@node Intro +@section Introduction to @code{m4} + +@code{m4} is a macro processor, in the sense that it copies its +input to the output, expanding macros as it goes. Macros are either +builtin or user-defined, and can take any number of arguments. +Besides just doing macro expansion, @code{m4} has builtin functions +for including named files, running shell commands, doing integer +arithmetic, manipulating text in various ways, performing recursion, +etc.@dots{} @code{m4} can be used either as a front-end to a compiler, +or as a macro processor in its own right. + +The @code{m4} macro processor is widely available on all UNIXes, and has +been standardized by @acronym{POSIX}. +Usually, only a small percentage of users are aware of its existence. +However, those who find it often become committed users. The +popularity of @acronym{GNU} Autoconf, which requires @acronym{GNU} +@code{m4} for @emph{generating} @file{configure} scripts, is an incentive +for many to install it, while these people will not themselves +program in @code{m4}. @acronym{GNU} @code{m4} is mostly compatible with the +System V, Release 3 version, except for some minor differences. +@xref{Compatibility}, for more details. + +Some people find @code{m4} to be fairly addictive. They first use +@code{m4} for simple problems, then take bigger and bigger challenges, +learning how to write complex sets of @code{m4} macros along the way. +Once really addicted, users pursue writing of sophisticated @code{m4} +applications even to solve simple problems, devoting more time +debugging their @code{m4} scripts than doing real work. Beware that +@code{m4} may be dangerous for the health of compulsive programmers. + +@node History +@section Historical references + +@code{GPM} was an important ancestor of @code{m4}. See +C. Stratchey: ``A General Purpose Macro generator'', Computer Journal +8,3 (1965), pp. 225 ff. @code{GPM} is also succinctly described into +David Gries classic ``Compiler Construction for Digital Computers''. + +The classic B. Kernighan and P.J. Plauger: ``Software Tools'', +Addison-Wesley, Inc. (1976) describes and implements a Unix +macro-processor language, which inspired Dennis Ritchie to write +@code{m3}, a macro processor for the AP-3 minicomputer. + +Kernighan and Ritchie then joined forces to develop the original +@code{m4}, as described in ``The M4 Macro Processor'', Bell +Laboratories (1977). It had only 21 builtin macros. + +While @code{GPM} was more @emph{pure}, @code{m4} is meant to deal with +the true intricacies of real life: macros can be recognized without +being pre-announced, skipping whitespace or end-of-lines is easier, +more constructs are builtin instead of derived, etc. + +Originally, the Kernighan and Plauger macro-processor, and then +@code{m3}, formed the engine for the Rational FORTRAN preprocessor, +that is, the @code{Ratfor} equivalent of @code{cpp}. Later, @code{m4} +was used as a frontend for @code{Ratfor}, @code{C} and @code{Cobol}. + +Ren@'e Seindal released his implementation of @code{m4}, @acronym{GNU} +@code{m4}, +in 1990, with the aim of removing the artificial limitations in many +of the traditional @code{m4} implementations, such as maximum line +length, macro size, or number of macros. + +The late Professor A. Dain Samples described and implemented a further +evolution in the form of @code{M5}: ``User's Guide to the M5 Macro +Language: 2nd edition'', Electronic Announcement on comp.compilers +newsgroup (1992). + +Fran@,{c}ois Pinard took over maintenance of @acronym{GNU} @code{m4} in +1992, until 1994 when he released @acronym{GNU} @code{m4} 1.4, which was +the stable release for 10 years. It was at this time that @acronym{GNU} +Autoconf decided to require @acronym{GNU} @code{m4} as its underlying +engine, since all other implementations of @code{m4} had too many +limitations. + +More recently, in 2004, Paul Eggert released 1.4.1 and 1.4.2 which +addressed some long standing bugs in the venerable 1.4 release. +Then in 2005 Gary V. Vaughan collected together the many +patches to @acronym{GNU} @code{m4} 1.4 that were floating around the net and +released 1.4.3 and 1.4.4. And in 2006, Eric Blake joined the team and +prepared patches for the release of 1.4.5, 1.4.6, 1.4.7, and 1.4.8. + +Meanwhile, development has continued on new features for @code{m4}, such +as dynamic module loading and additional builtins. When complete, +@acronym{GNU} @code{m4} 2.0 will start a new series of releases. + +@node Bugs +@section Problems and bugs + +If you have problems with @acronym{GNU} M4 or think you've found a bug, +please report it. Before reporting a bug, make sure you've actually +found a real bug. Carefully reread the documentation and see if it +really says you can do what you're trying to do. If it's not clear +whether you should be able to do something or not, report that too; it's +a bug in the documentation! + +Before reporting a bug or trying to fix it yourself, try to isolate it +to the smallest possible input file that reproduces the problem. Then +send us the input file and the exact results @code{m4} gave you. Also +say what you expected to occur; this will help us decide whether the +problem was really in the documentation. + +Once you've got a precise problem, send e-mail to (Internet) +@email{bug-m4@@gnu.org}. Please include the version number of @code{m4} +you are using. You can get this information with the command +@kbd{m4 --version}. Also provide details about the platform you are +executing on. + +Non-bug suggestions are always welcome as well. If you have questions +about things that are unclear in the documentation or are just obscure +features, please report them too. + +@node Manual +@section Using this manual + +This manual contains a number of examples of @code{m4} input and output, +and a simple notation is used to distinguish input, output and error +messages from @code{m4}. Examples are set out from the normal text, and +shown in a fixed width font, like this + +@comment ignore +@example +This is an example of an example! +@end example + +To distinguish input from output, all output from @code{m4} is prefixed +by the string @samp{@result{}}, and all error messages by the string +@samp{@error{}}. Thus + +@comment ignore +@example +Example of input line +@result{}Output line from m4 +@error{}and an error message +@end example + +The sequence @samp{^D} in an example indicates the end of the input file. +The majority of these examples are self-contained, and you can run them +with similar results by invoking @kbd{m4 -d}. In fact, the testsuite +that is bundled in the @acronym{GNU} M4 package consists of the examples +in this document! + +As each of the predefined macros in @code{m4} is described, a prototype +call of the macro will be shown, giving descriptive names to the +arguments, e.g., + +@deffn Composite example (@var{string}, @dvar{count, 1}, @ + @ovar{argument}@dots{}) +This is a sample prototype. There is not really a macro named +@code{example}, but this documents that if there were, it would be a +Composite macro, rather than a Builtin. It requires at least one +argument, @var{string}. Remember that in @code{m4}, there must not be a +space between the macro name and the opening parenthesis, unless it was +intended to call the macro without any arguments. The brackets around +@var{count} and @var{argument} show that these arguments are optional. +If @var{count} is omitted, the macro behaves as if count were @samp{1}, +whereas if @var{argument} is omitted, the macro behaves as if it were +the empty string. A blank argument is not the same as an omitted +argument. For example, @samp{example(`a')}, @samp{example(`a',`1')}, +and @samp{example(`a',`1',)} would behave identically with @var{count} +set to @samp{1}; while @samp{example(`a',)} and @samp{example(`a',`')} +would explicitly pass the empty string for @var{count}. The ellipses +(@samp{@dots{}}) show that the macro processes additional arguments +after @var{argument}, rather than ignoring them. +@end deffn + +All macro arguments in @code{m4} are strings, but some are given +special interpretation, e.g., as numbers, file names, regular +expressions, etc. The documentation for each macro will state how the +parameters are interpreted, and what happens if the argument cannot be +parsed according to the desired interpretation. Unless specified +otherwise, a parameter specified to be a number is parsed as a decimal, +even if the argument has leading zeros; and parsing the empty string as +a number results in 0 rather than an error, although a warning will be +issued. + +This document consistently writes and uses @dfn{builtin}, without a +hyphen, as if it were an English word. This is how the @code{builtin} +primitive is spelled within @code{m4}. + +@node Invoking m4 +@chapter Invoking @code{m4} + +The format of the @code{m4} command is: + +@comment ignore +@example +@code{m4} @r{[}@var{option}@dots{}@r{]} @r{[}@var{file}@dots{}@r{]} +@end example + +@cindex command line, options +@cindex options, command line +@cindex @env{POSIXLY_CORRECT} +All options begin with @samp{-}, or if long option names are used, with +@samp{--}. A long option name need not be written completely, any +unambiguous prefix is sufficient. @acronym{POSIX} requires @code{m4} to +recognize arguments intermixed with files, even when +@env{POSIXLY_CORRECT} is set in the environment. Most options take +effect at startup regardless of their position, but some are documented +below as taking effect after any files that occurred earlier in the +command line. The argument @option{--} is a marker to denote the end of +options. + +With short options, options that do not take arguments may be combined +into a single command line argument with subsequent options, options +with mandatory arguments may be provided either as a single command line +argument or as two arguments, and options with optional arguments must +be provided as a single argument. In other words, +@kbd{m4 -QPDfoo -d a -d+f} is equivalent to +@kbd{m4 -Q -P -D foo -d -d+f -- ./a}, although the latter form is +considered canonical. + +With long options, options with mandatory arguments may be provided with +an equal sign (@samp{=}) in a single argument, or as two arguments, and +options with optional arguments must be provided as a single argument. +In other words, @kbd{m4 --def foo --debug a} is equivalent to +@kbd{m4 --define=foo --debug= -- ./a}, although the latter form is +considered canonical (not to mention more robust, in case a future +version of @code{m4} introduces an option named @option{--default}). + +@code{m4} understands the following options, grouped by functionality. + +@menu +* Operation modes:: Command line options for operation modes +* Preprocessor features:: Command line options for preprocessor features +* Limits control:: Command line options for limits control +* Frozen state:: Command line options for frozen state +* Debugging options:: Command line options for debugging +* Command line files:: Specifying input files on the command line +@end menu + +@node Operation modes +@section Command line options for operation modes + +Several options control the overall operation of @code{m4}: + +@table @code +@item --help +Print a help summary on standard output, then immediately exit +@code{m4} without reading any input files or performing any other +actions. + +@item --version +Print the version number of the program on standard output, then +immediately exit @code{m4} without reading any input files or +performing any other actions. + +@item -E +@itemx --fatal-warnings +Stop execution and exit @code{m4} once the first warning has been +issued, considering all of them to be fatal. + +@item -i +@itemx --interactive +@itemx -e +Makes this invocation of @code{m4} interactive. This means that all +output will be unbuffered, and interrupts will be ignored. The +spelling @option{-e} exists for compatibility with other @code{m4} +implementations, and issues a warning because it may be withdrawn in a +future version of @acronym{GNU} M4. + +@item -P +@itemx --prefix-builtins +Internally modify @emph{all} builtin macro names so they all start with +the prefix @samp{m4_}. For example, using this option, one should write +@samp{m4_define} instead of @samp{define}, and @samp{m4___file__} +instead of @samp{__file__}. This option has no effect if @option{-R} +is also specified. + +@item -Q +@itemx --quiet +@itemx --silent +Suppress warnings, such as missing or superfluous arguments in macro +calls, or treating the empty string as zero. + +@item -W @var{REGEXP} +@itemx --word-regexp=@var{REGEXP} +Use @var{REGEXP} as an alternative syntax for macro names. This +experimental option will not be present in all @acronym{GNU} @code{m4} +implementations (@pxref{Changeword}). +@end table + +@node Preprocessor features +@section Command line options for preprocessor features + +@cindex macro definitions, on the command line +@cindex command line, macro definitions on the +Several options allow @code{m4} to behave more like a preprocessor. +Macro definitions and deletions can be made on the command line, the +search path can be altered, and the output file can track where the +input came from. These features occur with the following options: + +@table @code +@item -D @var{NAME}@r{[}=@var{VALUE}@r{]} +@itemx --define=@var{NAME}@r{[}=@var{VALUE}@r{]} +This enters @var{NAME} into the symbol table, before any input files are +read. If @samp{=@var{VALUE}} is missing, the value is taken to be the +empty string. The @var{VALUE} can be any string, and the macro can be +defined to take arguments, just as if it was defined from within the +input. This option may be given more than once; order with respect to +file names is significant, and redefining the same @var{NAME} loses the +previous value. + +@item -I @var{DIRECTORY} +@itemx --include=@var{DIRECTORY} +Make @code{m4} search @var{DIRECTORY} for included files that are not +found in the current working directory. @xref{Search Path}, for more +details. This option may be given more than once. + +@item -s +@itemx --synclines +Generate synchronization lines, for use by the C preprocessor or other +similar tools. Order is significant with respect to file names. This +option is useful, for example, when @code{m4} is used as a +front end to a compiler. Source file name and line number information +is conveyed by directives of the form @samp{#line @var{linenum} +"@var{file}"}, which are inserted as needed into the middle of the +output. Such directives mean that the following line originated or was +expanded from the contents of input file @var{file} at line +@var{linenum}. The @samp{"@var{file}"} part is often omitted when +the file name did not change from the previous directive. + +Synchronization directives are always given on complete lines by +themselves. When a synchronization discrepancy occurs in the middle of +an output line, the associated synchronization directive is delayed +until the beginning of the next generated line. + +@item -U @var{NAME} +@itemx --undefine=@var{NAME} +This deletes any predefined meaning @var{NAME} might have. Obviously, +only predefined macros can be deleted in this way. This option may be +given more than once; undefining a @var{NAME} that does not have a +definition is silently ignored. Order is significant with respect to +file names. +@end table + +@node Limits control +@section Command line options for limits control + +There are some limits within @code{m4} that can be tuned. For +compatibility, @code{m4} also accepts some options that control limits +in other implementations, but which are automatically unbounded (limited +only by your hardware and operating system constraints) in @acronym{GNU} +@code{m4}. + +@table @code +@item -G +@itemx --traditional +Suppress all the extensions made in this implementation, compared to the +System V version. @xref{Compatibility}, for a list of these. + +@item -H @var{NUM} +@itemx --hashsize=@var{NUM} +Make the internal hash table for symbol lookup be @var{NUM} entries big. +For better performance, the number should be prime, but this is not +checked. The default is 509 entries. It should not be necessary to +increase this value, unless you define an excessive number of macros. + +@item -L @var{NUM} +@itemx --nesting-limit=@var{NUM} +Artificially limit the nesting of macro calls to @var{NUM} levels, +stopping program execution if this limit is ever exceeded. When not +specified, nesting is limited to 1024 levels. A value of zero means +unlimited; but then heavily nested code could potentially cause a stack +overflow. + +The precise effect of this option might be more correctly associated +with textual nesting than dynamic recursion. It has been useful +when some complex @code{m4} input was generated by mechanical means. +Most users would never need this option. If shown to be obtrusive, +this option (which is still experimental) might well disappear. + +This option does @emph{not} have the ability to break endless +rescanning loops, since these do not necessarily consume much memory +or stack space. Through clever usage of rescanning loops, one can +request complex, time-consuming computations from @code{m4} with useful +results. Putting limitations in this area would break @code{m4} power. +There are many pathological cases: @w{@samp{define(`a', `a')a}} is +only the simplest example (but @pxref{Compatibility}). Expecting @acronym{GNU} +@code{m4} to detect these would be a little like expecting a compiler +system to detect and diagnose endless loops: it is a quite @emph{hard} +problem in general, if not undecidable! + +@item -B @var{NUM} +@itemx -S @var{NUM} +@itemx -T @var{NUM} +These options are present for compatibility with System V @code{m4}, but +do nothing in this implementation. They may disappear in future +releases, and issue a warning to that effect. + +@item -N @var{NUM} +@itemx --diversions=@var{NUM} +These options are present only for compatibility with previous +versions of @acronym{GNU} @code{m4}, and were controlling the number of +possible diversions which could be used at the same time. They do nothing, +because there is no fixed limit anymore. They may disappear in future +releases, and issue a warning to that effect. +@end table + +@node Frozen state +@section Command line options for frozen state + +@acronym{GNU} @code{m4} comes with a feature of freezing internal state +(@pxref{Frozen files}). This can be used to speed up @code{m4} +execution when reusing a common initialization script. + +@table @code +@item -F @var{FILE} +@itemx --freeze-state=@var{FILE} +Once execution is finished, write out the frozen state on the specified +@var{FILE}. It is conventional, but not required, for @var{FILE} to end +in @samp{.m4f}. + +@item -R @var{FILE} +@itemx --reload-state=@var{FILE} +Before execution starts, recover the internal state from the specified +frozen @var{FILE}. The options @option{-D}, @option{-U}, and +@option{-t} take effect after state is reloaded, but before the input +files are read. +@end table + +@node Debugging options +@section Command line options for debugging + +Finally, there are several options for aiding in debugging @code{m4} +scripts. + +@table @code +@item -d@r{[}@var{FLAGS}@r{]} +@itemx --debug@r{[}=@var{FLAGS}@r{]} +Set the debug-level according to the flags @var{FLAGS}. The debug-level +controls the format and amount of information presented by the debugging +functions. @xref{Debug Levels}, for more details on the format and +meaning of @var{FLAGS}. If omitted, @var{FLAGS} defaults to @samp{aeq}. + +@item --debugfile=@var{FILE} +@itemx -o @var{FILE} +@itemx --error-output=@var{FILE} +Redirect @code{dumpdef} output, debug messages, and trace output to the +named @var{FILE}. Warnings, error messages, and @code{errprint} output +are still printed to standard error. If unspecified, debug output goes +to standard error; if empty, debug output is discarded. @xref{Debug +Output}, for more details. The spellings @option{-o} and +@option{--error-output} are misleading and inconsistent with other +@acronym{GNU} tools; for now they are silently accepted as synonyms of +@option{--debugfile}, but in a future version of M4, using them will +cause a warning to be issued. + +@item -l @var{NUM} +@itemx --arglength=@var{NUM} +Restrict the size of the output generated by macro tracing to @var{NUM} +characters per trace line. If unspecified or zero, output is +unlimited. @xref{Debug Levels}, for more details. + +@item -t @var{NAME} +@itemx --trace=@var{NAME} +This enables tracing for the macro @var{NAME}, at any point where it is +defined. @var{NAME} need not be defined when this option is given. +This option may be given more than once, and order is significant with +respect to file names. @xref{Trace}, for more details. +@end table + +@node Command line files +@section Specifying input files on the command line + +@cindex command line, file names on the +@cindex file names, on the command line +The remaining arguments on the command line are taken to be input file +names. If no names are present, standard input is read. A file +name of @file{-} is taken to mean standard input. It is +conventional, but not required, for input files to end in @samp{.m4}. + +The input files are read in the sequence given. Standard input can be +read more than once, so the file name @file{-} may appear multiple times +on the command line; this makes a difference when input is from a +terminal or other special file type. It is an error if an input file +ends in the middle of argument collection, a comment, or a quoted +string. + +The options @option{--define} (@option{-D}), @option{--undefine} +(@option{-U}), @option{--synclines} (@option{-s}), and @option{--trace} +(@option{-t}) only take effect after processing input from any file +names that occur earlier on the command line. + +If none of the input files invoked @code{m4exit} (@pxref{M4exit}), the +exit status of @code{m4} will be 0 for success, 1 for general failure +(such as problems with reading an input file), and 63 for version +mismatch (@pxref{Using frozen files}). + +If you need to read a file whose name starts with a @file{-}, you can +specify it as @samp{./-file}, or use @option{--} to mark the end of +options. + +@node Syntax +@chapter Lexical and syntactic conventions + +@cindex input tokens +@cindex tokens +As @code{m4} reads its input, it separates it into @dfn{tokens}. A +token is either a name, a quoted string, or any single character, that +is not a part of either a name or a string. Input to @code{m4} can also +contain comments. @acronym{GNU} @code{m4} does not yet understand +locales; all operations are byte-oriented rather than +character-oriented. However, @code{m4} is eight-bit clean, so you can +use non-@sc{ascii} characters in quoted strings (@pxref{Changequote}), +comments (@pxref{Changecom}), and macro names (@pxref{Indir}), with the +exception of the @sc{nul} character (the zero byte @samp{'\0'}). + +@menu +* Names:: Macro names +* Quoted strings:: Quoting input to @code{m4} +* Comments:: Comments in @code{m4} input +* Other tokens:: Other kinds of input tokens +* Input processing:: How @code{m4} copies input to output +@end menu + +@node Names +@section Macro names + +@cindex names +A name is any sequence of letters, digits, and the character @samp{_} +(underscore), where the first character is not a digit. @code{m4} will +use the longest such sequence found in the input. If a name has a +macro definition, it will be subject to macro expansion +(@pxref{Macros}). Names are case-sensitive. + +Examples of legal names are: @samp{foo}, @samp{_tmp}, and @samp{name01}. + +@node Quoted strings +@section Quoting input to @code{m4} + +@cindex quoted string +A quoted string is a sequence of characters surrounded by quote +strings, defaulting to +@samp{`} and @samp{'}, where the nested begin and end quotes within the +string are balanced. The value of a string token is the text, with one +level of quotes stripped off. Thus + +@comment ignore +@example +`' +@result{} +@end example + +@noindent +is the empty string, and double-quoting turns into single-quoting. + +@comment ignore +@example +``quoted'' +@result{}`quoted' +@end example + +The quote characters can be changed at any time, using the builtin macro +@code{changequote}. @xref{Changequote}, for more information. + +@node Comments +@section Comments in @code{m4} input + +@cindex comments +Comments in @code{m4} are normally delimited by the characters @samp{#} +and newline. All characters between the comment delimiters are ignored, +but the entire comment (including the delimiters) is passed through to +the output---comments are @emph{not} discarded by @code{m4}. + +Comments cannot be nested, so the first newline after a @samp{#} ends +the comment. The commenting effect of the begin-comment string +can be inhibited by quoting it. + +@example +`quoted text' # `commented text' +@result{}quoted text # `commented text' +`quoting inhibits' `#' `comments' +@result{}quoting inhibits # comments +@end example + +The comment delimiters can be changed to any string at any time, using +the builtin macro @code{changecom}. @xref{Changecom}, for more +information. + +@node Other tokens +@section Other kinds of input tokens + +Any character, that is neither a part of a name, nor of a quoted string, +nor a comment, is a token by itself. When not in the context of macro +expansion, all of these tokens are just copied to output. However, +during macro expansion, whitespace characters (space, tab, newline, +formfeed, carriage return, vertical tab), parentheses (@samp{(} and +@samp{)}), comma (@samp{,}), and dollar (@samp{$}) have additional +roles, explained later. + +@node Input processing +@section How @code{m4} copies input to output + +As @code{m4} reads the input token by token, it will copy each token +directly to the output immediately. + +The exception is when it finds a word with a macro definition. In that +case @code{m4} will calculate the macro's expansion, possibly reading +more input to get the arguments. It then inserts the expansion in front +of the remaining input. In other words, the resulting text from a macro +call will be read and parsed into tokens again. + +@code{m4} expands a macro as soon as possible. If it finds a macro call +when collecting the arguments to another, it will expand the second +call first. For a running example, examine how @code{m4} handles this +input: + +@comment ignore +@example +format(`Result is %d', eval(`2**15')) +@end example + +@noindent +First, @code{m4} sees that the token @samp{format} is a macro name, so +it collects the tokens @samp{(}, @samp{`Result is %d'}, @samp{,}, +and @samp{@w{ }}, before encountering another potential macro. Sure +enough, @samp{eval} is a macro name, so the nested argument collection +picks up @samp{(}, @samp{`2**15'}, and @samp{)}, invoking the eval macro +with the lone argument of @samp{2**15}. The expansion of +@samp{eval(2**15)} is @samp{32768}, which is then rescanned as the five +tokens @samp{3}, @samp{2}, @samp{7}, @samp{6}, and @samp{8}; and +combined with the next @samp{)}, the format macro now has all its +arguments, as if the user had typed: + +@comment ignore +@example +format(`Result is %d', 32768) +@end example + +@noindent +The format macro expands to @samp{Result is 32768}, and we have another +round of scanning for the tokens @samp{Result}, @samp{@w{ }}, +@samp{is}, @samp{@w{ }}, @samp{3}, @samp{2}, @samp{7}, @samp{6}, and +@samp{8}. None of these are macros, so the final output is + +@comment ignore +@example +@result{}Result is 32768 +@end example + +The order in which @code{m4} expands the macros can be explored using +the trace facilities of @acronym{GNU} @code{m4} (@pxref{Trace}). + +This process continues until there are no more macro calls to expand and +all the input has been consumed. + +@node Macros +@chapter How to invoke macros + +This chapter covers macro invocation, macro arguments and how macro +expansion is treated. + +@menu +* Invocation:: Macro invocation +* Inhibiting Invocation:: Preventing macro invocation +* Macro Arguments:: Macro arguments +* Quoting Arguments:: On Quoting Arguments to macros +* Macro expansion:: Expanding macros +@end menu + +@node Invocation +@section Macro invocation + +@cindex macro invocation +Macro invocations has one of the forms + +@comment ignore +@example +name +@end example + +@noindent +which is a macro invocation without any arguments, or + +@comment ignore +@example +name(arg1, arg2, @dots{}, arg@var{n}) +@end example + +@noindent +which is a macro invocation with @var{n} arguments. Macros can have any +number of arguments. All arguments are strings, but different macros +might interpret the arguments in different ways. + +The opening parenthesis @emph{must} follow the @var{name} directly, with +no spaces in between. If it does not, the macro is called with no +arguments at all. + +For a macro call to have no arguments, the parentheses @emph{must} be +left out. The macro call + +@comment ignore +@example +name() +@end example + +@noindent +is a macro call with one argument, which is the empty string, not a call +with no arguments. + +@node Inhibiting Invocation +@section Preventing macro invocation + +An innovation of the @code{m4} language, compared to some of its +predecessors (like Stratchey's @code{GPM}, for example), is the ability +to recognize macro calls without resorting to any special, prefixed +invocation character. While generally useful, this feature might +sometimes be the source of spurious, unwanted macro calls. So, @acronym{GNU} +@code{m4} offers several mechanisms or techniques for inhibiting the +recognition of names as macro calls. + +First of all, many builtin macros cannot meaningfully be called +without arguments. For any of these macros, whenever an opening +parenthesis does not immediately follow their name, the builtin macro +call is not triggered. This solves the most usual cases, like for +@samp{include} or @samp{eval}. Later in this document, the sentence +``This macro is recognized only with parameters'' refers to this +specific provision. + +There is also a command line option (@option{--prefix-builtins}, or +@option{-P}, @pxref{Operation modes, , Invoking m4}) that renames all +builtin macros with a prefix of @samp{m4_} at startup. The option has +no effect whatsoever on user defined macros. For example, with this option, +one has to write @code{m4_dnl} and even @code{m4_m4exit}. It also has +no effect on whether a macro requires parameters. + +Another alternative is to redefine problematic macros to a name less +likely to cause conflicts, @xref{Definitions}. + +If your version of @acronym{GNU} @code{m4} has the @code{changeword} feature +compiled in, it offers far more flexibility in specifying the +syntax of macro names, both builtin or user-defined. @xref{Changeword}, +for more information on this experimental feature. + +Of course, the simplest way to prevent a name from being interpreted +as a call to an existing macro is to quote it. The remainder of +this section studies a little more deeply how quoting affects macro +invocation, and how quoting can be used to inhibit macro invocation. + +Even if quoting is usually done over the whole macro name, it can also +be done over only a few characters of this name (provided, of course, +that the unquoted portions are not also a macro). It is also possible +to quote the empty string, but this works only @emph{inside} the name. +For example: + +@example +`divert' +@result{}divert +`d'ivert +@result{}divert +di`ver't +@result{}divert +div`'ert +@result{}divert +@end example + +@noindent +all yield the string @samp{divert}. While in both: + +@example +`'divert +@result{} +divert`' +@result{} +@end example + +@noindent +the @code{divert} builtin macro will be called, which expands to the +empty string. + +The output of macro evaluations is always rescanned. The following +example would yield the string @samp{de}, exactly as if @code{m4} +has been given @w{@samp{substr(`abcde', `3', `2')}} as input: + +@example +define(`x', `substr(ab') +@result{} +define(`y', `cde, `3', `2')') +@result{} +x`'y +@result{}de +@end example + +Unquoted strings on either side of a quoted string are subject to +being recognized as macro names. In the following example, quoting the +empty string allows for the second @code{macro} to be recognized as such: + +@example +define(`macro', `m') +@result{} +macro(`m')macro +@result{}mmacro +macro(`m')`'macro +@result{}mm +@end example + +Quoting may prevent recognizing as a macro name the concatenation of a +macro expansion with the surrounding characters. In this example: + +@example +define(`macro', `di$1') +@result{} +macro(`v')`ert' +@result{}divert +macro(`v')ert +@result{} +@end example + +@noindent +the input will produce the string @samp{divert}. When the quotes were +removed, the @code{divert} builtin was called instead. + +@node Macro Arguments +@section Macro arguments + +@cindex macros, arguments to +@cindex arguments to macros +When a name is seen, and it has a macro definition, it will be expanded +as a macro. + +If the name is followed by an opening parenthesis, the arguments will be +collected before the macro is called. If too few arguments are +supplied, the missing arguments are taken to be the empty string. +However, some builtins are documented to behave differently for a +missing optional argument than for an explicit empty string. If there +are too many arguments, the excess arguments are ignored. Unquoted +leading whitespace is stripped off all arguments, but whitespace +generated by a macro expansion or occuring after a macro that expanded +to an empty string remains intact. Whitespace includes space, tab, +newline, carriage return, vertical tab, and formfeed. + +@example +define(`macro', `$1') +@result{} +macro( unquoted leading space lost) +@result{}unquoted leading space lost +macro(` quoted leading space kept') +@result{} quoted leading space kept +macro( + divert `unquoted space kept after expansion') +@result{} unquoted space kept after expansion +macro(macro(` +')`whitespace from expansion kept') +@result{} +@result{}whitespace from expansion kept +macro(`unquoted trailing whitespace kept' +) +@result{}unquoted trailing whitespace kept +@result{} +@end example + +Normally @code{m4} will issue warnings if a builtin macro is called +with an inappropriate number of arguments, but it can be suppressed with +the @option{--quiet} command line option (or @option{--silent}, or +@option{-Q}, @pxref{Operation modes, , Invoking m4}). For user +defined macros, there is no check of the number of arguments given. + +Macros are expanded normally during argument collection, and whatever +commas, quotes and parentheses that might show up in the resulting +expanded text will serve to define the arguments as well. Thus, if +@var{foo} expands to @samp{, b, c}, the macro call + +@comment ignore +@example +bar(a foo, d) +@end example + +@noindent +is a macro call with four arguments, which are @samp{a }, @samp{b}, +@samp{c} and @samp{d}. To understand why the first argument contains +whitespace, remember that unquoted leading whitespace is never part +of an argument, but trailing whitespace always is. + +It is possible for a macro's definition to change during argument +collection, in which case the expansion uses the definition that was in +effect at the time the opening @samp{(} was seen. + +@example +define(`f', `1') +@result{} +f(define(`f', `2')) +@result{}1 +f +@result{}2 +@end example + +It is an error if the end of file occurs while collecting arguments. + +@example +hello world +@result{}hello world +define( +^D +@error{}m4:stdin:2: ERROR: end of file in argument list +@end example + +@node Quoting Arguments +@section On Quoting Arguments to macros + +@cindex quoted macro arguments +@cindex macros, quoted arguments to +@cindex arguments, quoted macro +Each argument has unquoted leading whitespace removed. Within each +argument, all unquoted parentheses must match. For example, if +@var{foo} is a macro, + +@comment ignore +@example +foo(() (`(') `(') +@end example + +@noindent +is a macro call, with one argument, whose value is @samp{() (() (}. +Commas separate arguments, except when they occur inside quotes, +comments, or unquoted parentheses. @xref{Pseudo Arguments}, for +examples. + +It is common practice to quote all arguments to macros, unless you are +sure you want the arguments expanded. Thus, in the above +example with the parentheses, the `right' way to do it is like this: + +@comment ignore +@example +foo(`() (() (') +@end example + +It is, however, in certain cases necessary or convenient to leave out +quotes for some arguments, and there is nothing wrong in doing it. It +just makes life a bit harder, if you are not careful. For consistency, +this manual follows the rule of thumb that each layer of parentheses +introduces another layer of single quoting, except when showing the +consequences of quoting rules. This is done even when the quoted string +cannot be a macro, such as with integers when you have not changed the +syntax via @code{changeword} (@pxref{Changeword}). + +@node Macro expansion +@section Macro expansion + +@cindex macros, expansion of +@cindex expansion of macros +When the arguments, if any, to a macro call have been collected, the +macro is expanded, and the expansion text is pushed back onto the input +(unquoted), and reread. The expansion text from one macro call might +therefore result in more macros being called, if the calls are included, +completely or partially, in the first macro calls' expansion. + +Taking a very simple example, if @var{foo} expands to @samp{bar}, and +@var{bar} expands to @samp{Hello world}, the input + +@comment ignore +@example +foo +@end example + +@noindent +will expand first to @samp{bar}, and when this is reread and +expanded, into @samp{Hello world}. + +@node Definitions +@chapter How to define new macros + +@cindex macros, how to define new +@cindex defining new macros +Macros can be defined, redefined and deleted in several different ways. +Also, it is possible to redefine a macro without losing a previous +value, and bring back the original value at a later time. + +@menu +* Define:: Defining a new macro +* Arguments:: Arguments to macros +* Pseudo Arguments:: Special arguments to macros +* Undefine:: Deleting a macro +* Defn:: Renaming macros +* Pushdef:: Temporarily redefining macros + +* Indir:: Indirect call of macros +* Builtin:: Indirect call of builtins +@end menu + +@node Define +@section Defining a macro + +The normal way to define or redefine macros is to use the builtin +@code{define}: + +@deffn Builtin define (@var{name}, @ovar{expansion}) +Defines @var{name} to expand to @var{expansion}. If +@var{expansion} is not given, it is taken to be empty. + +The expansion of @code{define} is void. +The macro @code{define} is recognized only with parameters. +@end deffn + +The following example defines the macro @var{foo} to expand to the text +@samp{Hello World.}. + +@example +define(`foo', `Hello world.') +@result{} +foo +@result{}Hello world. +@end example + +The empty line in the output is there because the newline is not +a part of the macro definition, and it is consequently copied to +the output. This can be avoided by use of the macro @code{dnl}. +@xref{Dnl}, for details. + +The first argument to @code{define} should be quoted; otherwise, if the +macro is already defined, you will be defining a different macro. This +example shows the problems with underquoting, since we did not want to +redefine @code{one}: + +@example +define(foo, one) +@result{} +define(foo, two) +@result{} +one +@result{}two +@end example + +@cindex @acronym{GNU} extensions +@acronym{GNU} @code{m4} normally replaces only the @emph{topmost} +definition of a macro if it has several definitions from @code{pushdef} +(@pxref{Pushdef}). Some other implementations of @code{m4} replace all +definitions of a macro with @code{define}. @xref{Incompatibilities}, +for more details. + +As a @acronym{GNU} extension, the first argument to @code{define} does +not have to be a simple word. +It can be any text string, even the empty string. A macro with a +non-standard name cannot be invoked in the normal way, as the name is +not recognized. It can only be referenced by the builtins @code{Indir} +(@pxref{Indir}) and @code{Defn} (@pxref{Defn}). + +@cindex arrays +Arrays and associative arrays can be simulated by using this trick. + +@example +define(`array', `defn(format(``array[%d]'', `$1'))') +@result{} +define(`array_set', `define(format(``array[%d]'', `$1'), `$2')') +@result{} +array_set(`4', `array element no. 4') +@result{} +array_set(`17', `array element no. 17') +@result{} +array(`4') +@result{}array element no. 4 +array(eval(`10 + 7')) +@result{}array element no. 17 +@end example + +Change the @code{%d} to @code{%s} and it is an associative array. + +@node Arguments +@section Arguments to macros + +@cindex macros, arguments to +@cindex Arguments to macros +Macros can have arguments. The @var{n}th argument is denoted by +@code{$n} in the expansion text, and is replaced by the @var{n}th actual +argument, when the macro is expanded. Replacement of arguments happens +before rescanning, regardless of how many nesting levels of quoting +appear in the expansion. Here is an example of a macro with +two arguments. It simply exchanges the order of the two arguments. + +@example +define(`exch', `$2, $1') +@result{} +exch(`arg1', `arg2') +@result{}arg2, arg1 +@end example + +This can be used, for example, if you like the arguments to +@code{define} to be reversed. + +@example +define(`exch', `$2, $1') +@result{} +define(exch(``expansion text'', ``macro'')) +@result{} +macro +@result{}expansion text +@end example + +@xref{Quoting Arguments}, for an explanation of the double quotes. +(You should try and improve this example so that clients of @code{exch} +do not have to double quote; or @pxref{Improved exch, , Answers}). + +@cindex @acronym{GNU} extensions +@acronym{GNU} @code{m4} allows the number following the @samp{$} to +consist of one +or more digits, allowing macros to have any number of arguments. This +is not so in UNIX implementations of @code{m4}, which only recognize +one digit. + +As a special case, the zeroth argument, @code{$0}, is always the name +of the macro being expanded. + +@example +define(`test', ``Macro name: $0'') +@result{} +test +@result{}Macro name: test +@end example + +If you want quoted text to appear as part of the expansion text, +remember that quotes can be nested in quoted strings. Thus, in + +@example +define(`foo', `This is macro `foo'.') +@result{} +foo +@result{}This is macro foo. +@end example + +@noindent +The @samp{foo} in the expansion text is @emph{not} expanded, since it is +a quoted string, and not a name. + +@node Pseudo Arguments +@section Special arguments to macros + +@cindex special arguments to macros +@cindex macros, special arguments to +@cindex arguments to macros, special +There is a special notation for the number of actual arguments supplied, +and for all the actual arguments. + +The number of actual arguments in a macro call is denoted by @code{$#} +in the expansion text. Thus, a macro to display the number of arguments +given can be + +@example +define(`nargs', `$#') +@result{} +nargs +@result{}0 +nargs() +@result{}1 +nargs(`arg1', `arg2', `arg3') +@result{}3 +nargs(`commas can be quoted, like this') +@result{}1 +nargs(arg1#inside comments, commas do not separate arguments +still arg1) +@result{}1 +nargs((unquoted parentheses, like this, group arguments)) +@result{}1 +@end example + +The notation @code{$*} can be used in the expansion text to denote all +the actual arguments, unquoted, with commas in between. For example + +@example +define(`echo', `$*') +@result{} +echo(arg1, arg2, arg3 , arg4) +@result{}arg1,arg2,arg3 ,arg4 +@end example + +Often each argument should be quoted, and the notation @code{$@@} handles +that. It is just like @code{$*}, except that it quotes each argument. +A simple example of that is: + +@example +define(`echo', `$@@') +@result{} +echo(arg1, arg2, arg3 , arg4) +@result{}arg1,arg2,arg3 ,arg4 +@end example + +Where did the quotes go? Of course, they were eaten, when the expanded +text were reread by @code{m4}. To show the difference, try + +@example +define(`echo1', `$*') +@result{} +define(`echo2', `$@@') +@result{} +define(`foo', `This is macro `foo'.') +@result{} +echo1(foo) +@result{}This is macro This is macro foo.. +echo1(`foo') +@result{}This is macro foo. +echo2(foo) +@result{}This is macro foo. +echo2(`foo') +@result{}foo +@end example + +@noindent +@xref{Trace}, if you do not understand this. As another example of the +difference, remember that comments encountered in arguments are passed +untouched to the macro, and that quoting disables comments. + +@example +define(`echo1', `$*') +@result{} +define(`echo2', `$@@') +@result{} +define(`foo', `bar') +@result{} +echo1(#foo'foo +foo) +@result{}#foo'foo +@result{}bar +echo2(#foo'foo +foo) +@result{}#foobar +@result{}bar' +@end example + +A @samp{$} sign in the expansion text, that is not followed by anything +@code{m4} understands, is simply copied to the macro expansion, as any +other text is. + +@example +define(`foo', `$$$ hello $$$') +@result{} +foo +@result{}$$$ hello $$$ +@end example + +If you want a macro to expand to something like @samp{$12}, the +judicious use of nested quoting can put a safe character between the +@code{$} and the next character, relying on the rescanning to remove the +nested quote. This will prevent @code{m4} from interpreting the +@code{$} sign as a reference to an argument. + +@example +define(`foo', `no nested quote: $1') +@result{} +foo(`arg') +@result{}no nested quote: arg +define(`foo', `nested quote around $: `$'1') +@result{} +foo(`arg') +@result{}nested quote around $: $1 +define(`foo', `nested empty quote after $: $`'1') +@result{} +foo(`arg') +@result{}nested empty quote after $: $1 +define(`foo', `nested quote around next character: $`1'') +@result{} +foo(`arg') +@result{}nested quote around next character: $1 +define(`foo', `nested quote around both: `$1'') +@result{} +foo(`arg') +@result{}nested quote around both: arg +@end example + +@node Undefine +@section Deleting a macro + +@cindex macros, how to delete +@cindex deleting macros +@cindex undefining macros +A macro definition can be removed with @code{undefine}: + +@deffn Builtin undefine (@var{name}@dots{}) +For each argument, remove the macro @var{name}. The macro names must +necessarily be quoted, since they will be expanded otherwise. + +The expansion of @code{undefine} is void. +The macro @code{undefine} is recognized only with parameters. +@end deffn + +@example +foo bar blah +@result{}foo bar blah +define(`foo', `some')define(`bar', `other')define(`blah', `text') +@result{} +foo bar blah +@result{}some other text +undefine(`foo') +@result{} +foo bar blah +@result{}foo other text +undefine(`bar', `blah') +@result{} +foo bar blah +@result{}foo bar blah +@end example + +Undefining a macro inside that macro's expansion is safe; the macro +still expands to the definition that was in effect at the @samp{(}. + +@example +define(`f', ``$0':$1') +@result{} +f(f(f(undefine(`f')`hello world'))) +@result{}f:f:f:hello world +f(`bye') +@result{}f(bye) +@end example + +It is not an error for @var{name} to have no macro definition. In that +case, @code{undefine} does nothing. + +@node Defn +@section Renaming macros + +@cindex macros, how to rename +@cindex renaming macros +It is possible to rename an already defined macro. To do this, you need +the builtin @code{defn}: + +@deffn Builtin defn (@var{name}) +Expands to the @emph{quoted definition} of @var{name}. If the +argument is not a defined macro, the expansion is void. + +If @var{name} is a user-defined macro, the quoted definition is simply +the quoted expansion text. If, instead, @var{name} is a builtin, the +expansion is a special token, which points to the builtin's internal +definition. This token is only meaningful as the second argument to +@code{define} (and @code{pushdef}), and is silently converted to an +empty string in most other contexts. + +The macro @code{defn} is recognized only with parameters. +@end deffn + +Its normal use is best understood through an example, which shows how to +rename @code{undefine} to @code{zap}: + +@example +define(`zap', defn(`undefine')) +@result{} +zap(`undefine') +@result{} +undefine(`zap') +@result{}undefine(zap) +@end example + +In this way, @code{defn} can be used to copy macro definitions, and also +definitions of builtin macros. Even if the original macro is removed, +the other name can still be used to access the definition. + +The fact that macro definitions can be transferred also explains why you +should use @code{$0}, rather than retyping a macro's name in its +definition: + +@example +define(`foo', `This is `$0'') +@result{} +define(`bar', defn(`foo')) +@result{} +bar +@result{}This is bar +@end example + +Macros used as string variables should be referred through @code{defn}, +to avoid unwanted expansion of the text: + +@example +define(`string', `The macro dnl is very useful +') +@result{} +string +@result{}The macro@w{ } +defn(`string') +@result{}The macro dnl is very useful +@result{} +@end example + +However, it is important to remember that @code{m4} rescanning is purely +textual. If an unbalanced end-quote string occurs in a macro +definition, the rescan will see that embedded quote as the termination +of the quoted string, and the remainder of the macro's definition will +be rescanned unquoted. Thus it is a good idea to avoid unbalanced +end-quotes in macro definitions or arguments to macros. + +@example +define(`foo', a'a) +@result{} +define(`a', `A') +@result{} +define(`echo', `$@@') +@result{} +foo +@result{}A'A +defn(`foo') +@result{}aA' +echo(foo) +@result{}AA' +@end example + +Using @code{defn} to generate special tokens for builtin macros outside +of expected contexts can sometimes trigger warnings. But most of the +time, such tokens are silently converted to the empty string. + +@example +defn(`defn') +@result{} +define(defn(`divnum'), `cannot redefine a builtin token') +@error{}m4:stdin:2: Warning: define: invalid macro name ignored +@result{} +divnum +@result{}0 +@end example + +@node Pushdef +@section Temporarily redefining macros + +@cindex macros, temporary redefinition of +@cindex temporary redefinition of macros +@cindex redefinition of macros, temporary +It is possible to redefine a macro temporarily, reverting to the +previous definition at a later time. This is done with the builtins +@code{pushdef} and @code{popdef}: + +@deffn Builtin pushdef (@var{name}, @ovar{expansion}) +@deffnx Builtin popdef (@var{name}@dots{}) +Analogous to @code{define} and @code{undefine}. + +These macros work in a stack-like fashion. A macro is temporarily +redefined with @code{pushdef}, which replaces an existing definition of +@var{name}, while saving the previous definition, before the new one is +installed. If there is no previous definition, @code{pushdef} behaves +exactly like @code{define}. + +If a macro has several definitions (of which only one is accessible), +the topmost definition can be removed with @code{popdef}. If there is +no previous definition, @code{popdef} behaves like @code{undefine}. + +The expansion of both @code{pushdef} and @code{popdef} is void. +The macros @code{pushdef} and @code{popdef} are recognized only with +parameters. +@end deffn + +@example +define(`foo', `Expansion one.') +@result{} +foo +@result{}Expansion one. +pushdef(`foo', `Expansion two.') +@result{} +foo +@result{}Expansion two. +pushdef(`foo', `Expansion three.') +@result{} +pushdef(`foo', `Expansion four.') +@result{} +popdef(`foo') +@result{} +foo +@result{}Expansion three. +popdef(`foo', `foo') +@result{} +foo +@result{}Expansion one. +popdef(`foo') +@result{} +foo +@result{}foo +@end example + +If a macro with several definitions is redefined with @code{define}, the +topmost definition is @emph{replaced} with the new definition. If it is +removed with @code{undefine}, @emph{all} the definitions are removed, +and not only the topmost one. + +@example +define(`foo', `Expansion one.') +@result{} +foo +@result{}Expansion one. +pushdef(`foo', `Expansion two.') +@result{} +foo +@result{}Expansion two. +define(`foo', `Second expansion two.') +@result{} +foo +@result{}Second expansion two. +undefine(`foo') +@result{} +foo +@result{}foo +@end example + +@cindex local variables +@cindex variables, local +Local variables within macros are made with @code{pushdef} and +@code{popdef}. At the start of the macro a new definition is pushed, +within the macro it is manipulated and at the end it is popped, +revealing the former definition. + +It is possible to temporarily redefine a builtin with @code{pushdef} +and @code{defn}. + +@node Indir +@section Indirect call of macros + +@cindex indirect call of macros +@cindex call of macros, indirect +@cindex macros, indirect call of +@cindex @acronym{GNU} extensions +Any macro can be called indirectly with @code{indir}: + +@deffn Builtin indir (@var{name}, @ovar{args@dots{}}) +Results in a call to the macro @var{name}, which is passed the +rest of the arguments @var{args}. If @var{name} is not defined, an +error message is printed, and the expansion is void. + +The macro @code{indir} is recognized only with parameters. +@end deffn + +This can be used to call macros with computed or ``invalid'' +names (@code{define} allows such names to be defined): + +@example +define(`$$internal$macro', `Internal macro (name `$0')') +@result{} +$$internal$macro +@result{}$$internal$macro +indir(`$$internal$macro') +@result{}Internal macro (name $$internal$macro) +@end example + +The point is, here, that larger macro packages can have private macros +defined, that will not be called by accident. They can @emph{only} be +called through the builtin @code{indir}. + +One other point to observe is that argument collection occurs before +@code{indir} invokes @var{name}, so if argument collection changes the +value of @var{name}, that will be reflected in the final expansion. +This is different than the behavior when invoking macros directly, +where the definition that was in effect before argument collection is +used. + +@example +define(`f', `1') +@result{} +f(define(`f', `2')) +@result{}1 +indir(`f', define(`f', `3')) +@result{}3 +indir(`f', undefine(`f')) +@error{}m4:stdin:4: undefined macro `f' +@result{} +@end example + +When handed the result of @code{defn} (@pxref{Defn}) as one of its +arguments, @code{indir} defers to the invoked @var{name} for whether a +token representing a builtin is recognized or flattened to the empty +string. + +@example +indir(defn(`defn'), `divnum') +@error{}m4:stdin:1: Warning: indir: invalid macro name ignored +@result{} +indir(`define', defn(`defn'), `divnum') +@error{}m4:stdin:2: Warning: define: invalid macro name ignored +@result{} +indir(`define', `foo', defn(`divnum')) +@result{} +foo +@result{}0 +indir(`divert', defn(`foo')) +@error{}m4:stdin:5: empty string treated as 0 in builtin `divert' +@result{} +@end example + +@node Builtin +@section Indirect call of builtins + +@cindex indirect call of builtins +@cindex call of builtins, indirect +@cindex builtins, indirect call of +@cindex @acronym{GNU} extensions +Builtin macros can be called indirectly with @code{builtin}: + +@deffn Builtin builtin (@var{name}, @ovar{args@dots{}}) +Results in a call to the builtin @var{name}, which is passed the +rest of the arguments @var{args}. If @var{name} does not name a +builtin, an error message is printed, and the expansion is void. + +The macro @code{builtin} is recognized only with parameters. +@end deffn + +This can be used even if @var{name} has been given another definition +that has covered the original, or been undefined so that no macro +maps to the builtin. + +@example +pushdef(`define', `hidden') +@result{} +undefine(`undefine') +@result{} +define(`foo', `bar') +@result{}hidden +foo +@result{}foo +builtin(`define', `foo', defn(`divnum')) +@result{} +foo +@result{}0 +builtin(`define', `foo', `BAR') +@result{} +foo +@result{}BAR +undefine(`foo') +@result{}undefine(foo) +foo +@result{}BAR +builtin(`undefine', `foo') +@result{} +foo +@result{}foo +@end example + +The @var{name} argument only matches the original name of the builtin, +even when the @option{--prefix-builtins} option (or @option{-P}, +@pxref{Operation modes, , Invoking m4}) is in effect. This is different +from @code{indir}, which only tracks current macro names. + +Note that @code{indir} and @code{builtin} can be used to invoke builtins +without arguments, even when they normally require parameters to be +recognized; but it will provoke a warning, and result in a void expansion. + +@example +builtin +@result{}builtin +builtin() +@error{}m4:stdin:2: undefined builtin `' +@result{} +builtin(`builtin') +@error{}m4:stdin:3: Warning: too few arguments to builtin `builtin' +@result{} +builtin(`builtin',) +@error{}m4:stdin:4: undefined builtin `' +@result{} +@end example + +@node Conditionals +@chapter Conditionals, loops, and recursion + +Macros, expanding to plain text, perhaps with arguments, are not quite +enough. We would like to have macros expand to different things, based +on decisions taken at run-time. For that, we need some kind of conditionals. +Also, we would like to have some kind of loop construct, so we could do +something a number of times, or while some condition is true. + +@menu +* Ifdef:: Testing if a macro is defined +* Ifelse:: If-else construct, or multibranch +* Shift:: Recursion in @code{m4} +* Forloop:: Iteration by counting +* Foreach:: Iteration by list contents +@end menu + +@node Ifdef +@section Testing if a macro is defined + +@cindex conditionals +There are two different builtin conditionals in @code{m4}. The first is +@code{ifdef}: + +@deffn Builtin ifdef (@var{name}, @var{string-1}, @ovar{string-2}) +If @var{name} is defined as a macro, @code{ifdef} expands to +@var{string-1}, otherwise to @var{string-2}. If @var{string-2} is +omitted, it is taken to be the empty string (according to the normal +rules). + +The macro @code{ifdef} is recognized only with parameters. +@end deffn + +@example +ifdef(`foo', ``foo' is defined', ``foo' is not defined') +@result{}foo is not defined +define(`foo', `') +@result{} +ifdef(`foo', ``foo' is defined', ``foo' is not defined') +@result{}foo is defined +ifdef(`no_such_macro', `yes', `no', `extra argument') +@error{}m4:stdin:4: Warning: excess arguments to builtin `ifdef' ignored +@result{}no +@end example + +@node Ifelse +@section If-else construct, or multibranch + +@cindex comparing strings +The other conditional, @code{ifelse}, is much more powerful. It can be +used as a way to introduce a long comment, as an if-else construct, or +as a multibranch, depending on the number of arguments supplied: + +@deffn Builtin ifelse (@var{comment}) +@deffnx Builtin ifelse (@var{string-1}, @var{string-2}, @var{equal}, @ + @ovar{not-equal}) +@deffnx Builtin ifelse (@var{string-1}, @var{string-2}, @var{equal-1}, @ + @var{string-3}, @var{string-4}, @var{equal-2}, @dots{}) +Used with only one argument, the @code{ifelse} simply discards it and +produces no output. + +If called with three or four arguments, @code{ifelse} expands into +@var{equal}, if @var{string-1} and @var{string-2} are equal (character +for character), otherwise it expands to @var{not-equal}. A final fifth +argument is ignored, after triggering a warning. + +If called with six or more arguments, and @var{string-1} and +@var{string-2} are equal, @code{ifelse} expands into @var{equal-1}, +otherwise the first three arguments are discarded and the processing +starts again. + +The macro @code{ifelse} is recognized only with parameters. +@end deffn + +Using only one argument is a common @code{m4} idiom for introducing a +block comment, as an alternative to repeatedly using @code{dnl}. This +special usage is recognized by @acronym{GNU} @code{m4}, so that in this +case, the warning about missing arguments is never triggered. + +@example +ifelse(`some comments') +@result{} +ifelse(`foo', `bar') +@error{}m4:stdin:2: Warning: too few arguments to builtin `ifelse' +@result{} +@end example + +Using three or four arguments provides decision points. + +@example +ifelse(`foo', `bar', `true') +@result{} +ifelse(`foo', `foo', `true') +@result{}true +define(`foo', `bar') +@result{} +ifelse(foo, `bar', `true', `false') +@result{}true +ifelse(foo, `foo', `true', `false') +@result{}false +@end example + +Notice how the first argument was used unquoted; it is common to compare +the expansion of a macro with a string. With this macro, you can now +reproduce the behavior of many of the builtins, where the macro is +recognized only with arguments. + +@example +define(`foo', `ifelse(`$#', `0', ``$0'', `arguments:$#')') +@result{} +foo +@result{}foo +foo() +@result{}arguments:1 +foo(`a', `b', `c') +@result{}arguments:3 +@end example + +@cindex multibranches +However, @code{ifelse} can take more than four arguments. If given more +than four arguments, @code{ifelse} works like a @code{case} or @code{switch} +statement in traditional programming languages. If @var{string-1} and +@var{string-2} are equal, @code{ifelse} expands into @var{equal-1}, otherwise +the procedure is repeated with the first three arguments discarded. This +calls for an example: + +@example +ifelse(`foo', `bar', `third', `gnu', `gnats') +@error{}m4:stdin:1: Warning: excess arguments to builtin `ifelse' ignored +@result{}gnu +ifelse(`foo', `bar', `third', `gnu', `gnats', `sixth') +@result{} +ifelse(`foo', `bar', `third', `gnu', `gnats', `sixth', `seventh') +@result{}seventh +ifelse(`foo', `bar', `3', `gnu', `gnats', `6', `7', `8') +@error{}m4:stdin:4: Warning: excess arguments to builtin `ifelse' ignored +@result{}7 +@end example + +Naturally, the normal case will be slightly more advanced than these +examples. A common use of @code{ifelse} is in macros implementing loops +of various kinds. + +@node Shift +@section Recursion in @code{m4} + +@cindex recursive macros +@cindex macros, recursive +There is no direct support for loops in @code{m4}, but macros can be +recursive. There is no limit on the number of recursion levels, other +than those enforced by your hardware and operating system. + +@cindex loops +Loops can be programmed using recursion and the conditionals described +previously. + +There is a builtin macro, @code{shift}, which can, among other things, +be used for iterating through the actual arguments to a macro: + +@deffn Builtin shift (@var{arg1}, @dots{}) +Takes any number of arguments, and expands to all its arguments except +@var{arg1}, separated by commas, with each argument quoted. + +The macro @code{shift} is recognized only with parameters. +@end deffn + +@example +shift +@result{}shift +shift(`bar') +@result{} +shift(`foo', `bar', `baz') +@result{}bar,baz +@end example + +An example of the use of @code{shift} is this macro: + +@deffn Composite reverse (@dots{}) +Takes any number of arguments, and reverses their order. +@end deffn + +It is implemented as: + +@example +define(`reverse', `ifelse(`$#', `0', , `$#', `1', ``$1'', + `reverse(shift($@@)), `$1'')') +@result{} +reverse +@result{} +reverse(`foo') +@result{}foo +reverse(`foo', `bar', `gnats', `and gnus') +@result{}and gnus, gnats, bar, foo +@end example + +While not a very interesting macro, it does show how simple loops can be +made with @code{shift}, @code{ifelse} and recursion. It also shows +that @code{shift} is usually used with @samp{$@@}. Sometimes, a +recursive algorithm requires adding quotes to each element: + +@deffn Composite quote (@dots{}) +@deffnx Composite dquote (@dots{}) +@deffnx Composite dquote_elt (@dots{}) +Takes any number of arguments, and adds quoting. With @code{quote}, +only one level of quoting is added, effectively removing whitespace +after commas and turning multiple arguments into a single string. With +@code{dquote}, two levels of quoting are added, one around each element, +and one around the list. And with @code{dquote_elt}, two levels of +quoting are added around each element. +@end deffn + +An actual implementation of these three macros is distributed as +@file{m4-@value{VERSION}/@/examples/@/quote.m4} in this package. First, +let's examine their usage: + +@example +include(`quote.m4') +@result{} +-quote-dquote-dquote_elt- +@result{}---- +-quote()-dquote()-dquote_elt()- +@result{}--`'-`'- +-quote(`1')-dquote(`1')-dquote_elt(`1')- +@result{}-1-`1'-`1'- +-quote(`1', `2')-dquote(`1', `2')-dquote_elt(`1', `2')- +@result{}-1,2-`1',`2'-`1',`2'- +define(`n', `$#')dnl +-n(quote(`1', `2'))-n(dquote(`1', `2'))-n(dquote_elt(`1', `2'))- +@result{}-1-1-2- +dquote(dquote_elt(`1', `2')) +@result{}``1'',``2'' +dquote_elt(dquote(`1', `2')) +@result{}``1',`2'' +@end example + +The last two lines show that when given two arguments, @code{dquote} +results in one string, while @code{dquote_elt} results in two. Now, +examine the implementation. Note that @code{quote} and +@code{dquote_elt} make decisions based on their number of arguments, so +that when called without arguments, they result in nothing instead of a +quoted empty string; this is so that it is possible to distinquish +between no arguments and an empty first argument. @code{dquote}, on the +other hand, results in a string no matter what, since it is still +possible to tell whether it was invoked without arguments based on the +resulting string. + +@example +undivert(`quote.m4')dnl +@result{}divert(`-1') +@result{}# quote(args) - convert args to single-quoted string +@result{}define(`quote', `ifelse(`$#', `0', `', ``$*'')') +@result{}# dquote(args) - convert args to quoted list of quoted strings +@result{}define(`dquote', ``$@@'') +@result{}# dquote_elt(args) - convert args to list of double-quoted strings +@result{}define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''', +@result{} ```$1'',$0(shift($@@))')') +@result{}divert`'dnl +@end example + +@node Forloop +@section Iteration by counting + +@cindex for loops +@cindex loops, counting +@cindex counting loops +Here is an example of a loop macro that implements a simple for loop. + +@deffn Composite forloop (@var{iterator}, @var{start}, @var{end}, @var{text}) +Takes the name in @var{iterator}, which must be a valid macro name, and +successively assign it each integer value from @var{start} to @var{end}, +inclusive. For each assignment to @var{iterator}, append @var{text} to +the expansion of the @code{forloop}. @var{text} may refer to +@var{iterator}. Any definition of @var{iterator} prior to this +invocation is restored. +@end deffn + +It can, for example, be used for simple counting: + +@example +include(`forloop.m4') +@result{} +forloop(`i', `1', `8', `i ') +@result{}1 2 3 4 5 6 7 8@w{ } +@end example + +For-loops can be nested, like: + +@example +include(`forloop.m4') +@result{} +forloop(`i', `1', `4', `forloop(`j', `1', `8', ` (i, j)') +') +@result{} (1, 1) (1, 2) (1, 3) (1, 4) (1, 5) (1, 6) (1, 7) (1, 8) +@result{} (2, 1) (2, 2) (2, 3) (2, 4) (2, 5) (2, 6) (2, 7) (2, 8) +@result{} (3, 1) (3, 2) (3, 3) (3, 4) (3, 5) (3, 6) (3, 7) (3, 8) +@result{} (4, 1) (4, 2) (4, 3) (4, 4) (4, 5) (4, 6) (4, 7) (4, 8) +@result{} +@end example + +The implementation of the @code{forloop} macro is fairly +straightforward. The @code{forloop} macro itself is simply a wrapper, +which saves the previous definition of the first argument, calls the +internal macro @code{@w{_forloop}}, and re-establishes the saved +definition of the first argument. + +The macro @code{@w{_forloop}} expands the fourth argument once, and +tests to see if the iterator has reached the final value. If it has +not finished, it increments the iterator (using the predefined macro +@code{incr}, @pxref{Incr}), and recurses. + +Here is an actual implementation of @code{forloop}, distributed as +@file{m4-@value{VERSION}/@/examples/@/forloop.m4} in this package: + +@example +undivert(`forloop.m4')dnl +@result{}divert(`-1') +@result{}# forloop(var, from, to, stmt) - simple version +@result{}define(`forloop', `pushdef(`$1', `$2')_forloop($@@)popdef(`$1')') +@result{}define(`_forloop', +@result{} `$4`'ifelse($1, `$3', `', `define(`$1', incr($1))$0($@@)')') +@result{}divert`'dnl +@end example + +Notice the careful use of quotes. Certain macro arguments are left +unquoted, each for its own reason. Try to find out @emph{why} these +arguments are left unquoted, and see what happens if they are quoted. +(As presented, these two macros are useful but not very robust for +general use. They lack even basic error handling for cases like +@var{start} less than @var{end}, @var{end} not numeric, or +@var{iterator} not being a macro name. See if you can improve these +macros; or @pxref{Improved forloop, , Answers}). + +@node Foreach +@section Iteration by list contents + +@cindex for each loops +@cindex loops, list iteration +@cindex iterating over lists +Here is an example of a loop macro that implements list iteration. + +@deffn Composite foreach (@var{iterator}, @var{paren-list}, @var{text}) +@deffnx Composite foreachq (@var{iterator}, @var{quote-list}, @var{text}) +Takes the name in @var{iterator}, which must be a valid macro name, and +successively assign it each value from @var{paren-list} or +@var{quote-list}. In @code{foreach}, @var{paren-list} is a +comma-separated list of elements contained in parentheses. In +@code{foreachq}, @var{quote-list} is a comma-separated list of elements +contained in a quoted string. For each assignment to @var{iterator}, +append @var{text} to the overall expansion. @var{text} may refer to +@var{iterator}. Any definition of @var{iterator} prior to this +invocation is restored. +@end deffn + +As an example, this displays each word in a list inside of a sentence, +using an implementation of @code{foreach} distributed as +@file{m4-@value{VERSION}/@/examples/@/foreach.m4}, and @code{foreachq} +in @file{m4-@value{VERSION}/@/examples/@/foreachq.m4}. + +@example +include(`foreach.m4') +@result{} +foreach(`x', (foo, bar, foobar), `Word was: x +')dnl +@result{}Word was: foo +@result{}Word was: bar +@result{}Word was: foobar +include(`foreachq.m4') +@result{} +foreachq(`x', `foo, bar, foobar', `Word was: x +')dnl +@result{}Word was: foo +@result{}Word was: bar +@result{}Word was: foobar +@end example + +It is possible to be more complex; each element of the @var{paren-list} +or @var{quote-list} can itself be a list, to pass as further arguments +to a helper macro. This example generates a shell case statement: + +@example +include(`foreach.m4') +@result{} +define(`_case', ` $1) + $2=" $1";; +')dnl +define(`_cat', `$1$2')dnl +case $`'1 in +@result{}case $1 in +foreach(`x', `(`(`a', `vara')', `(`b', `varb')', `(`c', `varc')')', + `_cat(`_case', x)')dnl +@result{} a) +@result{} vara=" a";; +@result{} b) +@result{} varb=" b";; +@result{} c) +@result{} varc=" c";; +esac +@result{}esac +@end example + +The implementation of the @code{foreach} macro is a bit more involved; +it is a wrapper around two helper macros. First, @code{@w{_arg1}} is +needed to grab the first element of a list. Second, +@code{@w{_foreach}} implements the recursion, successively walking +through the original list. Here is a simple implementation of +@code{foreach}: + +@example +undivert(`foreach.m4')dnl +@result{}divert(`-1') +@result{}# foreach(x, (item_1, item_2, ..., item_n), stmt) +@result{}# parenthesized list, simple version +@result{}define(`foreach', `pushdef(`$1')_foreach($@@)popdef(`$1')') +@result{}define(`_arg1', `$1') +@result{}define(`_foreach', `ifelse(`$2', `()', `', +@result{} `define(`$1', _arg1$2)$3`'$0(`$1', (shift$2), `$3')')') +@result{}divert`'dnl +@end example + +Unfortunately, that implementation is not robust to macro names as list +elements. Each iteration of @code{@w{_foreach}} is stripping another +layer of quotes, leading to erratic results if list elements are not +already fully expanded. The first cut at implementing @code{foreachq} +takes this into account. Also, when using quoted elements in a +@var{paren-list}, the overall list must be quoted. A @var{quote-list} +has the nice property of requiring fewer characters to create a list +containing the same quoted elements. To see the difference between the +two macros, we attempt to pass double-quoted macro names in a list, +expecting the macro name on output after one layer of quotes is removed +during list iteration and the final layer removed during the final +rescan: + +@example +define(`a', `1')define(`b', `2')define(`c', `3') +@result{} +include(`foreach.m4') +@result{} +include(`foreachq.m4') +@result{} +foreach(`x', `(``a'', ``(b'', ``c)'')', `x +') +@result{}1 +@result{}(2)1 +@result{} +@result{}, x +@result{}) +foreachq(`x', ```a'', ``(b'', ``c)''', `x +')dnl +@result{}a +@result{}(b +@result{}c) +@end example + +Obviously, @code{foreachq} did a better job; here is its implementation: + +@example +undivert(`foreachq.m4')dnl +@result{}include(`quote.m4')dnl +@result{}divert(`-1') +@result{}# foreachq(x, `item_1, item_2, ..., item_n', stmt) +@result{}# quoted list, simple version +@result{}define(`foreachq', `pushdef(`$1')_foreachq($@@)popdef(`$1')') +@result{}define(`_arg1', `$1') +@result{}define(`_foreachq', `ifelse(quote($2), `', `', +@result{} `define(`$1', `_arg1($2)')$3`'$0(`$1', `shift($2)', `$3')')') +@result{}divert`'dnl +@end example + +Notice that @code{@w{_foreachq}} had to use the helper macro +@code{quote} defined earlier (@pxref{Shift}), to ensure that the +embedded @code{ifelse} call does not go haywire if a list element +contains a comma. Unfortunately, this implementation of @code{foreachq} +has its own severe flaw. Whereas the @code{foreach} implementation was +linear, this macro is quadratic in the number of list elements, and is +much more likely to trip up the limit set by the command line option +@option{--nesting-limit} (or @option{-L}, @pxref{Limits control, , +Invoking m4}). (It is possible to have robust iteration with linear +behavior for either list style. See if you can learn from the best +elements of both of these implementations to create robust macros; or +@pxref{Improved foreach, , Answers}). + +@node Debugging +@chapter How to debug macros and input + +When writing macros for @code{m4}, they often do not work as intended on +the first try (as is the case with most programming languages). +Fortunately, there is support for macro debugging in @code{m4}. + +@menu +* Dumpdef:: Displaying macro definitions +* Trace:: Tracing macro calls +* Debug Levels:: Controlling debugging output +* Debug Output:: Saving debugging output +@end menu + +@node Dumpdef +@section Displaying macro definitions + +@cindex displaying macro definitions +@cindex macros, displaying definitions +@cindex definitions, displaying macro +If you want to see what a name expands into, you can use the builtin +@code{dumpdef}: + +@deffn Builtin dumpdef (@ovar{names@dots{}}) +Accepts any number of arguments. If called without any arguments, +it displays the definitions of all known names, otherwise it displays +the definitions of the @var{names} given. The output is printed to the +current debug file (usually standard error), and is sorted by name. If +an unknown name is encountered, a warning is printed. + +The expansion of @code{dumpdef} is void. +@end deffn + +@example +define(`foo', `Hello world.') +@result{} +dumpdef(`foo') +@error{}foo:@tabchar{}`Hello world.' +@result{} +dumpdef(`define') +@error{}define:@tabchar{} +@result{} +@end example + +The last example shows how builtin macros definitions are displayed. +The definition that is dumped corresponds to what would occur if the +macro were to be called at that point, even if other definitions are +still live due to redefining a macro during argument collection. + +@example +pushdef(`f', ``$0'1')pushdef(`f', ``$0'2') +@result{} +f(popdef(`f')dumpdef(`f')) +@error{}f:@tabchar{}``$0'1' +@result{}f2 +f(popdef(`f')dumpdef(`f')) +@error{}m4:stdin:3: undefined macro `f' +@result{}f1 +@end example + +@xref{Debug Levels}, for information on controlling the details of the +display. + +@node Trace +@section Tracing macro calls + +@cindex tracing macro expansion +@cindex macro expansion, tracing +@cindex expansion, tracing macro +It is possible to trace macro calls and expansions through the builtins +@code{traceon} and @code{traceoff}: + +@deffn Builtin traceon (@ovar{names@dots{}}) +@deffnx Builtin traceoff (@ovar{names@dots{}}) +When called without any arguments, @code{traceon} and @code{traceoff} +will turn tracing on and off, respectively, for all defined macros. + +When called with arguments, only the macros listed in @var{names} are +affected, whether or not they are currently defined. + +The expansion of @code{traceon} and @code{traceoff} is void. +@end deffn + +Whenever a traced macro is called and the arguments have been collected, +the call is displayed. If the expansion of the macro call is not void, +the expansion can be displayed after the call. The output is printed +to the current debug file (defaulting to standard error, @pxref{Debug +Output}). + +@example +define(`foo', `Hello World.') +@result{} +define(`echo', `$@@') +@result{} +traceon(`foo', `echo') +@result{} +foo +@error{}m4trace: -1- foo -> `Hello World.' +@result{}Hello World. +echo(`gnus', `and gnats') +@error{}m4trace: -1- echo(`gnus', `and gnats') -> ``gnus',`and gnats'' +@result{}gnus,and gnats +@end example + +The number between dashes is the depth of the expansion. It is one most +of the time, signifying an expansion at the outermost level, but it +increases when macro arguments contain unquoted macro calls. The +maximum number that will appear between dashes is controlled by the +option @option{--nesting-limit} (@pxref{Limits control, , Invoking m4}). + +Tracing by name is an attribute that is preserved whether the macro is +defined or not. This allows the @option{-t} option to select macros to +trace before those macros are defined. + +@example +traceoff(`foo') +@result{} +traceon(`foo') +@result{} +foo +@result{}foo +define(`foo', `bar') +@result{} +foo +@error{}m4trace: -1- foo -> `bar' +@result{}bar +undefine(`foo') +@result{} +ifdef(`foo', `yes', `no') +@result{}no +indir(`foo') +@error{}m4:stdin:8: undefined macro `foo' +@result{} +define(`foo', `blah') +@result{} +foo +@error{}m4trace: -1- foo -> `blah' +@result{}blah +traceoff +@result{} +foo +@result{}blah +@end example + +Tracing even works on builtins. However, @command{defn} (@pxref{Defn}) +does not transfer tracing status. + +@example +traceon(`eval', `m4_divnum') +@result{} +define(`m4_eval', defn(`eval')) +@result{} +define(`m4_divnum', defn(`divnum')) +@result{} +eval(divnum) +@error{}m4trace: -1- eval(`0') -> `0' +@result{}0 +m4_eval(m4_divnum) +@error{}m4trace: -2- m4_divnum -> `0' +@result{}0 +@end example + +@xref{Debug Levels}, for information on controlling the details of the +display. + +@node Debug Levels +@section Controlling debugging output + +@cindex controlling debugging output +@cindex debugging output, controlling +The @option{-d} option to @code{m4} (@pxref{Debugging options, , +Invoking m4}) controls the amount of details presented, when using the +macros described in the preceding sections. + +The @var{flags} following the option can be one or more of the +following: + +@table @code +@item a +Show the actual arguments in each macro call. This applies to all macro +calls if the @samp{t} flag is used, otherwise only the macros covered by +calls of @code{traceon}. + +@item c +Show several trace lines for each macro call. A line is shown when the +macro is seen, but before the arguments are collected; a second line +when the arguments have been collected and a third line after the call +has completed. + +@item e +Show the expansion of each macro call, if it is not void. This applies +to all macro calls if the @samp{t} flag is used, otherwise only the +macros covered by calls of @code{traceon}. + +@item f +Show the name of the current input file in each trace output line. + +@item i +Print a message each time the current input file is changed, giving file +name and input line number. + +@item l +Show the current input line number in each trace output line. + +@item p +Print a message when a named file is found through the path search +mechanism (@pxref{Search Path}), giving the actual file name used. + +@item q +Quote actual arguments and macro expansions in the display with the +current quotes. + +@item t +Trace all macro calls made in this invocation of @code{m4}. + +@item x +Add a unique `macro call id' to each line of the trace output. This is +useful in connection with the @samp{c} flag above. + +@item V +A shorthand for all of the above flags. +@end table + +If no flags are specified with the @option{-d} option, the default is +@samp{aeq}. The examples throughout this manual assume the default +flags. + +@cindex @acronym{GNU} extensions +There is a builtin macro @code{debugmode}, which allows on-the-fly control of +the debugging output format: + +@deffn Builtin debugmode (@ovar{flags}) +The argument @var{flags} should be a subset of the letters listed above. +As special cases, if the argument starts with a @samp{+}, the flags are +added to the current debug flags, and if it starts with a @samp{-}, they +are removed. If no argument is present, all debugging flags are cleared +(as if no @option{-d} was given), and with an empty argument the flags +are reset to the default of @samp{aeq}. + +The expansion of @code{debugmode} is void. +@end deffn + +@example +define(`foo', `FOO') +@result{} +traceon(`foo') +@result{} +debugmode() +@result{} +foo +@error{}m4trace: -1- foo -> `FOO' +@result{}FOO +debugmode +@result{} +foo +@error{}m4trace: -1- foo +@result{}FOO +debugmode(`+l') +@result{} +foo +@error{}m4trace:8: -1- foo +@result{}FOO +@end example + +@node Debug Output +@section Saving debugging output + +@cindex saving debugging output +@cindex debugging output, saving +@cindex output, saving debugging +@cindex @acronym{GNU} extensions +Debug and tracing output can be redirected to files using either the +@option{--debugfile} option to @code{m4} (@pxref{Debugging options, , +Invoking m4}), or with the builtin macro @code{debugfile}: + +@deffn Builtin debugfile (@ovar{file}) +Sends all further debug and trace output to @var{file}, opened in append +mode. If @var{file} is the empty string, debug and trace output are +discarded. If @code{debugfile} is called without any arguments, debug +and trace output are sent to standard error. This does not affect +warnings, error messages, or @code{errprint} output, which are +always sent to standard error. If @var{file} cannot be opened, the +current debug file is unchanged, and an error is issued. + +The expansion of @code{debugfile} is void. +@end deffn + +@example +traceon(`divnum') +@result{} +divnum(`extra') +@error{}m4:stdin:2: Warning: excess arguments to builtin `divnum' ignored +@error{}m4trace: -1- divnum(`extra') -> `0' +@result{}0 +debugfile() +@result{} +divnum(`extra') +@error{}m4:stdin:4: Warning: excess arguments to builtin `divnum' ignored +@result{}0 +debugfile +@result{} +divnum +@error{}m4trace: -1- divnum -> `0' +@result{}0 +@end example + +@node Input Control +@chapter Input control + +This chapter describes various builtin macros for controlling the input +to @code{m4}. + +@menu +* Dnl:: Deleting whitespace in input +* Changequote:: Changing the quote characters +* Changecom:: Changing the comment delimiters +* Changeword:: Changing the lexical structure of words +* M4wrap:: Saving text until end of input +@end menu + +@node Dnl +@section Deleting whitespace in input + +@cindex deleting whitespace in input +The builtin @code{dnl} stands for ``Discard to Next Line'': + +@deffn Builtin dnl +All characters, up to and including the next newline, are discarded +without performing any macro expansion. A warning is issued if the end +of the file is encountered without a newline. + +The expansion of @code{dnl} is void. +@end deffn + +It is often used in connection with @code{define}, to remove the +newline that follows the call to @code{define}. Thus + +@example +define(`foo', `Macro `foo'.')dnl A very simple macro, indeed. +foo +@result{}Macro foo. +@end example + +The input up to and including the next newline is discarded, as opposed +to the way comments are treated (@pxref{Comments}). + +Usually, @code{dnl} is immediately followed by an end of line or some +other whitespace. @acronym{GNU} @code{m4} will produce a warning diagnostic if +@code{dnl} is followed by an open parenthesis. In this case, @code{dnl} +will collect and process all arguments, looking for a matching close +parenthesis. All predictable side effects resulting from this +collection will take place. @code{dnl} will return no output. The +input following the matching close parenthesis up to and including the +next newline, on whatever line containing it, will still be discarded. + +@example +dnl(`args are ignored, but side effects occur', +define(`foo', `like this')) while this text is ignored: undefine(`foo') +@error{}m4:stdin:1: Warning: excess arguments to builtin `dnl' ignored +See how `foo' was defined, foo? +@result{}See how foo was defined, like this? +@end example + +If the end of file is encountered without a newline character, a +warning is issued and dnl stops consuming input. + +@example +m4wrap(`m4wrap(`2 hi +')0 hi dnl 1 hi') +@result{} +define(`hi', `HI') +@result{} +^D +@error{}m4:stdin:1: Warning: end of file treated as newline +@result{}0 HI 2 HI +@end example + +@node Changequote +@section Changing the quote characters + +@cindex changing the quote delimiters +@cindex quote delimiters, changing the +The default quote delimiters can be changed with the builtin +@code{changequote}: + +@deffn Builtin changequote (@dvar{start, `}, @dvar{end, '}) +This sets @var{start} as the new begin-quote delimiter and @var{end} as +the new end-quote delimiter. If both arguments are missing, the default +quotes (@code{`} and @code{'}) are used. If @var{start} is void, then +quoting is disabled. Otherwise, if @var{end} is missing or void, the +default end-quote delimiter (@code{'}) is used. The quote delimiters +can be of any length. + +The expansion of @code{changequote} is void. +@end deffn + +@example +changequote(`[', `]') +@result{} +define([foo], [Macro [foo].]) +@result{} +foo +@result{}Macro foo. +@end example + +The quotation strings can safely contain eight-bit characters. +@ignore +@comment Yuck. I know of no clean way to render an 8-bit character in +@comment both info and dvi. This example uses the `open-guillemot' and +@comment `close-guillemot' characters of the Latin-1 character set. + +@example +define(`a', `b') +@result{} +«a» +@result{}«b» +changequote(`«', `»') +@result{} +«a» +@result{}a +@end example +@end ignore +If no single character is appropriate, @var{start} and @var{end} can be +of any length. Other implementations cap the delimiter length to five +characters, but @acronym{GNU} has no inherent limit. + +@example +changequote(`[[[', `]]]') +@result{} +define([[[foo]]], [[[Macro [[[[[foo]]]]].]]]) +@result{} +foo +@result{}Macro [[foo]]. +@end example + +Calling @code{changequote} with @var{start} as the empty string will +effectively disable the quoting mechanism, leaving no way to quote text. +However, using an empty string is not portable, as some other +implementations of @code{m4} revert to the default quoting, while others +preserve the prior non-empty delimiter. If @var{start} is not empty, +then an empty @var{end} will use the default end-quote delimiter of +@samp{'}, as otherwise, it would be impossible to end a quoted string. +Again, this is not portable, as some other @code{m4} implementations +reuse @var{start} as the end-quote delimiter, while others preserve the +previous non-empty value. Omitting both arguments restores the default +begin-quote and end-quote delimiters; fortunately this behavior is +portable to all implementations of @code{m4}. + +@example +define(`foo', `Macro `FOO'.') +@result{} +changequote(`', `') +@result{} +foo +@result{}Macro `FOO'. +`foo' +@result{}`Macro `FOO'.' +changequote(`,) +@result{} +foo +@result{}Macro FOO. +@end example + +There is no way in @code{m4} to quote a string containing an unmatched +begin-quote, except using @code{changequote} to change the current +quotes. + +If the quotes should be changed from, say, @samp{[} to @samp{[[}, +temporary quote characters have to be defined. To achieve this, two +calls of @code{changequote} must be made, one for the temporary quotes +and one for the new quotes. + +Macros are recognized in preference to the begin-quote string, so if a +prefix of @var{start} can be recognized as part of a potential macro +name, the quoting mechanism is effectively disabled. Unless you use +@code{changeword} (@pxref{Changeword}), this means that @var{start} +should not begin with a letter, digit, or @samp{_} (underscore). +However, even though quoted strings are not recognized, the quote +characters can still be discerned in macro expansion and in trace +output. + +@example +define(`echo', `$@@') +@result{} +define(`hi', `HI') +@result{} +changequote(`q', `Q') +@result{} +q hi Q hi +@result{}q HI Q HI +echo(hi) +@result{}qHIQ +changequote +@result{} +changequote(`-', `EOF') +@result{} +- hi EOF hi +@result{} hi HI +changequote +@result{} +changequote(`1', `2') +@result{} +hi1hi2 +@result{}hi1hi2 +hi 1hi2 +@result{}HI hi +@end example + +Quotes are recognized in preference to argument collection. In +particular, if @var{start} is a single @samp{(}, then argument +collection is effectively disabled. For portability with other +implementations, it is a good idea to avoid @samp{(}, @samp{,}, and +@samp{)} as the first character in @var{start}. + +@example +define(`echo', `$#:$@@:') +@result{} +define(`hi', `HI') +@result{} +changequote(`(',`)') +@result{} +echo(hi) +@result{}0::hi +changequote +@result{} +changequote(`((', `))') +@result{} +echo(hi) +@result{}1:HI: +echo((hi)) +@result{}0::hi +changequote +@result{} +changequote(`,', `)') +@result{} +echo(hi,hi)bye) +@result{}1:HIhibye: +@end example + +If @var{end} is a prefix of @var{start}, the end-quote will be +recognized in preference to a nested begin-quote. In particular, +changing the quotes to have the same string for @var{start} and +@var{end} disables nesting of quotes. When quote nesting is disabled, +it is impossible to double-quote strings across macro expansions, so +using the same string is not done very often. + +@example +define(`hi', `HI') +@result{} +changequote(`""', `"') +@result{} +""hi"""hi" +@result{}hihi +""hi" ""hi" +@result{}hi hi +""hi"" "hi" +@result{}hi" "HI" +changequote +@result{} +`hi`hi'hi' +@result{}hi`hi'hi +changequote(`"', `"') +@result{} +"hi"hi"hi" +@result{}hiHIhi +@end example + +It is an error if the end of file occurs within a quoted string. + +@example +`hello world' +@result{}hello world +`dangling quote +^D +@error{}m4:stdin:2: ERROR: end of file in string +@end example + +@node Changecom +@section Changing the comment delimiters + +@cindex changing comment delimiters +@cindex comment delimiters, changing +The default comment delimiters can be changed with the builtin +macro @code{changecom}: + +@deffn Builtin changecom (@ovar{start}, @dvar{end, @key{NL}}) +This sets @var{start} as the new begin-comment delimiter and @var{end} +as the new end-comment delimiter. If both arguments are missing, or +@var{start} is void, then comments are disabled. Otherwise, if +@var{end} is missing or void, the default end-comment delimiter of +newline is used. The comment delimiters can be of any length. + +The expansion of @code{changecom} is void. +@end deffn + +@example +define(`comment', `COMMENT') +@result{} +# A normal comment +@result{}# A normal comment +changecom(`/*', `*/') +@result{} +# Not a comment anymore +@result{}# Not a COMMENT anymore +But: /* this is a comment now */ while this is not a comment +@result{}But: /* this is a comment now */ while this is not a COMMENT +@end example + +@cindex comments, copied to output +Note how comments are copied to the output, much as if they were quoted +strings. If you want the text inside a comment expanded, quote the +begin-comment delimiter. + +Calling @code{changecom} without any arguments, or with @var{start} as +the empty string, will effectively disable the commenting mechanism. To +restore the original comment start of @samp{#}, you must explicitly ask +for it. If @var{start} is not empty, then an empty @var{end} will use +the default end-comment delimiter of newline, as otherwise, it would be +impossible to end a comment. However, this is not portable, as some +other @code{m4} implementations preserve the previous non-empty +delimiters instead. + +@example +define(`comment', `COMMENT') +@result{} +changecom +@result{} +# Not a comment anymore +@result{}# Not a COMMENT anymore +changecom(`#', `') +@result{} +# comment again +@result{}# comment again +@end example + +The comment strings can safely contain eight-bit characters. +@ignore +@comment Yuck. I know of no clean way to render an 8-bit character in +@comment both info and dvi. This example uses the `open-guillemot' and +@comment `close-guillemot' characters of the Latin-1 character set. + +@example +define(`a', `b') +@result{} +«a» +@result{}«b» +changecom(`«', `»') +@result{} +«a» +@result{}«a» +@end example +@end ignore +If no single character is appropriate, @var{start} and @var{end} can be +of any length. Other implementations cap the delimiter length to five +characters, but @acronym{GNU} has no inherent limit. + +Comments are recognized in preference to macros. However, this is not +compatible with other implementations, where macros and even quoting +takes precedence over comments, so it may change in a future release. +For portability, this means that @var{start} should not begin with a +letter, digit, or @samp{_} (underscore), and that neither the +start-quote nor the start-comment string should be a prefix of the +other. + +@example +define(`hi', `HI') +@result{} +define(`hi1hi2', `hello') +@result{} +changecom(`q', `Q') +@result{} +q hi Q hi +@result{}q hi Q HI +changecom(`1', `2') +@result{} +hi1hi2 +@result{}hello +hi 1hi2 +@result{}HI 1hi2 +@end example + +Comments are recognized in preference to argument collection. In +particular, if @var{start} is a single @samp{(}, then argument +collection is effectively disabled. For portability with other +implementations, it is a good idea to avoid @samp{(}, @samp{,}, and +@samp{)} as the first character in @var{start}. + +@example +define(`echo', `$#:$@@:') +@result{} +define(`hi', `HI') +@result{} +changecom(`(',`)') +@result{} +echo(hi) +@result{}0::(hi) +changecom +@result{} +changecom(`((', `))') +@result{} +echo(hi) +@result{}1:HI: +echo((hi)) +@result{}0::((hi)) +changecom(`,', `)') +@result{} +echo(hi,hi)bye) +@result{}1:HI,hi)bye: +@end example + +It is an error if the end of file occurs within a comment. + +@example +changecom(`/*', `*/') +@result{} +/*dangling comment +^D +@error{}m4:stdin:2: ERROR: end of file in comment +@end example + +@node Changeword +@section Changing the lexical structure of words + +@cindex lexical structure of words +@cindex words, lexical structure of +@quotation +The macro @code{changeword} and all associated functionality is +experimental. It is only available if the @option{--enable-changeword} +option was given to @code{configure}, at @acronym{GNU} @code{m4} installation +time. The functionality will go away in the future, to be replaced by +other new features that are more efficient at providing the same +capabilities. @emph{Do not rely on it}. Please direct your comments +about it the same way you would do for bugs. +@end quotation + +A file being processed by @code{m4} is split into quoted strings, words +(potential macro names) and simple tokens (any other single character). +Initially a word is defined by the following regular expression: + +@comment ignore +@example +[_a-zA-Z][_a-zA-Z0-9]* +@end example + +Using @code{changeword}, you can change this regular expression: + +@deffn {Optional builtin} changeword (@var{regex}) +Changes the regular expression for recognizing macro names to be +@var{regex}. If @var{regex} is empty, use +@samp{[_a-zA-Z][_a-zA-Z0-9]*}. @var{regex} must obey the constraint +that every prefix of the desired final pattern is also accepted by the +regular expression. If @var{regex} contains grouping parentheses, the +macro invoked is the portion that matched the first group, rather than +the entire matching string. + +The expansion of @code{changeword} is void. +The macro @code{changeword} is recognized only with parameters. +@end deffn + +Relaxing the lexical rules of @code{m4} might be useful (for example) if +you wanted to apply translations to a file of numbers: + +@example +ifdef(`changeword', `', `errprint(` skipping: no changeword support +')m4exit(`77')')dnl +changeword(`[_a-zA-Z0-9]+') +@result{} +define(`1', `0')1 +@result{}0 +@end example + +Tightening the lexical rules is less useful, because it will generally +make some of the builtins unavailable. You could use it to prevent +accidental call of builtins, for example: + +@example +ifdef(`changeword', `', `errprint(` skipping: no changeword support +')m4exit(`77')')dnl +define(`_indir', defn(`indir')) +@result{} +changeword(`_[_a-zA-Z0-9]*') +@result{} +esyscmd(`foo') +@result{}esyscmd(foo) +_indir(`esyscmd', `echo hi') +@result{}hi +@result{} +@end example + +Because @code{m4} constructs its words a character at a time, there +is a restriction on the regular expressions that may be passed to +@code{changeword}. This is that if your regular expression accepts +@samp{foo}, it must also accept @samp{f} and @samp{fo}. + +@example +ifdef(`changeword', `', `errprint(` skipping: no changeword support +')m4exit(`77')')dnl +define(`foo +', `bar +') +@result{} +dnl This example wants to recognize changeword, dnl, and `foo\n'. +dnl First, we check that our regexp will match. +regexp(`changeword', `[cd][a-z]*\|foo[ +]') +@result{}0 +regexp(`foo +', `[cd][a-z]*\|foo[ +]') +@result{}0 +regexp(`f', `[cd][a-z]*\|foo[ +]') +@result{}-1 +foo +@result{}foo +changeword(`[cd][a-z]*\|foo[ +]') +@result{} +dnl Even though `foo\n' matches, we forgot to allow `f'. +foo +@result{}foo +changeword(`[cd][a-z]*\|fo*[ +]?') +@result{} +dnl Now we can call `foo\n'. +foo +@result{}bar +@end example + +@ignore +@comment One more test of including newline in a macro name; but this +@comment does not need to be displayed in the manual. This ensures +@comment that line numbering is correct when dnl cuts across include +@comment file boundaries, and when __file__ or __line__ is the last +@comment token in an include file. + +@example +ifdef(`changeword', `', `errprint(` skipping: no changeword support +')m4exit(`77')')dnl +define(`bar +', defn(`dnl'))dnl +define(`baz', `dnl +include(`foo') ignored +dnl')dnl +changeword(`\([_a-zA-Z][_a-zA-Z0-9]*\|bar +\)') +@result{} +__file__:__line__ +@result{}stdin:10 +include(`foo') ignored +__file__:__line__ +@result{}stdin:12 +baz ignored +__file__:__line__ +@result{}stdin:14 +define(`bar +', defn(`__file__')) +@result{} +include(`foo') +@result{}../examples/foo +define(`bar +', defn(`__line__')) +@result{} +include(`foo') +@result{}1 +__file__:__line__ +@result{}stdin:21 +@end example +@end ignore + +@code{changeword} has another function. If the regular expression +supplied contains any grouped subexpressions, then text outside +the first of these is discarded before symbol lookup. So: + +@example +ifdef(`changeword', `', `errprint(` skipping: no changeword support +')m4exit(`77')')dnl +ifdef(`__unix__', , + `errprint(` skipping: syscmd does not have unix semantics +')m4exit(`77')')dnl +changecom(`/*', `*/')dnl +define(`foo', `bar')dnl +changeword(`#\([_a-zA-Z0-9]*\)') +@result{} +#esyscmd(`echo foo \#foo') +@result{}foo bar +@result{} +@end example + +@code{m4} now requires a @samp{#} mark at the beginning of every +macro invocation, so one can use @code{m4} to preprocess plain +text without losing various words like @samp{divert}. + +In @code{m4}, macro substitution is based on text, while in @TeX{}, it +is based on tokens. @code{changeword} can throw this difference into +relief. For example, here is the same idea represented in @TeX{} and +@code{m4}. First, the @TeX{} version: + +@comment ignore +@example +\def\a@{\message@{Hello@}@} +\catcode`\@@=0 +\catcode`\\=12 +@@a +@@bye +@result{}Hello +@end example + +@noindent +Then, the @code{m4} version: + +@example +ifdef(`changeword', `', `errprint(` skipping: no changeword support +')m4exit(`77')')dnl +define(`a', `errprint(`Hello')')dnl +changeword(`@@\([_a-zA-Z0-9]*\)') +@result{} +@@a +@result{}errprint(Hello) +@end example + +In the @TeX{} example, the first line defines a macro @code{a} to +print the message @samp{Hello}. The second line defines @key{@@} to +be usable instead of @key{\} as an escape character. The third line +defines @key{\} to be a normal printing character, not an escape. +The fourth line invokes the macro @code{a}. So, when @TeX{} is run +on this file, it displays the message @samp{Hello}. + +When the @code{m4} example is passed through @code{m4}, it outputs +@samp{errprint(Hello)}. The reason for this is that @TeX{} does +lexical analysis of macro definition when the macro is @emph{defined}. +@code{m4} just stores the text, postponing the lexical analysis until +the macro is @emph{used}. + +You should note that using @code{changeword} will slow @code{m4} down +by a factor of about seven, once it is changed to something other +than the default regular expression. You can invoke @code{changeword} +with the empty string to restore the default word definition, and regain +the parsing speed. + +@node M4wrap +@section Saving text until end of input + +@cindex saving input +@cindex input, saving +It is possible to `save' some text until the end of the normal input has +been seen. Text can be saved, to be read again by @code{m4} when the +normal input has been exhausted. This feature is normally used to +initiate cleanup actions before normal exit, e.g., deleting temporary +files. + +To save input text, use the builtin @code{m4wrap}: + +@deffn Builtin m4wrap (@var{string}, @dots{}) +Stores @var{string} in a safe place, to be reread when end of input is +reached. As a @acronym{GNU} extension, additional arguments are +concatenated with a space to the @var{string}. + +The expansion of @code{m4wrap} is void. +The macro @code{m4wrap} is recognized only with parameters. +@end deffn + +@example +define(`cleanup', `This is the `cleanup' action. +') +@result{} +m4wrap(`cleanup') +@result{} +This is the first and last normal input line. +@result{}This is the first and last normal input line. +^D +@result{}This is the cleanup action. +@end example + +The saved input is only reread when the end of normal input is seen, and +not if @code{m4exit} is used to exit @code{m4}. + +@comment FIXME: this contradicts POSIX, which requires that "If the +@comment m4wrap macro is used multiple times, the arguments specified +@comment shall be processed in the order in which the m4wrap macros were +@comment processed." +It is safe to call @code{m4wrap} from saved text, but then the order in +which the saved text is reread is undefined. If @code{m4wrap} is not used +recursively, the saved pieces of text are reread in the opposite order +in which they were saved (LIFO---last in, first out). However, this +behavior is likely to change in a future release, to match +@acronym{POSIX}, so you should not depend on this order. + +Here is an example of implementing a factorial function using +@code{m4wrap}: + +@example +define(`f', `ifelse(`$1', `0', `Answer: 0!=1 +', eval(`$1>1'), `0', `Answer: $2$1=eval(`$2$1') +', `m4wrap(`f(decr(`$1'), `$2$1*')')')') +@result{} +f(`10') +@result{} +^D +@result{}Answer: 10*9*8*7*6*5*4*3*2*1=3628800 +@end example + +Invocations of @code{m4wrap} at the same recursion level are +concatenated and rescanned as usual: + +@example +define(`aa', `AA +') +@result{} +m4wrap(`a')m4wrap(`a') +@result{} +^D +@result{}AA +@end example + +@noindent +however, the transition between recursion levels behaves like an end of +file condition between two input files. + +@example +m4wrap(`m4wrap(`)')len(abc') +@result{} +^D +@error{}m4:stdin:1: ERROR: end of file in argument list +@end example + +@node File Inclusion +@chapter File inclusion + +@cindex file inclusion +@cindex inclusion, of files + +@code{m4} allows you to include named files at any point in the input. + +@menu +* Include:: Including named files +* Search Path:: Searching for include files +@end menu + +@node Include +@section Including named files + +There are two builtin macros in @code{m4} for including files: + +@deffn Builtin include (@var{file}) +@deffnx Builtin sinclude (@var{file}) +Both macros cause the file named @var{file} to be read by +@code{m4}. When the end of the file is reached, input is resumed from +the previous input file. + +The expansion of @code{include} and @code{sinclude} is therefore the +contents of @var{file}. + +If @var{file} does not exist (or cannot be read), the expansion is void, +and @code{include} will fail with an error while @code{sinclude} is +silent. The empty string counts as a file that does not exist. + +The macros @code{include} and @code{sinclude} are recognized only with +parameters. +@end deffn + +@example +include(`none') +@error{}m4:stdin:1: cannot open `none': No such file or directory +@result{} +include() +@error{}m4:stdin:2: cannot open `': No such file or directory +@result{} +sinclude(`none') +@result{} +sinclude() +@result{} +@end example + +The rest of this section assumes that @code{m4} is invoked with the +@option{-I} option (@pxref{Preprocessor features, , Invoking m4}) +pointing to the @file{m4-@value{VERSION}/@/examples} +directory shipped as part of the @acronym{GNU} @code{m4} package. The +file @file{m4-@value{VERSION}/@/examples/@/incl.m4} in the distribution +contains the lines: + +@comment ignore +@example +Include file start +foo +Include file end +@end example + +Normally file inclusion is used to insert the contents of a file +into the input stream. The contents of the file will be read by +@code{m4} and macro calls in the file will be expanded: + +@example +define(`foo', `FOO') +@result{} +include(`incl.m4') +@result{}Include file start +@result{}FOO +@result{}Include file end +@result{} +@end example + +The fact that @code{include} and @code{sinclude} expand to the contents +of the file can be used to define macros that operate on entire files. +Here is an example, which defines @samp{bar} to expand to the contents +of @file{incl.m4}: + +@example +define(`bar', include(`incl.m4')) +@result{} +This is `bar': >>bar<< +@result{}This is bar: >>Include file start +@result{}foo +@result{}Include file end +@result{}<< +@end example + +This use of @code{include} is not trivial, though, as files can contain +quotes, commas, and parentheses, which can interfere with the way the +@code{m4} parser works. @acronym{GNU} @code{m4} seamlessly concatenates +the file contents with the next character, even if the included file +ended in the middle of a comment, string, or macro call. These +conditions are only treated as end of file errors if specified as input +files on the command line. + +In @acronym{GNU} @code{m4}, an alternative method of reading files is +using @code{undivert} (@pxref{Undivert}) on a named file. + +@node Search Path +@section Searching for include files + +@cindex search path for included files +@cindex included files, search path for +@cindex @acronym{GNU} extensions +@acronym{GNU} @code{m4} allows included files to be found in other directories +than the current working directory. + +If the @option{--prepend-include} or @option{-B} command-line option was +provided (@pxref{Preprocessor features, , Invoking m4}), those +directories are searched first, in reverse order that those options were +listed on the command line. Then @code{m4} looks in the current working +directory. Next comes the directories specified with the +@option{--include} or @option{-I} option, in the order found on the +command line. Finally, if the @env{M4PATH} environment variable is set, +it is expected to contain a colon-separated list of directories, which +will be searched in order. + +If the automatic search for include-files causes trouble, the @samp{p} +debug flag (@pxref{Debug Levels}) can help isolate the problem. + +@node Diversions +@chapter Diverting and undiverting output + +Diversions are a way of temporarily saving output. The output of +@code{m4} can at any time be diverted to a temporary file, and be +reinserted into the output stream, @dfn{undiverted}, again at a later +time. + +Numbered diversions are counted from 0 upwards, diversion number 0 +being the normal output stream. The number of simultaneous diversions +is limited mainly by the memory used to describe them, because @acronym{GNU} +@code{m4} tries to keep diversions in memory. However, there is a +limit to the overall memory usable by all diversions taken altogether +(512K, currently). When this maximum is about to be exceeded, +a temporary file is opened to receive the contents of the biggest +diversion still in memory, freeing this memory for other diversions. +When creating the temporary file, @code{m4} honors the value of the +environment variable @env{TMPDIR}, and falls back to @file{/tmp}. +So, it is theoretically possible that the number and aggregate size of +diversions is limited only by available disk space. + +@ignore +@comment We need to test spilled diversions, but don't need to expose +@comment this highly repetitive test in the manual. + +@example +divert(`-1')define(`f', `.') +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +divert`'dnl +len(f) +@result{}1048576 +divert(`1') +f +divert(`-1')undivert +@end example + +@comment Another test of spilled diversions. + +@example +divert(`-1')define(`f', `.') +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +define(`f', defn(`f')defn(`f')) +divert`'dnl +len(f) +@result{}1048576 +divert(`1') +f +m4exit +@end example +@end ignore + +Diversions make it possible to generate output in a different order than +the input was read. It is possible to implement topological sorting +dependencies. For example, @acronym{GNU} Autoconf makes use of +diversions under the hood to ensure that the expansion of a prerequisite +macro appears in the output prior to the expansion of a dependent macro, +regardless of which order the two macros were invoked in the user's +input file. + +@menu +* Divert:: Diverting output +* Undivert:: Undiverting output +* Divnum:: Diversion numbers +* Cleardivert:: Discarding diverted text +@end menu + +@node Divert +@section Diverting output + +@cindex diverting output to files +@cindex output, diverting to files +@cindex files, diverting output to +Output is diverted using @code{divert}: + +@deffn Builtin divert (@dvar{number, 0}) +The current diversion is changed to @var{number}. If @var{number} is left +out or empty, it is assumed to be zero. If @var{number} cannot be +parsed, the diversion is unchanged. + +The expansion of @code{divert} is void. +@end deffn + +When all the @code{m4} input will have been processed, all existing +diversions are automatically undiverted, in numerical order. + +@example +divert(`1') +This text is diverted. +divert +@result{} +This text is not diverted. +@result{}This text is not diverted. +^D +@result{} +@result{}This text is diverted. +@end example + +Several calls of @code{divert} with the same argument do not overwrite +the previous diverted text, but append to it. Diversions are printed +after any wrapped text is expanded. + +@example +define(`text', `TEXT') +@result{} +divert(`1')`diverted text.' +divert +@result{} +m4wrap(`Wrapped text preceeds ') +@result{} +^D +@result{}Wrapped TEXT preceeds diverted text. +@end example + +If output is diverted to a negative diversion, it is simply discarded. +This can be used to suppress unwanted output. A common example of +unwanted output is the trailing newlines after macro definitions. Here +is a common programming idiom in @code{m4} for avoiding them. + +@example +divert(`-1') +define(`foo', `Macro `foo'.') +define(`bar', `Macro `bar'.') +divert +@result{} +@end example + +@cindex @acronym{GNU} extensions +Traditional implementations only supported ten diversions. But as a +@acronym{GNU} extension, diversion numbers can be as large as positive +integers will allow, rather than treating a multi-digit diversion number +as a request to discard text. + +@example +divert(eval(`1<<28'))world +divert(`2')hello +^D +@result{}hello +@result{}world +@end example + +Note that @code{divert} is an English word, but also an active macro +without arguments. When processing plain text, the word might appear in +normal text and be unintentionally swallowed as a macro invocation. One +way to avoid this is to use the @option{-P} option to rename all +builtins (@pxref{Operation modes, , Invoking m4}). Another is to write +a wrapper that requires a parameter to be recognized. + +@example +We decided to divert the stream for irrigation. +@result{}We decided to the stream for irrigation. +define(`divert', `ifelse(`$#', `0', ``$0'', `builtin(`$0', $@@)')') +@result{} +divert(`-1') +Ignored text. +divert(`0') +@result{} +We decided to divert the stream for irrigation. +@result{}We decided to divert the stream for irrigation. +@end example + +@node Undivert +@section Undiverting output + +Diverted text can be undiverted explicitly using the builtin +@code{undivert}: + +@deffn Builtin undivert (@ovar{diversions@dots{}}) +Undiverts the numeric @var{diversions} given by the arguments, in the +order given. If no arguments are supplied, all diversions are +undiverted, in numerical order. + +As a @acronym{GNU} extension, @var{diversions} may contain non-numeric +strings, which are treated as the names of files to copy into the output +without expansion. A warning is issued if a file could not be opened. + +The expansion of @code{undivert} is void. +@end deffn + +@example +divert(`1') +This text is diverted. +divert +@result{} +This text is not diverted. +@result{}This text is not diverted. +undivert(`1') +@result{} +@result{}This text is diverted. +@result{} +@end example + +Notice the last two blank lines. One of them comes from the newline +following @code{undivert}, the other from the newline that followed the +@code{divert}! A diversion often starts with a blank line like this. + +When diverted text is undiverted, it is @emph{not} reread by @code{m4}, +but rather copied directly to the current output, and it is therefore +not an error to undivert into a diversion. Undiverting the empty string +is the same as specifying diversion 0; in either case nothing happens +since the output has already been flushed. + +@example +divert(`1')diverted text +divert +@result{} +undivert() +@result{} +undivert(`0') +@result{} +undivert +@result{}diverted text +@result{} +@end example + +When a diversion has been undiverted, the diverted text is discarded, +and it is not possible to bring back diverted text more than once. + +@example +divert(`1') +This text is diverted first. +divert(`0')undivert(`1')dnl +@result{} +@result{}This text is diverted first. +undivert(`1') +@result{} +divert(`1') +This text is also diverted but not appended. +divert(`0')undivert(`1')dnl +@result{} +@result{}This text is also diverted but not appended. +@end example + +Attempts to undivert the current diversion are silently ignored. Thus, +when the current diversion is not 0, the current diversion does not get +rearranged among the other diversions. + +@example +divert(`1')one +divert(`2')two +divert(`3')three +divert(`2')undivert`'dnl +divert`'undivert`'dnl +@result{}two +@result{}one +@result{}three +@end example + +@cindex @acronym{GNU} extensions +@cindex file inclusion +@cindex inclusion, of files +@acronym{GNU} @code{m4} allows named files to be undiverted. Given a +non-numeric +argument, the contents of the file named will be copied, uninterpreted, to +the current output. This complements the builtin @code{include} +(@pxref{Include}). To illustrate the difference, the file +@file{m4-@value{VERSION}/@/examples/@/foo} contains the word @samp{bar}: + +@example +define(`bar', `BAR') +@result{} +undivert(`foo') +@result{}bar +@result{} +include(`foo') +@result{}BAR +@result{} +@end example + +If the file is not found (or cannot be read), an error message is +issued, and the expansion is void. + +@node Divnum +@section Diversion numbers + +@cindex diversion numbers +The current diversion is tracked by the builtin @code{divnum}: + +@deffn Builtin divnum +Expands to the number of the current diversion. +@end deffn + +@example +Initial divnum +@result{}Initial 0 +divert(`1') +Diversion one: divnum +divert(`2') +Diversion two: divnum +^D +@result{} +@result{}Diversion one: 1 +@result{} +@result{}Diversion two: 2 +@end example + +@node Cleardivert +@section Discarding diverted text + +@cindex discarding diverted text +@cindex diverted text, discarding +Often it is not known, when output is diverted, whether the diverted +text is actually needed. Since all non-empty diversion are brought back +on the main output stream when the end of input is seen, a method of +discarding a diversion is needed. If all diversions should be +discarded, the easiest is to end the input to @code{m4} with +@samp{divert(`-1')} followed by an explicit @samp{undivert}: + +@example +divert(`1') +Diversion one: divnum +divert(`2') +Diversion two: divnum +divert(`-1') +undivert +^D +@end example + +@noindent +No output is produced at all. + +Clearing selected diversions can be done with the following macro: + +@deffn Composite cleardivert (@ovar{diversions@dots{}}) +Discard the contents of each of the listed numeric @var{diversions}. +@end deffn + +@example +define(`cleardivert', +`pushdef(`_n', divnum)divert(`-1')undivert($@@)divert(_n)popdef(`_n')') +@result{} +@end example + +It is called just like @code{undivert}, but the effect is to clear the +diversions, given by the arguments. (This macro has a nasty bug! You +should try to see if you can find it and correct it; or @pxref{Improved +cleardivert, , Answers}). + +@node Text handling +@chapter Macros for text handling + +There are a number of builtins in @code{m4} for manipulating text in +various ways, extracting substrings, searching, substituting, and so on. + +@menu +* Len:: Calculating length of strings +* Index macro:: Searching for substrings +* Regexp:: Searching for regular expressions +* Substr:: Extracting substrings +* Translit:: Translating characters +* Patsubst:: Substituting text by regular expression +* Format:: Formatting strings (printf-like) +@end menu + +@node Len +@section Calculating length of strings + +@cindex length of strings +@cindex strings, length of +The length of a string can be calculated by @code{len}: + +@deffn Builtin len (@var{string}) +Expands to the length of @var{string}, as a decimal number. + +The macro @code{len} is recognized only with parameters. +@end deffn + +@example +len() +@result{}0 +len(`abcdef') +@result{}6 +@end example + +@node Index macro +@section Searching for substrings + +Searching for substrings is done with @code{index}: + +@deffn Builtin index (@var{string}, @var{substring}) +Expands to the index of the first occurrence of @var{substring} in +@var{string}. The first character in @var{string} has index 0. If +@var{substring} does not occur in @var{string}, @code{index} expands to +@samp{-1}. + +The macro @code{index} is recognized only with parameters. +@end deffn + +@example +index(`gnus, gnats, and armadillos', `nat') +@result{}7 +index(`gnus, gnats, and armadillos', `dag') +@result{}-1 +@end example + +Omitting @var{substring} evokes a warning, but still produces output. + +@example +index(`abc') +@error{}m4:stdin:1: Warning: too few arguments to builtin `index' +@result{}0 +@end example + +@node Regexp +@section Searching for regular expressions + +@cindex regular expressions +@cindex @acronym{GNU} extensions +Searching for regular expressions is done with the builtin +@code{regexp}: + +@deffn Builtin regexp (@var{string}, @var{regexp}, @ovar{replacement}) +Searches for @var{regexp} in @var{string}. The syntax for regular +expressions is the same as in @acronym{GNU} Emacs. +@ifnothtml +@xref{Regexps, , Syntax of Regular Expressions, emacs, The GNU Emacs +Manual}. +@end ifnothtml +@ifhtml +See +@uref{http://www.gnu.org/@/software/@/emacs/@/manual/@/emacs.html#Regexps, +Syntax of Regular Expressions} in the @acronym{GNU} Emacs Manual. +@end ifhtml + +If @var{replacement} is omitted, @code{regexp} expands to the index of +the first match of @var{regexp} in @var{string}. If @var{regexp} does +not match anywhere in @var{string}, it expands to -1. + +If @var{replacement} is supplied, and there was a match, @code{regexp} +changes the expansion to this argument, with @samp{\@var{n}} substituted +by the text matched by the @var{n}th parenthesized sub-expression of +@var{regexp}, up to nine sub-expressions. The escape @samp{\&} is +replaced by the text of the entire regular expression matched. For +all other characters, @samp{\} treats the next character literally. A +warning is issued if there were fewer sub-expressions than the +@samp{\@var{n}} requested, or if there is a trailing @samp{\}. If there +was no match, @code{regexp} expands to the empty string. + +The macro @code{regexp} is recognized only with parameters. +@end deffn + +@example +regexp(`GNUs not Unix', `\<[a-z]\w+') +@result{}5 +regexp(`GNUs not Unix', `\a-c-a') +@result{}<;>abcba +translit(`abcdef', `aabdef', `bcged') +@result{}bgced +@end example + +In the @sc{ascii} encoding, the first example deletes all uppercase +letters, the second converts lowercase to uppercase, and the third +`mirrors' all uppercase letters, while converting them to lowercase. +The two first cases are by far the most common, even though they are not +portable to @sc{ebcdic} or other encodings. The fourth example shows a +range ending in @samp{-}, as well as back-to-back ranges. The final +example shows that @samp{a} is mapped to @samp{b}, not @samp{c}; the +resulting @samp{b} is not further remapped to @samp{g}; the @samp{d} and +@samp{e} are swapped, and the @samp{f} is discarded. + +@ignore +@comment No need to fight 8-bit characters, as it is difficult to get +@comment rendering right in both info and dvi. + +@example +translit(`«abc~', `~-»') +@result{}abc +@end example +@end ignore + +Omitting @var{chars} evokes a warning, but still produces output. + +@example +translit(`abc') +@error{}m4:stdin:1: Warning: too few arguments to builtin `translit' +@result{}abc +@end example + +@node Patsubst +@section Substituting text by regular expression + +@cindex regular expressions +@cindex pattern substitution +@cindex substitution by regular expression +@cindex @acronym{GNU} extensions +Global substitution in a string is done by @code{patsubst}: + +@deffn Builtin patsubst (@var{string}, @var{regexp}, @ovar{replacement}) +Searches @var{string} for matches of @var{regexp}, and substitutes +@var{replacement} for each match. The syntax for regular expressions +is the same as in @acronym{GNU} Emacs (@pxref{Regexp}). + +The parts of @var{string} that are not covered by any match of +@var{regexp} are copied to the expansion. Whenever a match is found, the +search proceeds from the end of the match, so a character from +@var{string} will never be substituted twice. If @var{regexp} matches a +string of zero length, the start position for the search is incremented, +to avoid infinite loops. + +When a replacement is to be made, @var{replacement} is inserted into +the expansion, with @samp{\@var{n}} substituted by the text matched by +the @var{n}th parenthesized sub-expression of @var{patsubst}, for up to +nine sub-expressions. The escape @samp{\&} is replaced by the text of +the entire regular expression matched. For all other characters, +@samp{\} treats the next character literally. A warning is issued if +there were fewer sub-expressions than the @samp{\@var{n}} requested, or +if there is a trailing @samp{\}. + +The @var{replacement} argument can be omitted, in which case the text +matched by @var{regexp} is deleted. + +The macro @code{patsubst} is recognized only with parameters. +@end deffn + +@example +patsubst(`GNUs not Unix', `^', `OBS: ') +@result{}OBS: GNUs not Unix +patsubst(`GNUs not Unix', `\<', `OBS: ') +@result{}OBS: GNUs OBS: not OBS: Unix +patsubst(`GNUs not Unix', `\w*', `(\&)') +@result{}(GNUs)() (not)() (Unix)() +patsubst(`GNUs not Unix', `\w+', `(\&)') +@result{}(GNUs) (not) (Unix) +patsubst(`GNUs not Unix', `[A-Z][a-z]+') +@result{}GN not@w{ } +patsubst(`GNUs not Unix', `not', `NOT\') +@error{}m4:stdin:6: Warning: trailing \ ignored in replacement +@result{}GNUs NOT Unix +@end example + +Here is a slightly more realistic example, which capitalizes individual +word or whole sentences, by substituting calls of the macros +@code{upcase} and @code{downcase} into the strings. + +@deffn Composite upcase (@var{text}) +@deffnx Composite downcase (@var{text}) +@deffnx Composite capitalize (@var{text}) +Expand to @var{text}, but with capitalization changed: @code{upcase} +changes all letters to upper case, @code{downcase} changes all letters +to lower case, and @code{capitalize} changes the first character of each +word to upper case and the remaining characters to lower case. +@end deffn + +@example +define(`upcase', `translit(`$*', `a-z', `A-Z')')dnl +define(`downcase', `translit(`$*', `A-Z', `a-z')')dnl +define(`capitalize1', + `regexp(`$1', `^\(\w\)\(\w*\)', + `upcase(`\1')`'downcase(`\2')')')dnl +define(`capitalize', + `patsubst(`$1', `\w+', `capitalize1(`\&')')')dnl +capitalize(`GNUs not Unix') +@result{}Gnus Not Unix +@end example + +While @code{regexp} replaces the whole input with the replacement as +soon as there is a match, @code{patsubst} replaces each +@emph{occurrence} of a match and preserves non-matching pieces: + +@example +define(`patreg', +`patsubst($@@) +regexp($@@)')dnl +patreg(`bar foo baz Foo', `foo\|Foo', `FOO') +@result{}bar FOO baz FOO +@result{}FOO +patreg(`aba abb 121', `\(.\)\(.\)\1', `\2\1\2') +@result{}bab abb 212 +@result{}bab +@end example + +Omitting @var{regexp} evokes a warning, but still produces output. + +@example +patsubst(`abc') +@error{}m4:stdin:1: Warning: too few arguments to builtin `patsubst' +@result{}abc +@end example + +@node Format +@section Formatted output + +@cindex formatted output +@cindex output, formatted +@cindex @acronym{GNU} extensions +Formatted output can be made with @code{format}: + +@deffn Builtin format (@var{format-string}, @dots{}) +Works much like the C function @code{printf}. The first argument +@var{format-string} can contain @samp{%} specifications which are +satisfied by additional arguments, and the expansion of @code{format} is +the formatted string. + +The macro @code{format} is recognized only with parameters. +@end deffn + +Its use is best described by a few examples: + +@example +define(`foo', `The brown fox jumped over the lazy dog') +@result{} +format(`The string "%s" uses %d characters', foo, len(foo)) +@result{}The string "The brown fox jumped over the lazy dog" uses 38 characters +format(`%.0f', `56789.9876') +@result{}56790 +len(format(`%-*X', `300', `1')) +@result{}300 +@end example + +Using the @code{forloop} macro defined earlier (@pxref{Forloop}), this +example shows how @code{format} can be used to produce tabular output. + +@example +include(`forloop.m4') +@result{} +forloop(`i', `1', `10', `format(`%6d squared is %10d +', i, eval(i**2))') +@result{} 1 squared is 1 +@result{} 2 squared is 4 +@result{} 3 squared is 9 +@result{} 4 squared is 16 +@result{} 5 squared is 25 +@result{} 6 squared is 36 +@result{} 7 squared is 49 +@result{} 8 squared is 64 +@result{} 9 squared is 81 +@result{} 10 squared is 100 +@result{} +@end example + +The builtin @code{format} is modeled after the ANSI C @samp{printf} +function, and supports these @samp{%} specifiers: @samp{c}, +@samp{s}, @samp{d}, @samp{o}, @samp{x}, @samp{X}, @samp{u}, @samp{e}, +@samp{E}, @samp{f}, @samp{F}, @samp{g}, @samp{G}, and @samp{%}; it +supports field widths and precisions, and the +modifiers @samp{+}, @samp{-}, @samp{@w{ }}, @samp{0}, @samp{#}, @samp{h} and +@samp{l}. For more details on the functioning of @code{printf}, see the +C Library Manual. + +For now, unrecognized specifiers are silently ignored, but it is +anticipated that a future release of @acronym{GNU} @code{m4} will support more +specifiers, and give warnings when problems are encountered. Likewise, +escape sequences are not yet recognized. + +@node Arithmetic +@chapter Macros for doing arithmetic + +@cindex arithmetic +@cindex integer arithmetic +Integer arithmetic is included in @code{m4}, with a C-like syntax. As +convenient shorthands, there are builtins for simple increment and +decrement operations. + +@menu +* Incr:: Decrement and increment operators +* Eval:: Evaluating integer expressions +@end menu + +@node Incr +@section Decrement and increment operators + +@cindex decrement operator +@cindex increment operator +Increment and decrement of integers are supported using the builtins +@code{incr} and @code{decr}: + +@deffn Builtin incr (@var{number}) +@deffnx Builtin decr (@var{number}) +Expand to the numerical value of @var{number}, incremented +or decremented, respectively, by one. Except for the empty string, the +expansion is empty if @var{number} could not be parsed. + +The macros @code{incr} and @code{decr} are recognized only with +parameters. +@end deffn + +@example +incr(`4') +@result{}5 +decr(`7') +@result{}6 +incr() +@error{}m4:stdin:3: empty string treated as 0 in builtin `incr' +@result{}1 +decr() +@error{}m4:stdin:4: empty string treated as 0 in builtin `decr' +@result{}-1 +@end example + +@node Eval +@section Evaluating integer expressions + +@cindex integer expression evaluation +@cindex evaluation, of integer expressions +@cindex expressions, evaluation of integer +Integer expressions are evaluated with @code{eval}: + +@deffn Builtin eval (@var{expression}, @dvar{radix, 10}, @ovar{width}) +Expands to the value of @var{expression}. The expansion is empty +if an error is encountered while parsing the arguments. If specified, +@var{radix} and @var{width} control the format of the output. + +The macro @code{eval} is recognized only with parameters. +@end deffn + +Expressions can contain the following operators, listed in order of +decreasing precedence. + +@table @code +@item + - +Unary plus and minus +@item ** +Exponentiation +@item * / % +Multiplication, division and modulo +@item + - +Addition and subtraction +@item << >> +Shift left or right +@item == != > >= < <= +Relational operators +@item ! +Logical negation +@item ~ +Bitwise negation +@item & +Bitwise and +@item ^ +Bitwise exclusive-or +@item | +Bitwise or +@item && +Logical and +@item || +Logical or +@end table + +All operators, except exponentiation, are left associative. + +Note that some older @code{m4} implementations use @samp{^} as an +alternate operator for exponentiation, although @acronym{POSIX} requires +the C behavior of bitwise exclusive-or. On the other hand, the +precedence of @samp{~} and @samp{!} are different in @acronym{GNU} +@code{m4} than +they are in C, matching the precedence in traditional @code{m4} +implementations. This behavior is likely to change in a future +version to match @acronym{POSIX}, so use parentheses to force the +desired precedence. + +Within @var{expression}, (but not @var{radix} or @var{width}), +numbers without a special prefix are decimal. A simple @samp{0} +prefix introduces an octal number. @samp{0x} introduces a hexadecimal +number. @samp{0b} introduces a binary number. @samp{0r} introduces a +number expressed in any radix between 1 and 36: the prefix should be +immediately followed by the decimal expression of the radix, a colon, +then the digits making the number. For radix 1, leading zeros are +ignored and all remaining digits must be @samp{1}; for all other +radices, the digits are +@samp{0}, @samp{1}, @samp{2}, @dots{}. Beyond @samp{9}, the digits are +@samp{a}, @samp{b} @dots{} up to @samp{z}. Lower and upper case letters +can be used interchangeably in numbers prefixes and as number digits. + +Parentheses may be used to group subexpressions whenever needed. For the +relational operators, a true relation returns @code{1}, and a false +relation return @code{0}. + +Here are a few examples of use of @code{eval}. + +@example +eval(`-3 * 5') +@result{}-15 +eval(index(`Hello world', `llo') >= 0) +@result{}1 +eval(`0r1:0111 + 0b100 + 0r3:12') +@result{}12 +define(`square', `eval(`('$1`)**2')') +@result{} +square(`9') +@result{}81 +square(square(`5')`+1') +@result{}676 +define(`foo', `666') +@result{} +eval(`foo/6') +@error{}m4:stdin:8: bad expression in eval: foo/6 +@result{} +eval(foo/6) +@result{}111 +@end example + +As the last two lines show, @code{eval} does not handle macro +names, even if they expand to a valid expression (or part of a valid +expression). Therefore all macros must be expanded before they are +passed to @code{eval}. + +All evaluation is done with 32-bit signed integers, assuming +2's-complement with wrap-around. The shift operators are defined in +@acronym{GNU} +@code{m4} by doing an implicit bit-wise and of the right-hand operand +with 0x1f, and sign-extension with right shift. + +@example +eval(0x80000000 / -1) +@result{}-2147483648 +eval(0x80000000 % -1) +@result{}0 +eval(0x7fffffff) +@result{}2147483647 +incr(eval(0x7fffffff)) +@result{}-2147483648 +eval(-4 >> 33) +@result{}-2 +@end example + +If @var{radix} is specified, it specifies the radix to be used in the +expansion. The default radix is 10; this is also the case if +@var{radix} is the empty string. It is an error if the radix is outside +the range of 1 through 36, inclusive. The result of @code{eval} is +always taken to be signed. No radix prefix is output, and for radices +greater than 10, the digits are lower case. The @var{width} argument +specifies the minimum output width, excluding any negative sign. The +result is zero-padded to extend the expansion to the requested width. +It is an error if the width is negative. On error, the expansion of +@code{eval} is empty. + +@example +eval(`666', `10') +@result{}666 +eval(`666', `11') +@result{}556 +eval(`666', `6') +@result{}3030 +eval(`666', `6', `10') +@result{}0000003030 +eval(`-666', `6', `10') +@result{}-0000003030 +eval(`10', `', `0') +@result{}10 +`0r1:'eval(`10', `1', `11') +@result{}0r1:01111111111 +eval(`10', `16') +@result{}a +@end example + +@node Shell commands +@chapter Macros for running shell commands + +@cindex executing UNIX commands +@cindex running UNIX commands +@cindex UNIX commands, running +@cindex commands, running UNIX +@cindex executing shell commands +@cindex running shell commands +@cindex shell commands, running +@cindex commands, running shell +There are a few builtin macros in @code{m4} that allow you to run shell +commands from within @code{m4}. + +Note that the definition of a valid shell command is system dependent. +On UNIX systems, this is the typical @code{/bin/sh}. But on other +systems, such as native Windows, the shell has a different syntax of +commands that it understands. Some examples in this chapter assume +@code{/bin/sh}, and also demonstrate how to quit early with a known +exit value if this is not the case. + +@menu +* Platform macros:: Determining the platform +* Syscmd:: Executing simple commands +* Esyscmd:: Reading the output of commands +* Sysval:: Exit status +* Mkstemp:: Making temporary files +@end menu + +@node Platform macros +@section Determining the platform + +@cindex platform macros +Sometimes it is desirable for an input file to know which +platform @code{m4} is running on. @acronym{GNU} @code{m4} provides several +macros that are predefined to expand to the empty string; checking for +their existence will confirm platform details. + +@deffn {Optional builtin} __gnu__ +@deffnx {Optional builtin} __os2__ +@deffnx {Optional builtin} os2 +@deffnx {Optional builtin} __unix__ +@deffnx {Optional builtin} unix +@deffnx {Optional builtin} __windows__ +@deffnx {Optional builtin} windows +Each of these macros is conditionally defined as needed to describe the +environment of @code{m4}. If defined, each macro expands to the empty +string. +@end deffn + +When @acronym{GNU} extensions are in effect (that is, when you did not +use the @option{-G} option, @pxref{Limits control, , Invoking m4}), +@acronym{GNU} @code{m4} will define the macro @code{@w{__gnu__}} to +expand to the empty string. + +@example +__gnu__ +@result{} +ifdef(`__gnu__', `Extensions are active') +@result{}Extensions are active +@end example + +@cindex platform macro +On UNIX systems, @acronym{GNU} @code{m4} will define @code{@w{__unix__}} +by default, or @code{unix} when the @option{-G} option is specified. + +On native Windows systems, @acronym{GNU} @code{m4} will define +@code{@w{__windows__}} by default, or @code{windows} when the +@option{-G} option is specified. + +On OS/2 systems, @acronym{GNU} @code{m4} will define @code{@w{__os2__}} +by default, or @code{os2} when the @option{-G} option is specified. + +If @acronym{GNU} @code{m4} does not provide a platform macro for your system, +please report that as a bug. + +@example +define(`provided', `0') +@result{} +ifdef(`__unix__', `define(`provided', incr(provided))') +@result{} +ifdef(`__windows__', `define(`provided', incr(provided))') +@result{} +ifdef(`__os2__', `define(`provided', incr(provided))') +@result{} +provided +@result{}1 +@end example + +@node Syscmd +@section Executing simple commands + +Any shell command can be executed, using @code{syscmd}: + +@deffn Builtin syscmd (@var{shell-command}) +Executes @var{shell-command} as a shell command. + +The expansion of @code{syscmd} is void, @emph{not} the output from +@var{shell-command}! Output or error messages from @var{shell-command} +are not read by @code{m4}. @xref{Esyscmd}, if you need to process the +command output. + +Prior to executing the command, @code{m4} flushes its buffers. +The default standard input, output and error of @var{shell-command} are +the same as those of @code{m4}. + +The macro @code{syscmd} is recognized only with parameters. +@end deffn + +@example +define(`foo', `FOO') +@result{} +syscmd(`echo foo') +@result{}foo +@result{} +@end example + +Note how the expansion of @code{syscmd} keeps the trailing newline of +the command, as well as using the newline that appeared after the macro. + +As an example of @var{shell-command} using the same standard input as +@code{m4}, the command line @kbd{echo "m4wrap(\`syscmd(\`cat')')" | m4} +will tell @code{m4} to read all of its input before executing the +wrapped text, then hand a valid (albeit emptied) pipe as standard input +for the @code{cat} subcommand. Therefore, you should be careful when +using standard input (either by specifying no files, or by passing +@samp{-} as a file name on the command line, @pxref{Command line files, +, Invoking m4}), and +also invoking subcommands via @code{syscmd} or @code{esyscmd} that +consume data from standard input. When standard input is a seekable +file, the subprocess will pick up with the next character not yet +processed by @code{m4}; when it is a pipe or other non-seekable file, +there is no guarantee how much data will already be buffered by +@code{m4} and thus unavailable to the child. + +@ignore +@comment If the user types the example below with stdin being an +@comment interactive terminal, then cat will hang waiting for additional +@comment input after m4 has exited. But the testsuite is using a pipe +@comment for stdin. Hence, we have two versions - the one we feed the +@comment testsuite below, and the one we display to the user above that +@comment more accurately shows what the testsuite is really doing but +@comment which the testsuite cannot parse. + +@example +m4wrap(`syscmd(`cat')') +@result{} +^D +@end example +@end ignore + +@node Esyscmd +@section Reading the output of commands + +@cindex @acronym{GNU} extensions +If you want @code{m4} to read the output of a shell command, use +@code{esyscmd}: + +@deffn Builtin esyscmd (@var{shell-command}) +Expands to the standard output of the shell command +@var{shell-command}. + +Prior to executing the command, @code{m4} flushes its buffers. +The default standard input and standard error of @var{shell-command} are +the same as those of @code{m4}. The error output of @var{shell-command} +is not a part of the expansion: it will appear along with the error +output of @code{m4}. + +The macro @code{esyscmd} is recognized only with parameters. +@end deffn + +@example +define(`foo', `FOO') +@result{} +esyscmd(`echo foo') +@result{}FOO +@result{} +@end example + +Note how the expansion of @code{esyscmd} keeps the trailing newline of +the command, as well as using the newline that appeared after the macro. + +Just as with @code{syscmd}, care must be exercised when sharing standard +input between @code{m4} and the child process of @code{esyscmd}. + +@node Sysval +@section Exit status + +@cindex UNIX commands, exit status from +@cindex exit status from shell commands +@cindex shell commands, exit status from +@cindex commands, exit status from shell +@cindex status of shell commands +To see whether a shell command succeeded, use @code{sysval}: + +@deffn Builtin sysval +Expands to the exit status of the last shell command run with +@code{syscmd} or @code{esyscmd}. Expands to 0 if no command has been +run yet. +@end deffn + +@example +syscmd(`false') +@result{} +ifelse(sysval, `0', `zero', `non-zero') +@result{}non-zero +syscmd(`exit 2') +@result{} +sysval +@result{}2 +syscmd(`true') +@result{} +sysval +@result{}0 +esyscmd(`false') +@result{} +ifelse(sysval, `0', `zero', `non-zero') +@result{}non-zero +esyscmd(`exit 2') +@result{} +sysval +@result{}2 +esyscmd(`true') +@result{} +sysval +@result{}0 +@end example + +@command{sysval} results in 127 if there was a problem executing the +command, for example, if the system-imposed argument length is exceeded, +or if there were not enough resources to fork. It is not possible to +distinguish between failed execution and successful execution that had +an exit status of 127. + +On UNIX platforms, where it is possible to detect when command execution +is terminated by a signal, rather than a normal exit, the result is the +signal number shifted left by eight bits. + +@comment This test has difficulties being portable, even on platforms +@comment where syscmd invokes /bin/sh. Kill is not portable with signal +@comment names. According to autoconf, the only portable signal numbers +@comment are 1 (HUP), 2 (INT), 9 (KILL), 13 (PIPE) and 15 (TERM). But +@comment all shells handle SIGINT, and ksh handles HUP (as in, the shell +@comment exits normally rather than letting the signal terminate it). +@comment Also, TERM is flaky, as it can also kill the running m4 on +@comment systems where /bin/sh does not create its own process group. +@comment That leaves KILL and PIPE as the two signals tested. +@example +dnl This test assumes kill is a shell builtin, and that signals are +dnl recognizable. +ifdef(`__unix__', , + `errprint(` skipping: syscmd does not have unix semantics +')m4exit(`77')')dnl +syscmd(`kill -13 $$') +@result{} +sysval +@result{}3328 +esyscmd(`kill -9 $$') +@result{} +sysval +@result{}2304 +@end example + +@node Mkstemp +@section Making temporary files + +@cindex temporary file names +@cindex files, names of temporary +Commands specified to @code{syscmd} or @code{esyscmd} might need a +temporary file, for output or for some other purpose. There is a +builtin macro, @code{mkstemp}, for making a temporary file: + +@deffn Builtin mkstemp (@var{template}) +@deffnx Builtin maketemp (@var{template}) +Expands to a name of a new, empty file, made from the string +@var{template}, which should end with the string @samp{XXXXXX}. The six +@samp{X} characters are then replaced with random characters matching +the regular expression @samp{[a-zA-Z0-9._-]}, in order to make the file +name unique. If fewer than six @samp{X} characters are found at the end +of @code{template}, the result will be longer than the template. The +created file will have access permissions as if by @kbd{chmod =rw,go=}, +meaning that the current umask of the @code{m4} process is taken into +account, and at most only the current user can read and write the file. + +The traditional behavior, standardized by @acronym{POSIX}, is that +@code{maketemp} merely replaces the trailing @samp{X} with the process +id, without creating a file, and without ensuring that the resulting +string is a unique file name. In part, this means that using the same +@var{template} twice in the same input file will result in the same +expansion. This behavior is a security hole, as it is very easy for +another process to guess the name that will be generated, and thus +interfere with a subsequent use of @code{syscmd} trying to manipulate +that file name. Hence, @acronym{POSIX} has recommended that all new +implementations of @code{m4} provide the secure @code{mkstemp} builtin, +and that users of @code{m4} check for its existence. + +The macros @code{mkstemp} and @code{maketemp} are recognized only with +parameters. +@end deffn + +If you try this next example, you will most likely get different output +for the two file names, since the replacement characters are randomly +chosen: + +@comment ignore +@example +maketemp(`/tmp/fooXXXXXX') +@result{}/tmp/fooa07346 +ifdef(`mkstemp', `define(`maketemp', defn(`mkstemp'))', + `define(`mkstemp', defn(`maketemp'))dnl +errprint(`warning: potentially insecure maketemp implementation +')') +@result{} +mkstemp(`doc') +@result{}docQv83Uw +@end example + +@cindex @acronym{GNU} extensions +Unless you use the @option{--traditional} command line option (or +@option{-G}, @pxref{Limits control, , Invoking m4}), the @acronym{GNU} +version of @code{maketemp} is secure. This means that using the same +template to multiple calls will generate multiple files. However, we +recommend that you use the new @code{mkstemp} macro, introduced in +@acronym{GNU} M4 1.4.8, which is secure even in traditional mode. + +@example +syscmd(`echo foo??????')dnl +@result{}foo?????? +define(`file1', maketemp(`fooXXXXXX'))dnl +ifelse(esyscmd(`echo foo??????'), `foo??????', `no file', `created') +@result{}created +define(`file2', maketemp(`fooXX'))dnl +define(`file3', mkstemp(`fooXXXXXX'))dnl +ifelse(len(file1), len(file2), `same length', `different') +@result{}same length +ifelse(file1, file2, `same', `different file') +@result{}different file +ifelse(file2, file3, `same', `different file') +@result{}different file +ifelse(file1, file3, `same', `different file') +@result{}different file +syscmd(`rm 'file1 file2 file3) +@result{} +sysval +@result{}0 +@end example + +@node Miscellaneous +@chapter Miscellaneous builtin macros + +This chapter describes various builtins, that do not really belong in +any of the previous chapters. + +@menu +* Errprint:: Printing error messages +* Location:: Printing current location +* M4exit:: Exiting from @code{m4} +@end menu + +@node Errprint +@section Printing error messages + +@cindex printing error messages +@cindex error messages, printing +@cindex messages, printing error +You can print error messages using @code{errprint}: + +@deffn Builtin errprint (@var{message}, @dots{}) +Prints @var{message} and the rest of the arguments to standard error, +separated by spaces. Standard error is used, regardless of the +@option{--debugfile} option (@pxref{Debugging options, , Invoking m4}). + +The expansion of @code{errprint} is void. +The macro @code{errprint} is recognized only with parameters. +@end deffn + +@example +errprint(`Invalid arguments to forloop +') +@error{}Invalid arguments to forloop +@result{} +errprint(`1')errprint(`2',`3 +') +@error{}12 3 +@result{} +@end example + +A trailing newline is @emph{not} printed automatically, so it should be +supplied as part of the argument, as in the example. Unfortunately, the +exact output of @code{errprint} is not very portable to other @code{m4} +implementations: @acronym{POSIX} requires that all arguments be printed, +but some implementations of @code{m4} only print the first. +Furthermore, some BSD implementations always append a newline for each +@code{errprint} call, regardless of whether the last argument already +had one, and @acronym{POSIX} is silent on whether this is acceptable. + +@node Location +@section Printing current location + +To make it possible to specify the location of an error, three +utility builtins exist: + +@deffn Builtin __file__ +@deffnx Builtin __line__ +@deffnx Builtin __program__ +Expand to the quoted name of the current input file, the +current input line number in that file, and the quoted name of the +current invocation of @code{m4}. +@end deffn + +@example +errprint(__program__:__file__:__line__: `input error +') +@error{}m4:stdin:1: input error +@result{} +@end example + +Line numbers start at 1 for each file. If the file was found due to the +@option{-I} option or @env{M4PATH} environment variable, that is +reflected in the file name. The syncline option (@option{-s}, +@pxref{Preprocessor features, , Invoking m4}), and the +@samp{f} and @samp{l} flags of @code{debugmode} (@pxref{Debug Levels}), +also use this notion of current file and line. Redefining the three +location macros has no effect on syncline, debug, or warning message +output. Assume this example is run in the +@file{m4-@value{VERSION}/@/checks} directory of the @acronym{GNU} M4 +package, using @samp{--include=../examples} in the command line to find +the file @file{incl.m4} mentioned earlier: + +@example +define(`foo', ``$0' called at __file__:__line__') +@result{} +foo +@result{}foo called at stdin:2 +include(`incl.m4') +@result{}Include file start +@result{}foo called at ../examples/incl.m4:2 +@result{}Include file end +@result{} +@end example + +The location of macros invoked during the rescanning of macro expansion +text corresponds to the location in the file where the expansion was +triggered, regardless of how many newline characters the expansion text +contains. As of @acronym{GNU} M4 1.4.8, the location of text wrapped +with @code{m4wrap} (@pxref{M4wrap}) is the point at which the +@code{m4wrap} was invoked. Previous versions, however, behaved as +though wrapped text came from line 0 of the file ``''. + +@example +define(`echo', `$@@') +@result{} +define(`foo', `echo(__line__ +__line__)') +@result{} +echo(__line__ +__line__) +@result{}4 +@result{}5 +m4wrap(`foo +') +@result{} +foo(errprint(__line__ +__line__ +)) +@error{}8 +@error{}9 +@result{}8 +@result{}8 +__line__ +@result{}11 +^D +@result{}6 +@result{}6 +@end example + +The @code{@w{__program__}} macro behaves like @samp{$0} in shell +terminology. If you invoke @code{m4} through an absolute path or a link +with a different spelling, rather than by relying on a @env{PATH} search +for plain @samp{m4}, it will affect how @code{@w{__program__}} expands. +The intent is that you can use it to produce error messages with the +same formatting that @code{m4} produces internally. It can also be used +within @code{syscmd} (@pxref{Syscmd}) to pick the same version of +@code{m4} that is currently running, rather than whatever version of +@code{m4} happens to be first in @env{PATH}. It was first introduced in +@acronym{GNU} M4 1.4.6. + +@node M4exit +@section Exiting from @code{m4} + +@cindex exiting from @code{m4} +@cindex status, setting @code{m4} exit +If you need to exit from @code{m4} before the entire input has been +read, you can use @code{m4exit}: + +@deffn Builtin m4exit (@dvar{code, 0}) +Causes @code{m4} to exit, with exit status @var{code}. If @var{code} is +left out, the exit status is zero. If @var{code} cannot be parsed, or +is outside the range of 0 to 255, the exit status is one. No further +input is read, and all wrapped and diverted text is discarded. +@end deffn + +@example +m4wrap(`This text is lost due to `m4exit'.') +@result{} +divert(`1') So is this. +divert +@result{} +m4exit And this is never read. +@end example + +A common use of this is to abort processing: + +@deffn Composite fatal_error (@var{message}) +Abort processing with an error message and non-zero status. Prefix +@var{message} with details about where the error occurred, and print the +resulting string to standard error. +@end deffn + +@example +define(`fatal_error', + `errprint(__program__:__file__:__line__`: fatal error: $* +')m4exit(`1')') +@result{} +fatal_error(`this is a BAD one, buster') +@error{}m4:stdin:4: fatal error: this is a BAD one, buster +@end example + +After this macro call, @code{m4} will exit with exit status 1. This macro +is only intended for error exits, since the normal exit procedures are +not followed, e.g., diverted text is not undiverted, and saved text +(@pxref{M4wrap}) is not reread. (This macro could be made more robust +to earlier versions of @code{m4}. You should try to see if you can find +weaknesses and correct them; or @pxref{Improved fatal_error, , Answers}). + +Note that it is still possible for the exit status to be different than +what was requested by @code{m4exit}. If @code{m4} detects some other +error, such as a write error on standard output, the exit status will be +non-zero even if @code{m4exit} requested zero. + +If standard input is seekable, then the file will be positioned at the +next unread character. If it is a pipe or other non-seekable file, +then there are no guarantees how much data @code{m4} might have read +into buffers, and thus discarded. + +@node Frozen files +@chapter Fast loading of frozen state + +Some bigger @code{m4} applications may be built over a common base +containing hundreds of definitions and other costly initializations. +Usually, the common base is kept in one or more declarative files, +which files are listed on each @code{m4} invocation prior to the +user's input file, or else each input file uses @code{include}. + +Reading the common base of a big application, over and over again, may +be time consuming. @acronym{GNU} @code{m4} offers some machinery to speed up +the start of an application using lengthy common bases. + +@menu +* Using frozen files:: Using frozen files +* Frozen file format:: Frozen file format +@end menu + +@node Using frozen files +@section Using frozen files +@cindex fast loading of frozen files +@cindex frozen files for fast loading +@cindex initialization, frozen states +@cindex dumping into frozen file +@cindex reloading a frozen file +@cindex @acronym{GNU} extensions +Suppose a user has a library of @code{m4} initializations in +@file{base.m4}, which is then used with multiple input files: + +@comment ignore +@example +m4 base.m4 input1.m4 +m4 base.m4 input2.m4 +m4 base.m4 input3.m4 +@end example + +Rather than spending time parsing the fixed contents of @file{base.m4} +every time, the user might rather execute: + +@comment ignore +@example +m4 -F base.m4f base.m4 +@end example + +@noindent +once, and further execute, as often as needed: + +@comment ignore +@example +m4 -R base.m4f input1.m4 +m4 -R base.m4f input2.m4 +m4 -R base.m4f input3.m4 +@end example + +@noindent +with the varying input. The first call, containing the @option{-F} +option, only reads and executes file @file{base.m4}, defining +various application macros and computing other initializations. +Once the input file @file{base.m4} has been completely processed, @acronym{GNU} +@code{m4} produces on @file{base.m4f} a @dfn{frozen} file, that is, a +file which contains a kind of snapshot of the @code{m4} internal state. + +Later calls, containing the @option{-R} option, are able to reload +the internal state of @code{m4}, from @file{base.m4f}, +@emph{prior} to reading any other input files. This means +instead of starting with a virgin copy of @code{m4}, input will be +read after having effectively recovered the effect of a prior run. +In our example, the effect is the same as if file @file{base.m4} has +been read anew. However, this effect is achieved a lot faster. + +Only one frozen file may be created or read in any one @code{m4} +invocation. It is not possible to recover two frozen files at once. +However, frozen files may be updated incrementally, through using +@option{-R} and @option{-F} options simultaneously. For example, if +some care is taken, the command: + +@comment ignore +@example +m4 file1.m4 file2.m4 file3.m4 file4.m4 +@end example + +@noindent +could be broken down in the following sequence, accumulating the same +output: + +@comment ignore +@example +m4 -F file1.m4f file1.m4 +m4 -R file1.m4f -F file2.m4f file2.m4 +m4 -R file2.m4f -F file3.m4f file3.m4 +m4 -R file3.m4f file4.m4 +@end example + +Some care is necessary because not every effort has been made for +this to work in all cases. In particular, the trace attribute of +macros is not handled, nor the current setting of @code{changeword}. +Currently, @code{m4wrap} and @code{sysval} also have problems. +Also, interactions for some options of @code{m4}, being used in one call +and not in the next, have not been fully analyzed yet. On the other +end, you may be confident that stacks of @code{pushdef} definitions +are handled correctly, as well as undefined or renamed builtins, and +changed strings for quotes or comments. And future releases of +@acronym{GNU} M4 will improve on the utility of frozen files. + +When an @code{m4} run is to be frozen, the automatic undiversion +which takes place at end of execution is inhibited. Instead, all +positively numbered diversions are saved into the frozen file. +The active diversion number is also transmitted. + +A frozen file to be reloaded need not reside in the current directory. +It is looked up the same way as an @code{include} file (@pxref{Search +Path}). + +If the frozen file was generated with a newer version of @code{m4}, and +contains directives that an older @code{m4} cannot parse, attempting to +load the frozen file with option @option{-R} will cause @code{m4} to +exit with status 63 to indicate version mismatch. + +@node Frozen file format +@section Frozen file format +@cindex frozen file format +@cindex file format, frozen file +Frozen files are sharable across architectures. It is safe to write +a frozen file on one machine and read it on another, given that the +second machine uses the same or newer version of @acronym{GNU} @code{m4}. +It is conventional, but not required, to give a frozen file the suffix +of @code{.m4f}. + +These are simple (editable) text files, made up of directives, +each starting with a capital letter and ending with a newline +(@key{NL}). Wherever a directive is expected, the character +@samp{#} introduces a comment line; empty lines are also ignored if they +are not part of an embedded string. +In the following descriptions, each @var{len} refers to the length of +the corresponding strings @var{str} in the next line of input. Numbers +are always expressed in decimal. There are no escape characters. The +directives are: + +@table @code +@item C @var{len1} , @var{len2} @key{NL} @var{str1} @var{str2} @key{NL} +Uses @var{str1} and @var{str2} as the begin-comment and +end-comment strings. If omitted, then @samp{#} and @key{NL} are the +comment delimiters. + +@item D @var{number}, @var{len} @key{NL} @var{str} @key{NL} +Selects diversion @var{number}, making it current, then copy +@var{str} in the current diversion. @var{number} may be a negative +number for a non-existing diversion. To merely specify an active +selection, use this command with an empty @var{str}. With 0 as the +diversion @var{number}, @var{str} will be issued on standard output +at reload time. @acronym{GNU} @code{m4} will not produce the @samp{D} +directive with non-zero length for diversion 0, but this can be done +with manual edits. This directive may +appear more than once for the same diversion, in which case the +diversion is the concatenation of the various uses. If omitted, then +diversion 0 is current. + +@item F @var{len1} , @var{len2} @key{NL} @var{str1} @var{str2} @key{NL} +Defines, through @code{pushdef}, a definition for @var{str1} +expanding to the function whose builtin name is @var{str2}. If the +builtin does not exist (for example, if the frozen file was produced by +a copy of @code{m4} compiled with changeword support, but the version +of @code{m4} reloading was compiled without it), the reload is silent, +but any subsequent use of the definition of @var{str1} will result in +a warning. This directive may appear more than once for the same name, +and its order, along with @samp{T}, is important. If omitted, you will +have no access to any builtins. + +@item Q @var{len1} , @var{len2} @key{NL} @var{str1} @var{str2} @key{NL} +Uses @var{str1} and @var{str2} as the begin-quote and end-quote +strings. If omitted, then @samp{`} and @samp{'} are the quote +delimiters. + +@item T @var{len1} , @var{len2} @key{NL} @var{str1} @var{str2} @key{NL} +Defines, though @code{pushdef}, a definition for @var{str1} +expanding to the text given by @var{str2}. This directive may appear +more than once for the same name, and its order, along with @samp{F}, is +important. + +@item V @var{number} @key{NL} +Confirms the format of the file. @code{m4} @value{VERSION} only creates +and understands frozen files where @var{number} is 1. This directive +must be the first non-comment in the file, and may not appear more than +once. +@end table + +@node Compatibility +@chapter Compatibility with other versions of @code{m4} + +@cindex compatibility +This chapter describes the differences between this implementation of +@code{m4}, and the implementation found under UNIX, notably System V, +Release 3. + +There are also differences in BSD flavors of @code{m4}. No attempt +is made to summarize these here. + +@menu +* Extensions:: Extensions in @acronym{GNU} M4 +* Incompatibilities:: Facilities in System V m4 not in GNU M4 +* Other Incompatibilities:: Other incompatibilities +@end menu + +@node Extensions +@section Extensions in @acronym{GNU} @code{m4} + +@cindex @acronym{GNU} extensions +This version of @code{m4} contains a few facilities that do not exist +in System V @code{m4}. These extra facilities are all suppressed by +using the @option{-G} command line option (@pxref{Limits control, , +Invoking m4}), unless overridden by other command line options. + +@itemize @bullet +@item +In the @code{$}@var{n} notation for macro arguments, @var{n} can contain +several digits, while the System V @code{m4} only accepts one digit. +This allows macros in @acronym{GNU} @code{m4} to take any number of +arguments, and not only nine (@pxref{Arguments}). + +This means that @code{define(`foo', `$11')} is ambiguous between +implementations. To portably choose between grabbing the first +parameter and appending 1 to the expansion, or grabbing the eleventh +parameter, you can do the following: + +@example +define(`a1', `A1') +@result{} +dnl First argument, concatenated with 1 +define(`_1', `$1')define(`first1', `_1($@@)1') +@result{} +dnl Eleventh argument, portable +define(`_9', `$9')define(`eleventh', `_9(shift(shift($@@)))') +@result{} +dnl Eleventh argument, GNU style +define(`Eleventh', `$11') +@result{} +first1(`a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k') +@result{}A1 +eleventh(`a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k') +@result{}k +Eleventh(`a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k') +@result{}k +@end example + +@item +The @code{divert} (@pxref{Divert}) macro can manage more than 9 +diversions. @acronym{GNU} @code{m4} treats all positive numbers as valid +diversions, rather than discarding diversions greater than 9. + +@item +Files included with @code{include} and @code{sinclude} are sought in a +user specified search path, if they are not found in the working +directory. The search path is specified by the @option{-I} option and the +@env{M4PATH} environment variable (@pxref{Search Path}). + +@item +Arguments to @code{undivert} can be non-numeric, in which case the named +file will be included uninterpreted in the output (@pxref{Undivert}). + +@item +Formatted output is supported through the @code{format} builtin, which +is modeled after the C library function @code{printf} (@pxref{Format}). + +@item +Searches and text substitution through regular expressions are +supported by the @code{regexp} (@pxref{Regexp}) and @code{patsubst} +(@pxref{Patsubst}) builtins. + +@item +The output of shell commands can be read into @code{m4} with +@code{esyscmd} (@pxref{Esyscmd}). + +@item +There is indirect access to any builtin macro with @code{builtin} +(@pxref{Builtin}). + +@item +Macros can be called indirectly through @code{indir} (@pxref{Indir}). + +@item +The name of the program, the current input file, and the current input +line number are accessible through the builtins @code{@w{__program__}}, +@code{@w{__file__}}, and @code{@w{__line__}} (@pxref{Location}). + +@item +The format of the output from @code{dumpdef} and macro tracing can be +controlled with @code{debugmode} (@pxref{Debug Levels}). + +@item +The destination of trace and debug output can be controlled with +@code{debugfile} (@pxref{Debug Output}). + +@item +The @code{maketemp} (@pxref{Mkstemp}) macro behaves like @code{mkstemp}, +creating a new file with a unique name on every invocation, rather than +following the insecure behavior of replacing the trailing @samp{X} +characters with the @code{m4} process id. +@end itemize + +In addition to the above extensions, @acronym{GNU} @code{m4} implements the +following command line options: @option{-F}, @option{-G}, @option{-I}, +@option{-L}, @option{-R}, @option{-V}, @option{-W}, @option{-d}, @option{-i}, +@option{-l}, @option{--debugfile} and @option{-t}. @xref{Invoking m4}, for a +description of these options. + +Also, the debugging and tracing facilities in @acronym{GNU} @code{m4} are much +more extensive than in most other versions of @code{m4}. + +@node Incompatibilities +@section Facilities in System V @code{m4} not in @acronym{GNU} @code{m4} + +The version of @code{m4} from System V contains a few facilities that +have not been implemented in @acronym{GNU} @code{m4} yet. Additionally, +@acronym{POSIX} requires some behaviors that @acronym{GNU} @code{m4} has not +implemented yet. Relying on these behaviors is non-portable, as a +future release of @acronym{GNU} @code{m4} may change. + +@itemize @bullet +@item +System V @code{m4} supports multiple arguments to @code{defn}, and +@acronym{POSIX} requires it. This is not yet implemented in @acronym{GNU} +@code{m4}. Unfortunately, this means it is not possible to mix builtins +and other text into a single macro; a helper macro is required. + +@item +@acronym{POSIX} requires an application to exit with non-zero status if +it wrote an error message to stderr. This has not yet been consistently +implemented for the various builtins that are required to issue an error +(such as @code{include} (@pxref{Include}) when a file is unreadable, +@code{eval} (@pxref{Eval}) when an argument cannot be parsed, or using +@code{m4exit} (@pxref{M4exit}) with a non-numeric argument). + +@item +Some traditional implementations only allow reading standard input +once, but @acronym{GNU} @code{m4} correctly handles multiple instances +of @samp{-} on the command line. + +@item +@acronym{POSIX} requires @code{m4wrap} (@pxref{M4wrap}) to act in FIFO +(first-in, first-out) order, but @acronym{GNU} @code{m4} currently uses +LIFO order. Furthermore, @acronym{POSIX} states that only the first +argument to @code{m4wrap} is saved for later evaluation, bug +@acronym{GNU} @code{m4} saves and processes all arguments, with output +separated by spaces. + +However, it is possible to emulate @acronym{POSIX} behavior by +including the file @file{m4-@value{VERSION}/@/examples/@/wrapfifo.m4} +from the distribution: + +@example +undivert(`wrapfifo.m4')dnl +@result{}dnl Redefine m4wrap to have FIFO semantics. +@result{}define(`_m4wrap_level', `0')dnl +@result{}define(`m4wrap', +@result{}`ifdef(`m4wrap'_m4wrap_level, +@result{} `define(`m4wrap'_m4wrap_level, +@result{} defn(`m4wrap'_m4wrap_level)`$1')', +@result{} `builtin(`m4wrap', `define(`_m4wrap_level', +@result{} incr(_m4wrap_level))dnl +@result{}m4wrap'_m4wrap_level)dnl +@result{}define(`m4wrap'_m4wrap_level, `$1')')')dnl +include(`wrapfifo.m4') +@result{} +m4wrap(`a`'m4wrap(`c +', `d')')m4wrap(`b') +@result{} +^D +@result{}abc +@end example + +@item +@acronym{POSIX} requires that all builtins that require arguments, but +are called without arguments, behave as though empty strings had been +passed. For example, @code{a`'define`'b} would expand to @code{ab}. +But @acronym{GNU} @code{m4} ignores certain builtins if they have missing +arguments, giving @code{adefineb} for the above example. + +@item +Traditional implementations handle @code{define(`f',`1')} (@pxref{Define}) +by undefining the entire stack of previous definitions, and if doing +@code{undefine(`f')} first. @acronym{GNU} @code{m4} replaces just the top +definition on the stack, as if doing @code{popdef(`f')} followed by +@code{pushdef(`f',`1')}. + +@item +@acronym{POSIX} requires @code{syscmd} (@pxref{Syscmd}) to evaluate +command output for macro expansion, but this appears to be a mistake +in @acronym{POSIX} since traditional implementations did not do this. +@acronym{GNU} @code{m4} follows traditional behavior in @code{syscmd}, and +provides the extension @code{esyscmd} that provides the @acronym{POSIX} +semantics. + +@item +At one point, @acronym{POSIX} required @code{changequote(@var{arg})} +(@pxref{Changequote}) to use newline as the close quote, but this was a +bug, and the next version of @acronym{POSIX} is anticipated to state +that using empty strings or just one argument is unspecified. +Meanwhile, the @acronym{GNU} @code{m4} behavior of treating an empty +end-quote delimiter as @samp{'} is not portable, as Solaris treats it as +repeating the start-quote delimiter, and BSD treats it as leaving the +previous end-quote delimiter unchanged. For predictable results, never +call changequote with just one argument, or with empty strings for +arguments. + +@item +At one point, @acronym{POSIX} required @code{changecom(@var{arg},)} +(@pxref{Changecom}) to make it impossible to end a comment, but this is +a bug, and the next version of @acronym{POSIX} is anticipated to state +that using empty strings is unspecified. Meanwhile, the @acronym{GNU} +@code{m4} behavior of treating an empty end-comment delimiter as newline +is not portable, as BSD treats it as leaving the previous end-comment +delimiter unchanged. It is also impossible in BSD implementations to +disable comments, even though that is required by @acronym{POSIX}. For +predictable results, never call changecom with empty strings for +arguments. + +@item +Most implementations of @code{m4} give macros a higher precedence than +comments when parsing, meaning that if the start delimiter given to +@code{changecom} (@pxref{Changecom}) starts with a macro name, comments +are effectively disabled. @acronym{POSIX} does not specify what the +precedence is, so the @acronym{GNU} @code{m4} parser recognizes +comments, then macros, then quoted strings. + +@item +Traditional implementations allow argument collection, but not string +and comment processing, to span file boundaries. Thus, if @file{a.m4} +contains @samp{len(}, and @file{b.m4} contains @samp{abc)}, +@kbd{m4 a.m4 b.m4} outputs @samp{3} with traditional @code{m4}, but +gives an error message that the end of file was encountered inside a +macro with @acronym{GNU} @code{m4}. On the other hand, traditional +implementations do end of file processing for files included with +@code{include} or @code{sinclude} (@pxref{Include}), while @acronym{GNU} +@code{m4} seamlessly integrates the content of those files. Thus +@code{include(`a.m4')include(`b.m4')} will output @samp{3} instead of +giving an error. + +@item +Traditional @code{m4} treats @code{traceon} (@pxref{Trace}) without +arguments as a global variable, independent of named macro tracing. +Also, once a macro is undefined, named tracing of that macro is lost. +On the other hand, when @acronym{GNU} @code{m4} encounters +@code{traceon} without +arguments, it turns tracing on for all existing definitions at the time, +but does not trace future definitions; @code{traceoff} without arguments +turns tracing off for all definitions regardless of whether they were +also traced by name; and tracing by name, such as with @option{-tfoo} at +the command line or @code{traceon(`foo')} in the input, is an attribute +that is preserved even if the macro is currently undefined. + +@item +@acronym{POSIX} requires @code{eval} (@pxref{Eval}) to treat all +operators with the same precedence as C. However, @acronym{GNU} @code{m4} +currently follows the traditional precedence of other @code{m4} +implementations, where bitwise and logical negation (@samp{~} and +@samp{!}) have lower precedence than equality operators, rather than +equal precedence with other unary operators. Use explicit parentheses +to ensure proper precedence. As extensions to @acronym{POSIX}, @acronym{GNU} +@code{m4} treats the shift operators @samp{<<} and @samp{>>} as +well-defined on signed integers (even though they are not in C), and +adds the exponentiation operator @samp{**}. + +@item +@acronym{POSIX} requires @code{translit} (@pxref{Translit}) to treat +each character of the second and third arguments literally, but @acronym{GNU} +@code{m4} treats @samp{-} as a range operator. + +@item +@acronym{POSIX} requires @code{m4} to honor the locale environment +variables of @env{LANG}, @env{LC_ALL}, @env{LC_CTYPE}, +@env{LC_MESSAGES}, and @env{NLSPATH}, but this has not yet been +implemented in @acronym{GNU} @code{m4}. + +@item +@acronym{POSIX} states that only unquoted leading newlines and blanks +(that is, space and tab) are ignored when collecting macro arguments. +However, this appears to be a bug in @acronym{POSIX}, since most +traditional implementations also ignore all whitespace (formfeed, +carriage return, and vertical tab). @acronym{GNU} @code{m4} follows +tradition and ignores all leading unquoted whitespace. +@end itemize + +@node Other Incompatibilities +@section Other incompatibilities + +There are a few other incompatibilities between this implementation of +@code{m4}, and the System V version. + +@itemize @bullet +@item +@acronym{GNU} @code{m4} implements sync lines differently from System V +@code{m4}, when text is being diverted. @acronym{GNU} @code{m4} outputs +the sync lines when the text is being diverted, and System V @code{m4} +when the diverted text is being brought back. + +The problem is which lines and file names should be attached to text that +is being, or has been, diverted. System V @code{m4} regards all the +diverted text as being generated by the source line containing the +@code{undivert} call, whereas @acronym{GNU} @code{m4} regards the +diverted text as being generated at the time it is diverted. + +The sync line option is used mostly when using @code{m4} as +a front end to a compiler. If a diverted line causes a compiler error, +the error messages should most probably refer to the place where the +diversion were made, and not where it was inserted again. + +@item +@acronym{GNU} @code{m4} makes no attempt at prohibiting self-referential +definitions like: + +@comment ignore +@example +define(`x', `x') +@result{} +define(`x', `x ') +@result{} +@end example + +There is nothing inherently wrong with defining @samp{x} to +return @samp{x}. The wrong thing is to expand @samp{x} unquoted. +In @code{m4}, one might use macros to hold strings, as we do for +variables in other programming languages, further checking them with: + +@comment ignore +@example +ifelse(defn(`@var{holder}'), `@var{value}', @dots{}) +@end example + +@noindent +In cases like this one, an interdiction for a macro to hold its own +name would be a useless limitation. Of course, this leaves more rope +for the @acronym{GNU} @code{m4} user to hang himself! Rescanning hangs may be +avoided through careful programming, a little like for endless loops +in traditional programming languages. +@end itemize + +@node Answers +@chapter Correct version of some examples + +Some of the examples in this manuals are buggy or not very robust, for +demonstration purposes. Improved versions of these composite macros are +presented here. + +@menu +* Improved exch:: Solution for @code{exch} +* Improved forloop:: Solution for @code{forloop} +* Improved foreach:: Solution for @code{foreach} +* Improved cleardivert:: Solution for @code{cleardivert} +* Improved fatal_error:: Solution for @code{fatal_error} +@end menu + +@node Improved exch +@section Solution for @code{exch} + +The @code{exch} macro (@pxref{Arguments}) as presented requires clients +to double quote their arguments. A nicer definition, which lets +clients follow the rule of thumb of one level of quoting per level of +parentheses, involves adding quotes in the definition of @code{exch}, as +follows: + +@example +define(`exch', ``$2', `$1'') +@result{} +define(exch(`expansion text', `macro')) +@result{} +macro +@result{}expansion text +@end example + +@node Improved forloop +@section Solution for @code{forloop} + +The @code{forloop} macro (@pxref{Forloop}) as presented earlier can go +into an infinite loop if given an iterator that is not parsed as a macro +name. It does not do any sanity checking on its numeric bounds, and +only permits decimal numbers for bounds. Here is an improved version, +shipped as @file{m4-@value{VERSION}/@/examples/@/forloop2.m4}; this +version also optimizes based on the fact that the starting bound does +not need to be passed to the helper @code{@w{_forloop}}. + +@example +undivert(`forloop2.m4')dnl +@result{}divert(`-1') +@result{}# forloop(var, from, to, stmt) - improved version: +@result{}# works even if VAR is not a strict macro name +@result{}# performs sanity check that FROM is larger than TO +@result{}# allows complex numerical expressions in TO and FROM +@result{}define(`forloop', `ifelse(eval(`($3) >= ($2)'), `1', +@result{} `pushdef(`$1', eval(`$2'))_forloop(`$1', +@result{} eval(`$3'), `$4')popdef(`$1')')') +@result{}define(`_forloop', +@result{} `$3`'ifelse(indir(`$1'), `$2', `', +@result{} `define(`$1', incr(indir(`$1')))$0($@@)')') +@result{}divert`'dnl +include(`forloop2.m4') +@result{} +forloop(`i', `2', `1', `no iteration occurs') +@result{} +forloop(`', `1', `2', ` odd iterator name') +@result{} odd iterator name odd iterator name +forloop(`i', `5 + 5', `0xc', ` 0x`'eval(i, `16')') +@result{} 0xa 0xb 0xc +forloop(`i', `a', `b', `non-numeric bounds') +@error{}m4:stdin:6: bad expression in eval (bad input): (b) >= (a) +@result{} +@end example + +Of course, it is possible to make even more improvements, such as +adding an optional step argument, or allowing iteration through +descending sequences. @acronym{GNU} Autoconf provides some of these +additional bells and whistles in its @code{m4_for} macro. + +@node Improved foreach +@section Solution for @code{foreach} + +The @code{foreach} and @code{foreachq} macros (@pxref{Foreach}) as +presented earlier each have flaws. First, we will examine and fix the +quadratic behavior of @code{foreachq}: + +@example +include(`foreachq.m4') +@result{} +traceon(`shift')debugmode(`aq') +@result{} +foreachq(`x', ``1', `2', `3', `4'', `x +')dnl +@result{}1 +@error{}m4trace: -3- shift(`1', `2', `3', `4') +@error{}m4trace: -2- shift(`1', `2', `3', `4') +@result{}2 +@error{}m4trace: -4- shift(`1', `2', `3', `4') +@error{}m4trace: -3- shift(`2', `3', `4') +@error{}m4trace: -3- shift(`1', `2', `3', `4') +@error{}m4trace: -2- shift(`2', `3', `4') +@result{}3 +@error{}m4trace: -5- shift(`1', `2', `3', `4') +@error{}m4trace: -4- shift(`2', `3', `4') +@error{}m4trace: -3- shift(`3', `4') +@error{}m4trace: -4- shift(`1', `2', `3', `4') +@error{}m4trace: -3- shift(`2', `3', `4') +@error{}m4trace: -2- shift(`3', `4') +@result{}4 +@error{}m4trace: -6- shift(`1', `2', `3', `4') +@error{}m4trace: -5- shift(`2', `3', `4') +@error{}m4trace: -4- shift(`3', `4') +@error{}m4trace: -3- shift(`4') +@end example + +Each successive iteration was adding more quoted @code{shift} +invocations, and the entire list contents were passing through every +iteration. In general, when recursing, it is a good idea to make the +recursion use fewer arguments, rather than adding additional quoted +uses of @code{shift}. By doing so, @code{m4} uses less memory, invokes +fewer macros, is less likely to run into machine limits, and most +importantly, performs faster. The fixed version of @code{foreachq} can +be found in @file{m4-@value{VERSION}/@/examples/@/foreachq2.m4}: + +@example +include(`foreachq2.m4') +@result{} +undivert(`foreachq2.m4')dnl +@result{}include(`quote.m4')dnl +@result{}divert(`-1') +@result{}# foreachq(x, `item_1, item_2, ..., item_n', stmt) +@result{}# quoted list, improved version +@result{}define(`foreachq', `pushdef(`$1')_foreachq($@@)popdef(`$1')') +@result{}define(`_arg1q', ``$1'') +@result{}define(`_rest', `ifelse(`$#', `1', `', `dquote(shift($@@))')') +@result{}define(`_foreachq', `ifelse(`$2', `', `', +@result{} `define(`$1', _arg1q($2))$3`'$0(`$1', _rest($2), `$3')')') +@result{}divert`'dnl +traceon(`shift')debugmode(`aq') +@result{} +foreachq(`x', ``1', `2', `3', `4'', `x +')dnl +@result{}1 +@error{}m4trace: -3- shift(`1', `2', `3', `4') +@result{}2 +@error{}m4trace: -3- shift(`2', `3', `4') +@result{}3 +@error{}m4trace: -3- shift(`3', `4') +@result{}4 +@end example + +Note that the fixed version calls unquoted helper macros in +@code{@w{_foreachq}} to trim elements immediately; those helper macros +in turn must re-supply the layer of quotes lost in the macro invocation. +Contrast the use of @code{@w{_arg1q}}, which quotes the first list +element, with @code{@w{_arg1}} of the earlier implementation that +returned the first list element directly. + +For a different approach, the improved version of @code{foreach}, +available in @file{m4-@value{VERSION}/@/examples/@/foreach2.m4}, simply +overquotes the arguments to @code{@w{_foreach}} to begin with, using +@code{dquote_elt}. Then @code{@w{_foreach}} can just use +@code{@w{_arg1}} to remove the extra layer of quoting that was added up +front: + +@example +include(`foreach2.m4') +@result{} +undivert(`foreach2.m4')dnl +@result{}include(`quote.m4')dnl +@result{}divert(`-1') +@result{}# foreach(x, (item_1, item_2, ..., item_n), stmt) +@result{}# parenthesized list, improved version +@result{}define(`foreach', `pushdef(`$1')_foreach(`$1', +@result{} (dquote(dquote_elt$2)), `$3')popdef(`$1')') +@result{}define(`_arg1', `$1') +@result{}define(`_foreach', `ifelse(`$2', `(`')', `', +@result{} `define(`$1', _arg1$2)$3`'$0(`$1', (dquote(shift$2)), `$3')')') +@result{}divert`'dnl +traceon(`shift')debugmode(`aq') +@result{} +foreach(`x', `(`1', `2', `3', `4')', `x +')dnl +@error{}m4trace: -4- shift(`1', `2', `3', `4') +@error{}m4trace: -4- shift(`2', `3', `4') +@error{}m4trace: -4- shift(`3', `4') +@result{}1 +@error{}m4trace: -3- shift(``1'', ``2'', ``3'', ``4'') +@result{}2 +@error{}m4trace: -3- shift(``2'', ``3'', ``4'') +@result{}3 +@error{}m4trace: -3- shift(``3'', ``4'') +@result{}4 +@error{}m4trace: -3- shift(``4'') +@end example + +In summary, recursion over list elements is trickier than it appeared at +first glance, but provides a powerful idiom within @code{m4} processing. +As a final demonstration, both list styles are now able to handle +several scenarios that would wreak havoc on the original +implementations. This points out one other difference between the two +list styles. @code{foreach} evaluates unquoted list elements only once, +in preparation for calling @code{@w{_foreach}}. But @code{foreachq} +evaluates unquoted list elements twice while visiting the first list +element, once in @code{@w{_arg1q}} and once in @code{@w{_rest}}. When +deciding which list style to use, one must take into account whether +repeating the side effects of unquoted list elements will have any +detrimental effects. + +@example +include(`foreach2.m4') +@result{} +include(`foreachq2.m4') +@result{} +dnl 0-element list: +foreach(`x', `', `') / foreachq(`x', `', `') +@result{} /@w{ } +dnl 1-element list of empty element +foreach(`x', `()', `') / foreachq(`x', ``'', `') +@result{}<> / <> +dnl 2-element list of empty elements +foreach(`x', `(`',`')', `') / foreachq(`x', ``',`'', `') +@result{}<><> / <><> +dnl 1-element list of a comma +foreach(`x', `(`,')', `') / foreachq(`x', ``,'', `') +@result{}<,> / <,> +dnl 2-element list of unbalanced parentheses +foreach(`x', `(`(', `)')', `') / foreachq(`x', ``(', `)'', `') +@result{}<(><)> / <(><)> +define(`active', `ACT, IVE') +@result{} +traceon(`active') +@result{} +dnl list of unquoted macros; expansion occurs before recursion +foreach(`x', `(active, active)', ` +')dnl +@error{}m4trace: -4- active -> `ACT, IVE' +@error{}m4trace: -4- active -> `ACT, IVE' +@result{} +@result{} +@result{} +@result{} +foreachq(`x', `active, active', ` +')dnl +@error{}m4trace: -3- active -> `ACT, IVE' +@error{}m4trace: -3- active -> `ACT, IVE' +@result{} +@error{}m4trace: -3- active -> `ACT, IVE' +@error{}m4trace: -3- active -> `ACT, IVE' +@result{} +@result{} +@result{} +dnl list of quoted macros; expansion occurs during recursion +foreach(`x', `(`active', `active')', ` +')dnl +@error{}m4trace: -1- active -> `ACT, IVE' +@result{} +@error{}m4trace: -1- active -> `ACT, IVE' +@result{} +foreachq(`x', ``active', `active'', ` +')dnl +@error{}m4trace: -1- active -> `ACT, IVE' +@result{} +@error{}m4trace: -1- active -> `ACT, IVE' +@result{} +dnl list of double-quoted macro names; no expansion +foreach(`x', `(``active'', ``active'')', ` +')dnl +@result{} +@result{} +foreachq(`x', ```active'', ``active''', ` +')dnl +@result{} +@result{} +@end example + +@node Improved cleardivert +@section Solution for @code{cleardivert} + +The @code{cleardivert} macro (@pxref{Cleardivert}) cannot, as it stands, be +called without arguments to clear all pending diversions. That is +because using undivert with an empty string for an argument is different +than using it with no arguments at all. Compare the earlier definition +with one that takes the number of arguments into account: + +@example +define(`cleardivert', + `pushdef(`_n', divnum)divert(`-1')undivert($@@)divert(_n)popdef(`_n')') +@result{} +divert(`1')one +divert +@result{} +cleardivert +@result{} +undivert +@result{}one +@result{} +define(`cleardivert', + `pushdef(`_num', divnum)divert(`-1')ifelse(`$#', `0', + `undivert`'', `undivert($@@)')divert(_num)popdef(`_num')') +@result{} +divert(`2')two +divert +@result{} +cleardivert +@result{} +undivert +@result{} +@end example + +@node Improved fatal_error +@section Solution for @code{fatal_error} + +The @code{fatal_error} macro (@pxref{M4exit}) is not robust to versions +of @acronym{GNU} M4 earlier than 1.4.8, where invoking +@code{@w{__file__}} (@pxref{Location}) inside @code{m4wrap} would result +in an empty string, and @code{@w{__line__}} resulted in @samp{0} even +though all files start at line 1. Furthermore, versions earlier than +1.4.6 did not support the @code{@w{__program__}} macro. If you want +@code{fatal_error} to work across the entire 1.4.x release series, a +better implementation would be: + +@example +define(`fatal_error', + `errprint(ifdef(`__program__', `__program__', ``m4'')'dnl +`:ifelse(__line__, `0', `', + `__file__:__line__:')` fatal error: $* +')m4exit(`1')') +@result{} +m4wrap(`divnum(`demo of internal message') +fatal_error(`inside wrapped text')') +@result{} +^D +@error{}m4:stdin:6: Warning: excess arguments to builtin `divnum' ignored +@result{}0 +@error{}m4:stdin:6: fatal error: inside wrapped text +@end example + +@c ========================================================== Appendices + +@node Copying This Manual +@appendix How to make copies of this manual +@cindex License + +@menu +* GNU Free Documentation License:: License for copying this manual +@end menu + +@include fdl.texi + +@node Indices +@appendix Indices of concepts and macros + +@menu +* Concept index:: Index for many concepts +* Macro index:: Index for all @code{m4} macros +@end menu + +@node Concept index +@appendixsec Index for many concepts + +@printindex cp + +@node Macro index +@appendixsec Index for all @code{m4} macros + +References are exclusively to the places where a builtin is introduced +the first time. + +@iftex +@sp 1 +@end iftex + +@printindex fn + +@bye + +@c Local Variables: +@c coding: ISO-8859-1 +@c fill-column: 72 +@c ispell-local-dictionary: "american" +@c indent-tabs-mode: nil +@c whitespace-check-buffer-indent: nil +@c End: diff --git a/src/bin/m4/doc/mdate-sh b/src/bin/m4/doc/mdate-sh new file mode 100755 index 0000000000..cd916c0a34 --- /dev/null +++ b/src/bin/m4/doc/mdate-sh @@ -0,0 +1,201 @@ +#!/bin/sh +# Get modification time of a file or directory and pretty-print it. + +scriptversion=2005-06-29.22 + +# Copyright (C) 1995, 1996, 1997, 2003, 2004, 2005 Free Software +# Foundation, Inc. +# written by Ulrich Drepper , June 1995 +# +# 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +case $1 in + '') + echo "$0: No file. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: mdate-sh [--help] [--version] FILE + +Pretty-print the modification time of FILE. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "mdate-sh $scriptversion" + exit $? + ;; +esac + +# Prevent date giving response in another language. +LANG=C +export LANG +LC_ALL=C +export LC_ALL +LC_TIME=C +export LC_TIME + +# GNU ls changes its time format in response to the TIME_STYLE +# variable. Since we cannot assume `unset' works, revert this +# variable to its documented default. +if test "${TIME_STYLE+set}" = set; then + TIME_STYLE=posix-long-iso + export TIME_STYLE +fi + +save_arg1=$1 + +# Find out how to get the extended ls output of a file or directory. +if ls -L /dev/null 1>/dev/null 2>&1; then + ls_command='ls -L -l -d' +else + ls_command='ls -l -d' +fi + +# A `ls -l' line looks as follows on OS/2. +# drwxrwx--- 0 Aug 11 2001 foo +# This differs from Unix, which adds ownership information. +# drwxrwx--- 2 root root 4096 Aug 11 2001 foo +# +# To find the date, we split the line on spaces and iterate on words +# until we find a month. This cannot work with files whose owner is a +# user named `Jan', or `Feb', etc. However, it's unlikely that `/' +# will be owned by a user whose name is a month. So we first look at +# the extended ls output of the root directory to decide how many +# words should be skipped to get the date. + +# On HPUX /bin/sh, "set" interprets "-rw-r--r--" as options, so the "x" below. +set x`ls -l -d /` + +# Find which argument is the month. +month= +command= +until test $month +do + shift + # Add another shift to the command. + command="$command shift;" + case $1 in + Jan) month=January; nummonth=1;; + Feb) month=February; nummonth=2;; + Mar) month=March; nummonth=3;; + Apr) month=April; nummonth=4;; + May) month=May; nummonth=5;; + Jun) month=June; nummonth=6;; + Jul) month=July; nummonth=7;; + Aug) month=August; nummonth=8;; + Sep) month=September; nummonth=9;; + Oct) month=October; nummonth=10;; + Nov) month=November; nummonth=11;; + Dec) month=December; nummonth=12;; + esac +done + +# Get the extended ls output of the file or directory. +set dummy x`eval "$ls_command \"\$save_arg1\""` + +# Remove all preceding arguments +eval $command + +# Because of the dummy argument above, month is in $2. +# +# On a POSIX system, we should have +# +# $# = 5 +# $1 = file size +# $2 = month +# $3 = day +# $4 = year or time +# $5 = filename +# +# On Darwin 7.7.0 and 7.6.0, we have +# +# $# = 4 +# $1 = day +# $2 = month +# $3 = year or time +# $4 = filename + +# Get the month. +case $2 in + Jan) month=January; nummonth=1;; + Feb) month=February; nummonth=2;; + Mar) month=March; nummonth=3;; + Apr) month=April; nummonth=4;; + May) month=May; nummonth=5;; + Jun) month=June; nummonth=6;; + Jul) month=July; nummonth=7;; + Aug) month=August; nummonth=8;; + Sep) month=September; nummonth=9;; + Oct) month=October; nummonth=10;; + Nov) month=November; nummonth=11;; + Dec) month=December; nummonth=12;; +esac + +case $3 in + ???*) day=$1;; + *) day=$3; shift;; +esac + +# Here we have to deal with the problem that the ls output gives either +# the time of day or the year. +case $3 in + *:*) set `date`; eval year=\$$# + case $2 in + Jan) nummonthtod=1;; + Feb) nummonthtod=2;; + Mar) nummonthtod=3;; + Apr) nummonthtod=4;; + May) nummonthtod=5;; + Jun) nummonthtod=6;; + Jul) nummonthtod=7;; + Aug) nummonthtod=8;; + Sep) nummonthtod=9;; + Oct) nummonthtod=10;; + Nov) nummonthtod=11;; + Dec) nummonthtod=12;; + esac + # For the first six month of the year the time notation can also + # be used for files modified in the last year. + if (expr $nummonth \> $nummonthtod) > /dev/null; + then + year=`expr $year - 1` + fi;; + *) year=$3;; +esac + +# The result. +echo $day $month $year + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/src/bin/m4/doc/stamp-vti b/src/bin/m4/doc/stamp-vti new file mode 100644 index 0000000000..c9d4e2fb98 --- /dev/null +++ b/src/bin/m4/doc/stamp-vti @@ -0,0 +1,4 @@ +@set UPDATED 20 November 2006 +@set UPDATED-MONTH November 2006 +@set EDITION 1.4.8 +@set VERSION 1.4.8 diff --git a/src/bin/m4/doc/texinfo.tex b/src/bin/m4/doc/texinfo.tex new file mode 100644 index 0000000000..ab95370db4 --- /dev/null +++ b/src/bin/m4/doc/texinfo.tex @@ -0,0 +1,7483 @@ +% texinfo.tex -- TeX macros to handle Texinfo files. +% +% Load plain if necessary, i.e., if running under initex. +\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi +% +\def\texinfoversion{2006-10-15.13} +% +% Copyright (C) 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, +% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free +% Software Foundation, Inc. +% +% This texinfo.tex file 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, or (at +% your option) any later version. +% +% This texinfo.tex file 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 texinfo.tex file; see the file COPYING. If not, write +% to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +% Boston, MA 02110-1301, USA. +% +% As a special exception, when this file is read by TeX when processing +% a Texinfo source document, you may use the result without +% restriction. (This has been our intent since Texinfo was invented.) +% +% Please try the latest version of texinfo.tex before submitting bug +% reports; you can get the latest version from: +% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or +% ftp://tug.org/tex/texinfo.tex +% (and all CTAN mirrors, see http://www.ctan.org). +% The texinfo.tex in any given distribution could well be out +% of date, so if that's what you're using, please check. +% +% Send bug reports to bug-texinfo@gnu.org. Please include including a +% complete document in each bug report with which we can reproduce the +% problem. Patches are, of course, greatly appreciated. +% +% To process a Texinfo manual with TeX, it's most reliable to use the +% texi2dvi shell script that comes with the distribution. For a simple +% manual foo.texi, however, you can get away with this: +% tex foo.texi +% texindex foo.?? +% tex foo.texi +% tex foo.texi +% dvips foo.dvi -o # or whatever; this makes foo.ps. +% The extra TeX runs get the cross-reference information correct. +% Sometimes one run after texindex suffices, and sometimes you need more +% than two; texi2dvi does it as many times as necessary. +% +% It is possible to adapt texinfo.tex for other languages, to some +% extent. You can get the existing language-specific files from the +% full Texinfo distribution. +% +% The GNU Texinfo home page is http://www.gnu.org/software/texinfo. + + +\message{Loading texinfo [version \texinfoversion]:} + +% If in a .fmt file, print the version number +% and turn on active characters that we couldn't do earlier because +% they might have appeared in the input file name. +\everyjob{\message{[Texinfo version \texinfoversion]}% + \catcode`+=\active \catcode`\_=\active} + +\message{Basics,} +\chardef\other=12 + +% We never want plain's \outer definition of \+ in Texinfo. +% For @tex, we can use \tabalign. +\let\+ = \relax + +% Save some plain tex macros whose names we will redefine. +\let\ptexb=\b +\let\ptexbullet=\bullet +\let\ptexc=\c +\let\ptexcomma=\, +\let\ptexdot=\. +\let\ptexdots=\dots +\let\ptexend=\end +\let\ptexequiv=\equiv +\let\ptexexclam=\! +\let\ptexfootnote=\footnote +\let\ptexgtr=> +\let\ptexhat=^ +\let\ptexi=\i +\let\ptexindent=\indent +\let\ptexinsert=\insert +\let\ptexlbrace=\{ +\let\ptexless=< +\let\ptexnewwrite\newwrite +\let\ptexnoindent=\noindent +\let\ptexplus=+ +\let\ptexrbrace=\} +\let\ptexslash=\/ +\let\ptexstar=\* +\let\ptext=\t + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Pre-3.0. +\else + \def\linenumber{l.\the\inputlineno:\space} +\fi + +% Set up fixed words for English if not already set. +\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi +\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi +\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi +\ifx\putwordin\undefined \gdef\putwordin{in}\fi +\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi +\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi +\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi +\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi +\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi +\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi +\ifx\putwordof\undefined \gdef\putwordof{of}\fi +\ifx\putwordon\undefined \gdef\putwordon{on}\fi +\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi +\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi +\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi +\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi +\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi +\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi +\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi +% +\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi +\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi +\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi +\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi +\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi +\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi +\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi +\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi +\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi +\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi +\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi +\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi +% +\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi +\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi +\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi +\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi +\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi + +% Since the category of space is not known, we have to be careful. +\chardef\spacecat = 10 +\def\spaceisspace{\catcode`\ =\spacecat} + +% sometimes characters are active, so we need control sequences. +\chardef\colonChar = `\: +\chardef\commaChar = `\, +\chardef\dashChar = `\- +\chardef\dotChar = `\. +\chardef\exclamChar= `\! +\chardef\lquoteChar= `\` +\chardef\questChar = `\? +\chardef\rquoteChar= `\' +\chardef\semiChar = `\; +\chardef\underChar = `\_ + +% Ignore a token. +% +\def\gobble#1{} + +% The following is used inside several \edef's. +\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} + +% Hyphenation fixes. +\hyphenation{ + Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script + ap-pen-dix bit-map bit-maps + data-base data-bases eshell fall-ing half-way long-est man-u-script + man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm + par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces + spell-ing spell-ings + stand-alone strong-est time-stamp time-stamps which-ever white-space + wide-spread wrap-around +} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen\bindingoffset +\newdimen\normaloffset +\newdimen\pagewidth \newdimen\pageheight + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. We also make +% some effort to order the tracing commands to reduce output in the log +% file; cf. trace.sty in LaTeX. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\def\loggingall{% + \tracingstats2 + \tracingpages1 + \tracinglostchars2 % 2 gives us more in etex + \tracingparagraphs1 + \tracingoutput1 + \tracingmacros2 + \tracingrestores1 + \showboxbreadth\maxdimen \showboxdepth\maxdimen + \ifx\eTeXversion\undefined\else % etex gives us more logging + \tracingscantokens1 + \tracingifs1 + \tracinggroups1 + \tracingnesting2 + \tracingassigns1 + \fi + \tracingcommands3 % 3 gives us more in etex + \errorcontextlines16 +}% + +% add check for \lastpenalty to plain's definitions. If the last thing +% we did was a \nobreak, we don't want to insert more space. +% +\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount + \removelastskip\penalty-50\smallskip\fi\fi} +\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount + \removelastskip\penalty-100\medskip\fi\fi} +\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount + \removelastskip\penalty-200\bigskip\fi\fi} + +% For @cropmarks command. +% Do @cropmarks to get crop marks. +% +\newif\ifcropmarks +\let\cropmarks = \cropmarkstrue +% +% Dimensions to add cropmarks at corners. +% Added by P. A. MacKay, 12 Nov. 1986 +% +\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines +\newdimen\cornerlong \cornerlong=1pc +\newdimen\cornerthick \cornerthick=.3pt +\newdimen\topandbottommargin \topandbottommargin=.75in + +% Main output routine. +\chardef\PAGE = 255 +\output = {\onepageout{\pagecontents\PAGE}} + +\newbox\headlinebox +\newbox\footlinebox + +% \onepageout takes a vbox as an argument. Note that \pagecontents +% does insertions, but you have to call it yourself. +\def\onepageout#1{% + \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi + % + \ifodd\pageno \advance\hoffset by \bindingoffset + \else \advance\hoffset by -\bindingoffset\fi + % + % Do this outside of the \shipout so @code etc. will be expanded in + % the headline as they should be, not taken literally (outputting ''code). + \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}% + \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}% + % + {% + % Have to do this stuff outside the \shipout because we want it to + % take effect in \write's, yet the group defined by the \vbox ends + % before the \shipout runs. + % + \indexdummies % don't expand commands in the output. + \normalturnoffactive % \ in index entries must not stay \, e.g., if + % the page break happens to be in the middle of an example. + % We don't want .vr (or whatever) entries like this: + % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}} + % "\acronym" won't work when it's read back in; + % it needs to be + % {\code {{\tt \backslashcurfont }acronym} + \shipout\vbox{% + % Do this early so pdf references go to the beginning of the page. + \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi + % + \ifcropmarks \vbox to \outervsize\bgroup + \hsize = \outerhsize + \vskip-\topandbottommargin + \vtop to0pt{% + \line{\ewtop\hfil\ewtop}% + \nointerlineskip + \line{% + \vbox{\moveleft\cornerthick\nstop}% + \hfill + \vbox{\moveright\cornerthick\nstop}% + }% + \vss}% + \vskip\topandbottommargin + \line\bgroup + \hfil % center the page within the outer (page) hsize. + \ifodd\pageno\hskip\bindingoffset\fi + \vbox\bgroup + \fi + % + \unvbox\headlinebox + \pagebody{#1}% + \ifdim\ht\footlinebox > 0pt + % Only leave this space if the footline is nonempty. + % (We lessened \vsize for it in \oddfootingyyy.) + % The \baselineskip=24pt in plain's \makefootline has no effect. + \vskip 24pt + \unvbox\footlinebox + \fi + % + \ifcropmarks + \egroup % end of \vbox\bgroup + \hfil\egroup % end of (centering) \line\bgroup + \vskip\topandbottommargin plus1fill minus1fill + \boxmaxdepth = \cornerthick + \vbox to0pt{\vss + \line{% + \vbox{\moveleft\cornerthick\nsbot}% + \hfill + \vbox{\moveright\cornerthick\nsbot}% + }% + \nointerlineskip + \line{\ewbot\hfil\ewbot}% + }% + \egroup % \vbox from first cropmarks clause + \fi + }% end of \shipout\vbox + }% end of group with \indexdummies + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi +} + +\newinsert\margin \dimen\margin=\maxdimen + +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +% marginal hacks, juha@viisa.uucp (Juha Takala) +\ifvoid\margin\else % marginal info is present + \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi +\dimen@=\dp#1 \unvbox#1 +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% +\def\parsearg{\parseargusing{}} +\def\parseargusing#1#2{% + \def\argtorun{#2}% + \begingroup + \obeylines + \spaceisspace + #1% + \parseargline\empty% Insert the \empty token, see \finishparsearg below. +} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + \argremovecomment #1\comment\ArgTerm% + }% +} + +% First remove any @comment, then any @c comment. +\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} +\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} + +% Each occurence of `\^^M' or `\^^M' is replaced by a single space. +% +% \argremovec might leave us with trailing space, e.g., +% @end itemize @c foo +% This space token undergoes the same procedure and is eventually removed +% by \finishparsearg. +% +\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} +\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} +\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% + \def\temp{#3}% + \ifx\temp\empty + % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: + \let\temp\finishparsearg + \else + \let\temp\argcheckspaces + \fi + % Put the space token in: + \temp#1 #3\ArgTerm +} + +% If a _delimited_ argument is enclosed in braces, they get stripped; so +% to get _exactly_ the rest of the line, we had to prevent such situation. +% We prepended an \empty token at the very beginning and we expand it now, +% just before passing the control to \argtorun. +% (Similarily, we have to think about #3 of \argcheckspacesY above: it is +% either the null string, or it ends with \^^M---thus there is no danger +% that a pair of braces would be stripped. +% +% But first, we have to remove the trailing space token. +% +\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} + +% \parseargdef\foo{...} +% is roughly equivalent to +% \def\foo{\parsearg\Xfoo} +% \def\Xfoo#1{...} +% +% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my +% favourite TeX trick. --kasal, 16nov03 + +\def\parseargdef#1{% + \expandafter \doparseargdef \csname\string#1\endcsname #1% +} +\def\doparseargdef#1#2{% + \def#2{\parsearg#1}% + \def#1##1% +} + +% Several utility definitions with active space: +{ + \obeyspaces + \gdef\obeyedspace{ } + + % Make each space character in the input produce a normal interword + % space in the output. Don't allow a line break at this space, as this + % is used only in environments like @example, where each line of input + % should produce a line of output anyway. + % + \gdef\sepspaces{\obeyspaces\let =\tie} + + % If an index command is used in an @example environment, any spaces + % therein should become regular spaces in the raw index file, not the + % expansion of \tie (\leavevmode \penalty \@M \ ). + \gdef\unsepspaces{\let =\space} +} + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +% Define the framework for environments in texinfo.tex. It's used like this: +% +% \envdef\foo{...} +% \def\Efoo{...} +% +% It's the responsibility of \envdef to insert \begingroup before the +% actual body; @end closes the group after calling \Efoo. \envdef also +% defines \thisenv, so the current environment is known; @end checks +% whether the environment name matches. The \checkenv macro can also be +% used to check whether the current environment is the one expected. +% +% Non-false conditionals (@iftex, @ifset) don't fit into this, so they +% are not treated as enviroments; they don't open a group. (The +% implementation of @end takes care not to call \endgroup in this +% special case.) + + +% At runtime, environments start with this: +\def\startenvironment#1{\begingroup\def\thisenv{#1}} +% initialize +\let\thisenv\empty + +% ... but they get defined via ``\envdef\foo{...}'': +\long\def\envdef#1#2{\def#1{\startenvironment#1#2}} +\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} + +% Check whether we're in the right environment: +\def\checkenv#1{% + \def\temp{#1}% + \ifx\thisenv\temp + \else + \badenverr + \fi +} + +% Evironment mismatch, #1 expected: +\def\badenverr{% + \errhelp = \EMsimple + \errmessage{This command can appear only \inenvironment\temp, + not \inenvironment\thisenv}% +} +\def\inenvironment#1{% + \ifx#1\empty + out of any environment% + \else + in environment \expandafter\string#1% + \fi +} + +% @end foo executes the definition of \Efoo. +% But first, it executes a specialized version of \checkenv +% +\parseargdef\end{% + \if 1\csname iscond.#1\endcsname + \else + % The general wording of \badenverr may not be ideal, but... --kasal, 06nov03 + \expandafter\checkenv\csname#1\endcsname + \csname E#1\endcsname + \endgroup + \fi +} + +\newhelp\EMsimple{Press RETURN to continue.} + + +%% Simple single-character @ commands + +% @@ prints an @ +% Kludge this until the fonts are right (grr). +\def\@{{\tt\char64}} + +% This is turned off because it was never documented +% and you can use @w{...} around a quote to suppress ligatures. +%% Define @` and @' to be the same as ` and ' +%% but suppressing ligatures. +%\def\`{{`}} +%\def\'{{'}} + +% Used to generate quoted braces. +\def\mylbrace {{\tt\char123}} +\def\myrbrace {{\tt\char125}} +\let\{=\mylbrace +\let\}=\myrbrace +\begingroup + % Definitions to produce \{ and \} commands for indices, + % and @{ and @} for the aux/toc files. + \catcode`\{ = \other \catcode`\} = \other + \catcode`\[ = 1 \catcode`\] = 2 + \catcode`\! = 0 \catcode`\\ = \other + !gdef!lbracecmd[\{]% + !gdef!rbracecmd[\}]% + !gdef!lbraceatcmd[@{]% + !gdef!rbraceatcmd[@}]% +!endgroup + +% @comma{} to avoid , parsing problems. +\let\comma = , + +% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent +% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. +\let\, = \c +\let\dotaccent = \. +\def\ringaccent#1{{\accent23 #1}} +\let\tieaccent = \t +\let\ubaraccent = \b +\let\udotaccent = \d + +% Other special characters: @questiondown @exclamdown @ordf @ordm +% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. +\def\questiondown{?`} +\def\exclamdown{!`} +\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}} +\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}} + +% Dotless i and dotless j, used for accents. +\def\imacro{i} +\def\jmacro{j} +\def\dotless#1{% + \def\temp{#1}% + \ifx\temp\imacro \ptexi + \else\ifx\temp\jmacro \j + \else \errmessage{@dotless can be used only with i or j}% + \fi\fi +} + +% The \TeX{} logo, as in plain, but resetting the spacing so that a +% period following counts as ending a sentence. (Idea found in latex.) +% +\edef\TeX{\TeX \spacefactor=1000 } + +% @LaTeX{} logo. Not quite the same results as the definition in +% latex.ltx, since we use a different font for the raised A; it's most +% convenient for us to use an explicitly smaller font, rather than using +% the \scriptstyle font (since we don't reset \scriptstyle and +% \scriptscriptstyle). +% +\def\LaTeX{% + L\kern-.36em + {\setbox0=\hbox{T}% + \vbox to \ht0{\hbox{\selectfonts\lllsize A}\vss}}% + \kern-.15em + \TeX +} + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + % Avoid using \@M directly, because that causes trouble + % if the definition is written into an index file. + \global\let\tiepenalty = \@M + \gdef\tie{\leavevmode\penalty\tiepenalty\ } +} + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\hfil\break\hbox{}\ignorespaces} + +% @/ allows a line break. +\let\/=\allowbreak + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=\endofsentencespacefactor\space} + +% @! is an end-of-sentence bang. +\def\!{!\spacefactor=\endofsentencespacefactor\space} + +% @? is an end-of-sentence query. +\def\?{?\spacefactor=\endofsentencespacefactor\space} + +% @frenchspacing on|off says whether to put extra space after punctuation. +% +\def\onword{on} +\def\offword{off} +% +\parseargdef\frenchspacing{% + \def\temp{#1}% + \ifx\temp\onword \plainfrenchspacing + \else\ifx\temp\offword \plainnonfrenchspacing + \else + \errhelp = \EMsimple + \errmessage{Unknown @frenchspacing option `\temp', must be on/off}% + \fi\fi +} + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +% Another complication is that the group might be very large. This can +% cause the glue on the previous page to be unduly stretched, because it +% does not have much material. In this case, it's better to add an +% explicit \vfill so that the extra space is at the bottom. The +% threshold for doing this is if the group is more than \vfilllimit +% percent of a page (\vfilllimit can be changed inside of @tex). +% +\newbox\groupbox +\def\vfilllimit{0.7} +% +\envdef\group{% + \ifnum\catcode`\^^M=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + \startsavinginserts + % + \setbox\groupbox = \vtop\bgroup + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% The \vtop produces a box with normal height and large depth; thus, TeX puts +% \baselineskip glue before it, and (when the next line of text is done) +% \lineskip glue after it. Thus, space below is not quite equal to space +% above. But it's pretty close. +\def\Egroup{% + % To get correct interline space between the last line of the group + % and the first line afterwards, we have to propagate \prevdepth. + \endgraf % Not \par, as it may have been set to \lisppar. + \global\dimen1 = \prevdepth + \egroup % End the \vtop. + % \dimen0 is the vertical size of the group's box. + \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox + % \dimen2 is how much space is left on the page (more or less). + \dimen2 = \pageheight \advance\dimen2 by -\pagetotal + % if the group doesn't fit on the current page, and it's a big big + % group, force a page break. + \ifdim \dimen0 > \dimen2 + \ifdim \pagetotal < \vfilllimit\pageheight + \page + \fi + \fi + \box\groupbox + \prevdepth = \dimen1 + \checkinserts +} +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +% Old definition--didn't work. +%\parseargdef\need{\par % +%% This method tries to make TeX break the page naturally +%% if the depth of the box does not fit. +%{\baselineskip=0pt% +%\vtop to #1\mil{\vfil}\kern -#1\mil\nobreak +%\prevdepth=-1000pt +%}} + +\parseargdef\need{% + % Ensure vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % If the @need value is less than one line space, it's useless. + \dimen0 = #1\mil + \dimen2 = \ht\strutbox + \advance\dimen2 by \dp\strutbox + \ifdim\dimen0 > \dimen2 + % + % Do a \strut just to make the height of this box be normal, so the + % normal leading is inserted relative to the preceding line. + % And a page break here is fine. + \vtop to #1\mil{\strut\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak + \fi +} + +% @br forces paragraph break (and is undocumented). + +\let\br = \par + +% @page forces the start of a new page. +% +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} + +% This defn is used inside nofill environments such as @example. +\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount + \leftline{\hskip\leftskip{\rm#1}}}} + +% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current +% paragraph. For more general purposes, use the \margin insertion +% class. WHICH is `l' or `r'. +% +\newskip\inmarginspacing \inmarginspacing=1cm +\def\strutdepth{\dp\strutbox} +% +\def\doinmargin#1#2{\strut\vadjust{% + \nobreak + \kern-\strutdepth + \vtop to \strutdepth{% + \baselineskip=\strutdepth + \vss + % if you have multiple lines of stuff to put here, you'll need to + % make the vbox yourself of the appropriate size. + \ifx#1l% + \llap{\ignorespaces #2\hskip\inmarginspacing}% + \else + \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% + \fi + \null + }% +}} +\def\inleftmargin{\doinmargin l} +\def\inrightmargin{\doinmargin r} +% +% @inmargin{TEXT [, RIGHT-TEXT]} +% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; +% else use TEXT for both). +% +\def\inmargin#1{\parseinmargin #1,,\finish} +\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \def\lefttext{#1}% have both texts + \def\righttext{#2}% + \else + \def\lefttext{#1}% have only one text + \def\righttext{#1}% + \fi + % + \ifodd\pageno + \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin + \else + \def\temp{\inleftmargin\lefttext}% + \fi + \temp +} + +% @include file insert text of that file as input. +% +\def\include{\parseargusing\filenamecatcodes\includezzz} +\def\includezzz#1{% + \pushthisfilestack + \def\thisfile{#1}% + {% + \makevalueexpandable + \def\temp{\input #1 }% + \expandafter + }\temp + \popthisfilestack +} +\def\filenamecatcodes{% + \catcode`\\=\other + \catcode`~=\other + \catcode`^=\other + \catcode`_=\other + \catcode`|=\other + \catcode`<=\other + \catcode`>=\other + \catcode`+=\other + \catcode`-=\other +} + +\def\pushthisfilestack{% + \expandafter\pushthisfilestackX\popthisfilestack\StackTerm +} +\def\pushthisfilestackX{% + \expandafter\pushthisfilestackY\thisfile\StackTerm +} +\def\pushthisfilestackY #1\StackTerm #2\StackTerm {% + \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% +} + +\def\popthisfilestack{\errthisfilestackempty} +\def\errthisfilestackempty{\errmessage{Internal error: + the stack of filenames is empty.}} + +\def\thisfile{} + +% @center line +% outputs that line, centered. +% +\parseargdef\center{% + \ifhmode + \let\next\centerH + \else + \let\next\centerV + \fi + \next{\hfil \ignorespaces#1\unskip \hfil}% +} +\def\centerH#1{% + {% + \hfil\break + \advance\hsize by -\leftskip + \advance\hsize by -\rightskip + \line{#1}% + \break + }% +} +\def\centerV#1{\line{\kern\leftskip #1\kern\rightskip}} + +% @sp n outputs n lines of vertical space + +\parseargdef\sp{\vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment + +\def\comment{\begingroup \catcode`\^^M=\other% +\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% +\commentxxx} +{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}} + +\let\c=\comment + +% @paragraphindent NCHARS +% We'll use ems for NCHARS, close enough. +% NCHARS can also be the word `asis' or `none'. +% We cannot feasibly implement @paragraphindent asis, though. +% +\def\asisword{asis} % no translation, these are keywords +\def\noneword{none} +% +\parseargdef\paragraphindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \defaultparindent = 0pt + \else + \defaultparindent = #1em + \fi + \fi + \parindent = \defaultparindent +} + +% @exampleindent NCHARS +% We'll use ems for NCHARS like @paragraphindent. +% It seems @exampleindent asis isn't necessary, but +% I preserve it to make it similar to @paragraphindent. +\parseargdef\exampleindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \lispnarrowing = 0pt + \else + \lispnarrowing = #1em + \fi + \fi +} + +% @firstparagraphindent WORD +% If WORD is `none', then suppress indentation of the first paragraph +% after a section heading. If WORD is `insert', then do indent at such +% paragraphs. +% +% The paragraph indentation is suppressed or not by calling +% \suppressfirstparagraphindent, which the sectioning commands do. +% We switch the definition of this back and forth according to WORD. +% By default, we suppress indentation. +% +\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} +\def\insertword{insert} +% +\parseargdef\firstparagraphindent{% + \def\temp{#1}% + \ifx\temp\noneword + \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent + \else\ifx\temp\insertword + \let\suppressfirstparagraphindent = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @firstparagraphindent option `\temp'}% + \fi\fi +} + +% Here is how we actually suppress indentation. Redefine \everypar to +% \kern backwards by \parindent, and then reset itself to empty. +% +% We also make \indent itself not actually do anything until the next +% paragraph. +% +\gdef\dosuppressfirstparagraphindent{% + \gdef\indent{% + \restorefirstparagraphindent + \indent + }% + \gdef\noindent{% + \restorefirstparagraphindent + \noindent + }% + \global\everypar = {% + \kern -\parindent + \restorefirstparagraphindent + }% +} + +\gdef\restorefirstparagraphindent{% + \global \let \indent = \ptexindent + \global \let \noindent = \ptexnoindent + \global \everypar = {}% +} + + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math outputs its argument in math mode. +% +% One complication: _ usually means subscripts, but it could also mean +% an actual _ character, as in @math{@var{some_variable} + 1}. So make +% _ active, and distinguish by seeing if the current family is \slfam, +% which is what @var uses. +{ + \catcode`\_ = \active + \gdef\mathunderscore{% + \catcode`\_=\active + \def_{\ifnum\fam=\slfam \_\else\sb\fi}% + } +} +% Another complication: we want \\ (and @\) to output a \ character. +% FYI, plain.tex uses \\ as a temporary control sequence (why?), but +% this is not advertised and we don't care. Texinfo does not +% otherwise define @\. +% +% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. +\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} +% +\def\math{% + \tex + \mathunderscore + \let\\ = \mathbackslash + \mathactive + $\finishmath +} +\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. + +% Some active characters (such as <) are spaced differently in math. +% We have to reset their definitions in case the @math was an argument +% to a command which sets the catcodes (such as @item or @section). +% +{ + \catcode`^ = \active + \catcode`< = \active + \catcode`> = \active + \catcode`+ = \active + \gdef\mathactive{% + \let^ = \ptexhat + \let< = \ptexless + \let> = \ptexgtr + \let+ = \ptexplus + } +} + +% @bullet and @minus need the same treatment as @math, just above. +\def\bullet{$\ptexbullet$} +\def\minus{$-$} + +% @dots{} outputs an ellipsis using the current font. +% We do .5em per period so that it has the same spacing in the cm +% typewriter fonts as three actual period characters; on the other hand, +% in other typewriter fonts three periods are wider than 1.5em. So do +% whichever is larger. +% +\def\dots{% + \leavevmode + \setbox0=\hbox{...}% get width of three periods + \ifdim\wd0 > 1.5em + \dimen0 = \wd0 + \else + \dimen0 = 1.5em + \fi + \hbox to \dimen0{% + \hskip 0pt plus.25fil + .\hskip 0pt plus1fil + .\hskip 0pt plus1fil + .\hskip 0pt plus.5fil + }% +} + +% @enddots{} is an end-of-sentence ellipsis. +% +\def\enddots{% + \dots + \spacefactor=\endofsentencespacefactor +} + +% @comma{} is so commas can be inserted into text without messing up +% Texinfo's parsing. +% +\let\comma = , + +% @refill is a no-op. +\let\refill=\relax + +% If working on a large document in chapters, it is convenient to +% be able to disable indexing, cross-referencing, and contents, for test runs. +% This is done with @novalidate (before @setfilename). +% +\newif\iflinks \linkstrue % by default we want the aux files. +\let\novalidate = \linksfalse + +% @setfilename is done at the beginning of every texinfo file. +% So open here the files we need to have open while reading the input. +% This makes it possible to make a .fmt file for texinfo. +\def\setfilename{% + \fixbackslash % Turn off hack to swallow `\input texinfo'. + \iflinks + \tryauxfile + % Open the new aux file. TeX will close it automatically at exit. + \immediate\openout\auxfile=\jobname.aux + \fi % \openindices needs to do some work in any case. + \openindices + \let\setfilename=\comment % Ignore extra @setfilename cmds. + % + % If texinfo.cnf is present on the system, read it. + % Useful for site-wide @afourpaper, etc. + \openin 1 texinfo.cnf + \ifeof 1 \else \input texinfo.cnf \fi + \closein 1 + % + \comment % Ignore the actual filename. +} + +% Called from \setfilename. +% +\def\openindices{% + \newindex{cp}% + \newcodeindex{fn}% + \newcodeindex{vr}% + \newcodeindex{tp}% + \newcodeindex{ky}% + \newcodeindex{pg}% +} + +% @bye. +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + + +\message{pdf,} +% adobe `portable' document format +\newcount\tempnum +\newcount\lnkcount +\newtoks\filename +\newcount\filenamelength +\newcount\pgn +\newtoks\toksA +\newtoks\toksB +\newtoks\toksC +\newtoks\toksD +\newbox\boxA +\newcount\countA +\newif\ifpdf +\newif\ifpdfmakepagedest + +% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 +% can be set). So we test for \relax and 0 as well as \undefined, +% borrowed from ifpdf.sty. +\ifx\pdfoutput\undefined +\else + \ifx\pdfoutput\relax + \else + \ifcase\pdfoutput + \else + \pdftrue + \fi + \fi +\fi + +% PDF uses PostScript string constants for the names of xref targets, +% for display in the outlines, and in other places. Thus, we have to +% double any backslashes. Otherwise, a name like "\node" will be +% interpreted as a newline (\n), followed by o, d, e. Not good. +% http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html +% (and related messages, the final outcome is that it is up to the TeX +% user to double the backslashes and otherwise make the string valid, so +% that's what we do). + +% double active backslashes. +% +{\catcode`\@=0 \catcode`\\=\active + @gdef@activebackslashdouble{% + @catcode`@\=@active + @let\=@doublebackslash} +} + +% To handle parens, we must adopt a different approach, since parens are +% not active characters. hyperref.dtx (which has the same problem as +% us) handles it with this amazing macro to replace tokens. I've +% tinkered with it a little for texinfo, but it's definitely from there. +% +% #1 is the tokens to replace. +% #2 is the replacement. +% #3 is the control sequence with the string. +% +\def\HyPsdSubst#1#2#3{% + \def\HyPsdReplace##1#1##2\END{% + ##1% + \ifx\\##2\\% + \else + #2% + \HyReturnAfterFi{% + \HyPsdReplace##2\END + }% + \fi + }% + \xdef#3{\expandafter\HyPsdReplace#3#1\END}% +} +\long\def\HyReturnAfterFi#1\fi{\fi#1} + +% #1 is a control sequence in which to do the replacements. +\def\backslashparens#1{% + \xdef#1{#1}% redefine it as its expansion; the definition is simply + % \lastnode when called from \setref -> \pdfmkdest. + \HyPsdSubst{(}{\realbackslash(}{#1}% + \HyPsdSubst{)}{\realbackslash)}{#1}% +} + +\ifpdf + \input pdfcolor + \pdfcatalog{/PageMode /UseOutlines}% + % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). + \def\dopdfimage#1#2#3{% + \def\imagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% + \def\imageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% + % without \immediate, pdftex seg faults when the same image is + % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) + \ifnum\pdftexversion < 14 + \immediate\pdfimage + \else + \immediate\pdfximage + \fi + \ifdim \wd0 >0pt width \imagewidth \fi + \ifdim \wd2 >0pt height \imageheight \fi + \ifnum\pdftexversion<13 + #1.pdf% + \else + {#1.pdf}% + \fi + \ifnum\pdftexversion < 14 \else + \pdfrefximage \pdflastximage + \fi} + \def\pdfmkdest#1{{% + % We have to set dummies so commands such as @code, and characters + % such as \, aren't expanded when present in a section title. + \atdummies + \activebackslashdouble + \def\pdfdestname{#1}% + \backslashparens\pdfdestname + \pdfdest name{\pdfdestname} xyz% + }}% + % + % used to mark target names; must be expandable. + \def\pdfmkpgn#1{#1}% + % + \let\linkcolor = \Blue % was Cyan, but that seems light? + \def\endlink{\Black\pdfendlink} + % Adding outlines to PDF; macros for calculating structure of outlines + % come from Petr Olsak + \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% + \else \csname#1\endcsname \fi} + \def\advancenumber#1{\tempnum=\expnumber{#1}\relax + \advance\tempnum by 1 + \expandafter\xdef\csname#1\endcsname{\the\tempnum}} + % + % #1 is the section text, which is what will be displayed in the + % outline by the pdf viewer. #2 is the pdf expression for the number + % of subentries (or empty, for subsubsections). #3 is the node text, + % which might be empty if this toc entry had no corresponding node. + % #4 is the page number + % + \def\dopdfoutline#1#2#3#4{% + % Generate a link to the node text if that exists; else, use the + % page number. We could generate a destination for the section + % text in the case where a section has no node, but it doesn't + % seem worth the trouble, since most documents are normally structured. + \def\pdfoutlinedest{#3}% + \ifx\pdfoutlinedest\empty + \def\pdfoutlinedest{#4}% + \else + % Doubled backslashes in the name. + {\activebackslashdouble \xdef\pdfoutlinedest{#3}% + \backslashparens\pdfoutlinedest}% + \fi + % + % Also double the backslashes in the display string. + {\activebackslashdouble \xdef\pdfoutlinetext{#1}% + \backslashparens\pdfoutlinetext}% + % + \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}% + } + % + \def\pdfmakeoutlines{% + \begingroup + % Thanh's hack / proper braces in bookmarks + \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace + \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace + % + % Read toc silently, to get counts of subentries for \pdfoutline. + \def\numchapentry##1##2##3##4{% + \def\thischapnum{##2}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + }% + \def\numsecentry##1##2##3##4{% + \advancenumber{chap\thischapnum}% + \def\thissecnum{##2}% + \def\thissubsecnum{0}% + }% + \def\numsubsecentry##1##2##3##4{% + \advancenumber{sec\thissecnum}% + \def\thissubsecnum{##2}% + }% + \def\numsubsubsecentry##1##2##3##4{% + \advancenumber{subsec\thissubsecnum}% + }% + \def\thischapnum{0}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + % + % use \def rather than \let here because we redefine \chapentry et + % al. a second time, below. + \def\appentry{\numchapentry}% + \def\appsecentry{\numsecentry}% + \def\appsubsecentry{\numsubsecentry}% + \def\appsubsubsecentry{\numsubsubsecentry}% + \def\unnchapentry{\numchapentry}% + \def\unnsecentry{\numsecentry}% + \def\unnsubsecentry{\numsubsecentry}% + \def\unnsubsubsecentry{\numsubsubsecentry}% + \readdatafile{toc}% + % + % Read toc second time, this time actually producing the outlines. + % The `-' means take the \expnumber as the absolute number of + % subentries, which we calculated on our first read of the .toc above. + % + % We use the node names as the destinations. + \def\numchapentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% + \def\numsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% + \def\numsubsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% + \def\numsubsubsecentry##1##2##3##4{% count is always zero + \dopdfoutline{##1}{}{##3}{##4}}% + % + % PDF outlines are displayed using system fonts, instead of + % document fonts. Therefore we cannot use special characters, + % since the encoding is unknown. For example, the eogonek from + % Latin 2 (0xea) gets translated to a | character. Info from + % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. + % + % xx to do this right, we have to translate 8-bit characters to + % their "best" equivalent, based on the @documentencoding. Right + % now, I guess we'll just let the pdf reader have its way. + \indexnofonts + \setupdatafile + \catcode`\\=\active \otherbackslash + \input \jobname.toc + \endgroup + } + % + \def\skipspaces#1{\def\PP{#1}\def\D{|}% + \ifx\PP\D\let\nextsp\relax + \else\let\nextsp\skipspaces + \ifx\p\space\else\addtokens{\filename}{\PP}% + \advance\filenamelength by 1 + \fi + \fi + \nextsp} + \def\getfilename#1{\filenamelength=0\expandafter\skipspaces#1|\relax} + \ifnum\pdftexversion < 14 + \let \startlink \pdfannotlink + \else + \let \startlink \pdfstartlink + \fi + % make a live url in pdf output. + \def\pdfurl#1{% + \begingroup + % it seems we really need yet another set of dummies; have not + % tried to figure out what each command should do in the context + % of @url. for now, just make @/ a no-op, that's the only one + % people have actually reported a problem with. + % + \normalturnoffactive + \def\@{@}% + \let\/=\empty + \makevalueexpandable + \leavevmode\Red + \startlink attr{/Border [0 0 0]}% + user{/Subtype /Link /A << /S /URI /URI (#1) >>}% + \endgroup} + \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} + \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} + \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} + \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} + \def\maketoks{% + \expandafter\poptoks\the\toksA|ENDTOKS|\relax + \ifx\first0\adn0 + \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 + \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 + \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 + \else + \ifnum0=\countA\else\makelink\fi + \ifx\first.\let\next=\done\else + \let\next=\maketoks + \addtokens{\toksB}{\the\toksD} + \ifx\first,\addtokens{\toksB}{\space}\fi + \fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \next} + \def\makelink{\addtokens{\toksB}% + {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} + \def\pdflink#1{% + \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} + \linkcolor #1\endlink} + \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +\else + \let\pdfmkdest = \gobble + \let\pdfurl = \gobble + \let\endlink = \relax + \let\linkcolor = \relax + \let\pdfmakeoutlines = \relax +\fi % \ifx\pdfoutput + + +\message{fonts,} + +% Change the current font style to #1, remembering it in \curfontstyle. +% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in +% italics, not bold italics. +% +\def\setfontstyle#1{% + \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. + \csname ten#1\endcsname % change the current font +} + +% Select #1 fonts with the current style. +% +\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname} + +\def\rm{\fam=0 \setfontstyle{rm}} +\def\it{\fam=\itfam \setfontstyle{it}} +\def\sl{\fam=\slfam \setfontstyle{sl}} +\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} +\def\tt{\fam=\ttfam \setfontstyle{tt}} + +% Texinfo sort of supports the sans serif font style, which plain TeX does not. +% So we set up a \sf. +\newfam\sffam +\def\sf{\fam=\sffam \setfontstyle{sf}} +\let\li = \sf % Sometimes we call it \li, not \sf. + +% We don't need math for this font style. +\def\ttsl{\setfontstyle{ttsl}} + + +% Default leading. +\newdimen\textleading \textleading = 13.2pt + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +\def\setleading#1{% + \normalbaselineskip = #1\relax + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + + +% Set the font macro #1 to the font named #2, adding on the +% specified font prefix (normally `cm'). +% #3 is the font's design size, #4 is a scale factor +\def\setfont#1#2#3#4{\font#1=\fontprefix#2#3 scaled #4} + + +% Use cm as the default font prefix. +% To specify the font prefix, you must define \fontprefix +% before you read in texinfo.tex. +\ifx\fontprefix\undefined +\def\fontprefix{cm} +\fi +% Support font families that don't use the same naming scheme as CM. +\def\rmshape{r} +\def\rmbshape{bx} %where the normal face is bold +\def\bfshape{b} +\def\bxshape{bx} +\def\ttshape{tt} +\def\ttbshape{tt} +\def\ttslshape{sltt} +\def\itshape{ti} +\def\itbshape{bxti} +\def\slshape{sl} +\def\slbshape{bxsl} +\def\sfshape{ss} +\def\sfbshape{ss} +\def\scshape{csc} +\def\scbshape{csc} + +% Definitions for a main text size of 11pt. This is the default in +% Texinfo. +% +\def\definetextfontsizexi{ +% Text fonts (11.2pt, magstep1). +\def\textnominalsize{11pt} +\edef\mainmagstep{\magstephalf} +\setfont\textrm\rmshape{10}{\mainmagstep} +\setfont\texttt\ttshape{10}{\mainmagstep} +\setfont\textbf\bfshape{10}{\mainmagstep} +\setfont\textit\itshape{10}{\mainmagstep} +\setfont\textsl\slshape{10}{\mainmagstep} +\setfont\textsf\sfshape{10}{\mainmagstep} +\setfont\textsc\scshape{10}{\mainmagstep} +\setfont\textttsl\ttslshape{10}{\mainmagstep} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstep1} +\setfont\deftt\ttshape{10}{\magstep1} +\setfont\defttsl\ttslshape{10}{\magstep1} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000} +\setfont\smalltt\ttshape{9}{1000} +\setfont\smallbf\bfshape{10}{900} +\setfont\smallit\itshape{9}{1000} +\setfont\smallsl\slshape{9}{1000} +\setfont\smallsf\sfshape{9}{1000} +\setfont\smallsc\scshape{10}{900} +\setfont\smallttsl\ttslshape{10}{900} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000} +\setfont\smallertt\ttshape{8}{1000} +\setfont\smallerbf\bfshape{10}{800} +\setfont\smallerit\itshape{8}{1000} +\setfont\smallersl\slshape{8}{1000} +\setfont\smallersf\sfshape{8}{1000} +\setfont\smallersc\scshape{10}{800} +\setfont\smallerttsl\ttslshape{10}{800} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3} +\setfont\titleit\itbshape{10}{\magstep4} +\setfont\titlesl\slbshape{10}{\magstep4} +\setfont\titlett\ttbshape{12}{\magstep3} +\setfont\titlettsl\ttslshape{10}{\magstep4} +\setfont\titlesf\sfbshape{17}{\magstep1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\authorrm{\secrm} +\def\authortt{\sectt} + +% Chapter (and unnumbered) fonts (17.28pt). +\def\chapnominalsize{17pt} +\setfont\chaprm\rmbshape{12}{\magstep2} +\setfont\chapit\itbshape{10}{\magstep3} +\setfont\chapsl\slbshape{10}{\magstep3} +\setfont\chaptt\ttbshape{12}{\magstep2} +\setfont\chapttsl\ttslshape{10}{\magstep3} +\setfont\chapsf\sfbshape{17}{1000} +\let\chapbf=\chaprm +\setfont\chapsc\scbshape{10}{\magstep3} +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 + +% Section fonts (14.4pt). +\def\secnominalsize{14pt} +\setfont\secrm\rmbshape{12}{\magstep1} +\setfont\secit\itbshape{10}{\magstep2} +\setfont\secsl\slbshape{10}{\magstep2} +\setfont\sectt\ttbshape{12}{\magstep1} +\setfont\secttsl\ttslshape{10}{\magstep2} +\setfont\secsf\sfbshape{12}{\magstep1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep2} +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 + +% Subsection fonts (13.15pt). +\def\ssecnominalsize{13pt} +\setfont\ssecrm\rmbshape{12}{\magstephalf} +\setfont\ssecit\itbshape{10}{1315} +\setfont\ssecsl\slbshape{10}{1315} +\setfont\ssectt\ttbshape{12}{\magstephalf} +\setfont\ssecttsl\ttslshape{10}{1315} +\setfont\ssecsf\sfbshape{12}{\magstephalf} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1315} +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled 1315 + +% Reduced fonts for @acro in text (10pt). +\def\reducednominalsize{10pt} +\setfont\reducedrm\rmshape{10}{1000} +\setfont\reducedtt\ttshape{10}{1000} +\setfont\reducedbf\bfshape{10}{1000} +\setfont\reducedit\itshape{10}{1000} +\setfont\reducedsl\slshape{10}{1000} +\setfont\reducedsf\sfshape{10}{1000} +\setfont\reducedsc\scshape{10}{1000} +\setfont\reducedttsl\ttslshape{10}{1000} +\font\reducedi=cmmi10 +\font\reducedsy=cmsy10 + +% reset the current fonts +\textfonts +\rm +} % end of 11pt text font size definitions + + +% Definitions to make the main text be 10pt Computer Modern, with +% section, chapter, etc., sizes following suit. This is for the GNU +% Press printing of the Emacs 22 manual. Maybe other manuals in the +% future. Used with @smallbook, which sets the leading to 12pt. +% +\def\definetextfontsizex{% +% Text fonts (10pt). +\def\textnominalsize{10pt} +\edef\mainmagstep{1000} +\setfont\textrm\rmshape{10}{\mainmagstep} +\setfont\texttt\ttshape{10}{\mainmagstep} +\setfont\textbf\bfshape{10}{\mainmagstep} +\setfont\textit\itshape{10}{\mainmagstep} +\setfont\textsl\slshape{10}{\mainmagstep} +\setfont\textsf\sfshape{10}{\mainmagstep} +\setfont\textsc\scshape{10}{\mainmagstep} +\setfont\textttsl\ttslshape{10}{\mainmagstep} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstephalf} +\setfont\deftt\ttshape{10}{\magstephalf} +\setfont\defttsl\ttslshape{10}{\magstephalf} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000} +\setfont\smalltt\ttshape{9}{1000} +\setfont\smallbf\bfshape{10}{900} +\setfont\smallit\itshape{9}{1000} +\setfont\smallsl\slshape{9}{1000} +\setfont\smallsf\sfshape{9}{1000} +\setfont\smallsc\scshape{10}{900} +\setfont\smallttsl\ttslshape{10}{900} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000} +\setfont\smallertt\ttshape{8}{1000} +\setfont\smallerbf\bfshape{10}{800} +\setfont\smallerit\itshape{8}{1000} +\setfont\smallersl\slshape{8}{1000} +\setfont\smallersf\sfshape{8}{1000} +\setfont\smallersc\scshape{10}{800} +\setfont\smallerttsl\ttslshape{10}{800} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3} +\setfont\titleit\itbshape{10}{\magstep4} +\setfont\titlesl\slbshape{10}{\magstep4} +\setfont\titlett\ttbshape{12}{\magstep3} +\setfont\titlettsl\ttslshape{10}{\magstep4} +\setfont\titlesf\sfbshape{17}{\magstep1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\authorrm{\secrm} +\def\authortt{\sectt} + +% Chapter fonts (14.4pt). +\def\chapnominalsize{14pt} +\setfont\chaprm\rmbshape{12}{\magstep1} +\setfont\chapit\itbshape{10}{\magstep2} +\setfont\chapsl\slbshape{10}{\magstep2} +\setfont\chaptt\ttbshape{12}{\magstep1} +\setfont\chapttsl\ttslshape{10}{\magstep2} +\setfont\chapsf\sfbshape{12}{\magstep1} +\let\chapbf\chaprm +\setfont\chapsc\scbshape{10}{\magstep2} +\font\chapi=cmmi12 scaled \magstep1 +\font\chapsy=cmsy10 scaled \magstep2 + +% Section fonts (12pt). +\def\secnominalsize{12pt} +\setfont\secrm\rmbshape{12}{1000} +\setfont\secit\itbshape{10}{\magstep1} +\setfont\secsl\slbshape{10}{\magstep1} +\setfont\sectt\ttbshape{12}{1000} +\setfont\secttsl\ttslshape{10}{\magstep1} +\setfont\secsf\sfbshape{12}{1000} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep1} +\font\seci=cmmi12 +\font\secsy=cmsy10 scaled \magstep1 + +% Subsection fonts (10pt). +\def\ssecnominalsize{10pt} +\setfont\ssecrm\rmbshape{10}{1000} +\setfont\ssecit\itbshape{10}{1000} +\setfont\ssecsl\slbshape{10}{1000} +\setfont\ssectt\ttbshape{10}{1000} +\setfont\ssecttsl\ttslshape{10}{1000} +\setfont\ssecsf\sfbshape{10}{1000} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1000} +\font\sseci=cmmi10 +\font\ssecsy=cmsy10 + +% Reduced fonts for @acro in text (9pt). +\def\reducednominalsize{9pt} +\setfont\reducedrm\rmshape{9}{1000} +\setfont\reducedtt\ttshape{9}{1000} +\setfont\reducedbf\bfshape{10}{900} +\setfont\reducedit\itshape{9}{1000} +\setfont\reducedsl\slshape{9}{1000} +\setfont\reducedsf\sfshape{9}{1000} +\setfont\reducedsc\scshape{10}{900} +\setfont\reducedttsl\ttslshape{10}{900} +\font\reducedi=cmmi9 +\font\reducedsy=cmsy9 + +% reduce space between paragraphs +\divide\parskip by 2 + +% reset the current fonts +\textfonts +\rm +} % end of 10pt text font size definitions + + +% We provide the user-level command +% @fonttextsize 10 +% (or 11) to redefine the text font size. pt is assumed. +% +\def\xword{10} +\def\xiword{11} +% +\parseargdef\fonttextsize{% + \def\textsizearg{#1}% + \wlog{doing @fonttextsize \textsizearg}% + % + % Set \globaldefs so that documents can use this inside @tex, since + % makeinfo 4.8 does not support it, but we need it nonetheless. + % + \begingroup \globaldefs=1 + \ifx\textsizearg\xword \definetextfontsizex + \else \ifx\textsizearg\xiword \definetextfontsizexi + \else + \errhelp=\EMsimple + \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} + \fi\fi + \endgroup +} + + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. Since +% texinfo doesn't allow for producing subscripts and superscripts except +% in the main text, we don't bother to reset \scriptfont and +% \scriptscriptfont (which would also require loading a lot more fonts). +% +\def\resetmathfonts{% + \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy + \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf + \textfont\ttfam=\tentt \textfont\sffam=\tensf +} + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this because \STYLE needs to also set the +% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire +% \tenSTYLE to set the current font. +% +% Each font-changing command also sets the names \lsize (one size lower) +% and \lllsize (three sizes lower). These relative commands are used in +% the LaTeX logo and acronyms. +% +% This all needs generalizing, badly. +% +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy + \let\tenttsl=\textttsl + \def\curfontsize{text}% + \def\lsize{reduced}\def\lllsize{smaller}% + \resetmathfonts \setleading{\textleading}} +\def\titlefonts{% + \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl + \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc + \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy + \let\tenttsl=\titlettsl + \def\curfontsize{title}% + \def\lsize{chap}\def\lllsize{subsec}% + \resetmathfonts \setleading{25pt}} +\def\titlefont#1{{\titlefonts\rm #1}} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy + \let\tenttsl=\chapttsl + \def\curfontsize{chap}% + \def\lsize{sec}\def\lllsize{text}% + \resetmathfonts \setleading{19pt}} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy + \let\tenttsl=\secttsl + \def\curfontsize{sec}% + \def\lsize{subsec}\def\lllsize{reduced}% + \resetmathfonts \setleading{16pt}} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy + \let\tenttsl=\ssecttsl + \def\curfontsize{ssec}% + \def\lsize{text}\def\lllsize{small}% + \resetmathfonts \setleading{15pt}} +\let\subsubsecfonts = \subsecfonts +\def\reducedfonts{% + \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl + \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc + \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy + \let\tenttsl=\reducedttsl + \def\curfontsize{reduced}% + \def\lsize{small}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallfonts{% + \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl + \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc + \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy + \let\tenttsl=\smallttsl + \def\curfontsize{small}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallerfonts{% + \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl + \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc + \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy + \let\tenttsl=\smallerttsl + \def\curfontsize{smaller}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{9.5pt}} + +% Set the fonts to use with the @small... environments. +\let\smallexamplefonts = \smallfonts + +% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample +% can fit this many characters: +% 8.5x11=86 smallbook=72 a4=90 a5=69 +% If we use \scriptfonts (8pt), then we can fit this many characters: +% 8.5x11=90+ smallbook=80 a4=90+ a5=77 +% For me, subjectively, the few extra characters that fit aren't worth +% the additional smallness of 8pt. So I'm making the default 9pt. +% +% By the way, for comparison, here's what fits with @example (10pt): +% 8.5x11=71 smallbook=60 a4=75 a5=58 +% +% I wish the USA used A4 paper. +% --karl, 24jan03. + + +% Set up the default fonts, so we can use them for creating boxes. +% +\definetextfontsizexi + +% Define these so they can be easily changed for other fonts. +\def\angleleft{$\langle$} +\def\angleright{$\rangle$} + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Fonts for short table of contents. +\setfont\shortcontrm\rmshape{12}{1000} +\setfont\shortcontbf\bfshape{10}{\magstep1} % no cmb12 +\setfont\shortcontsl\slshape{12}{1000} +\setfont\shortconttt\ttshape{12}{1000} + +%% Add scribe-like font environments, plus @l for inline lisp (usually sans +%% serif) and @ii for TeX italic + +% \smartitalic{ARG} outputs arg in italics, followed by an italic correction +% unless the following character is such as not to need one. +\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else + \ptexslash\fi\fi\fi} +\def\smartslanted#1{{\ifusingtt\ttsl\sl #1}\futurelet\next\smartitalicx} +\def\smartitalic#1{{\ifusingtt\ttsl\it #1}\futurelet\next\smartitalicx} + +% like \smartslanted except unconditionally uses \ttsl. +% @var is set to this for defun arguments. +\def\ttslanted#1{{\ttsl #1}\futurelet\next\smartitalicx} + +% like \smartslanted except unconditionally use \sl. We never want +% ttsl for book titles, do we? +\def\cite#1{{\sl #1}\futurelet\next\smartitalicx} + +\let\i=\smartitalic +\let\slanted=\smartslanted +\let\var=\smartslanted +\let\dfn=\smartslanted +\let\emph=\smartitalic + +% @b, explicit bold. +\def\b#1{{\bf #1}} +\let\strong=\b + +% @sansserif, explicit sans. +\def\sansserif#1{{\sf #1}} + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +% Set sfcode to normal for the chars that usually have another value. +% Can't use plain's \frenchspacing because it uses the `\x notation, and +% sometimes \x has an active definition that messes things up. +% +\catcode`@=11 + \def\plainfrenchspacing{% + \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m + \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m + \def\endofsentencespacefactor{1000}% for @. and friends + } + \def\plainnonfrenchspacing{% + \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 + \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 + \def\endofsentencespacefactor{3000}% for @. and friends + } +\catcode`@=\other +\def\endofsentencespacefactor{3000}% default + +\def\t#1{% + {\tt \rawbackslash \plainfrenchspacing #1}% + \null +} +\def\samp#1{`\tclose{#1}'\null} +\setfont\keyrm\rmshape{8}{1000} +\font\keysy=cmsy9 +\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% + \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% + \vbox{\hrule\kern-0.4pt + \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% + \kern-0.4pt\hrule}% + \kern-.06em\raise0.4pt\hbox{\angleright}}}} +\def\key #1{{\nohyphenation \uppercase{#1}}\null} +% The old definition, with no lozenge: +%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null} +\def\ctrl #1{{\tt \rawbackslash \hat}#1} + +% @file, @option are the same as @samp. +\let\file=\samp +\let\option=\samp + +% @code is a modification of @t, +% which makes spaces the same size as normal in the surrounding text. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \plainfrenchspacing + #1% + }% + \null +} + +% We *must* turn on hyphenation at `-' and `_' in @code. +% Otherwise, it is too hard to avoid overfull hboxes +% in the Emacs manual, the Library manual, etc. + +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate at a dash. +% -- rms. +{ + \catcode`\-=\active \catcode`\_=\active + \catcode`\'=\active \catcode`\`=\active + % + \global\def\code{\begingroup + \catcode\rquoteChar=\active \catcode\lquoteChar=\active + \let'\codequoteright \let`\codequoteleft + % + \catcode\dashChar=\active \catcode\underChar=\active + \ifallowcodebreaks + \let-\codedash + \let_\codeunder + \else + \let-\realdash + \let_\realunder + \fi + \codex + } +} + +\def\realdash{-} +\def\codedash{-\discretionary{}{}{}} +\def\codeunder{% + % this is all so @math{@code{var_name}+1} can work. In math mode, _ + % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) + % will therefore expand the active definition of _, which is us + % (inside @code that is), therefore an endless loop. + \ifusingtt{\ifmmode + \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. + \else\normalunderscore \fi + \discretionary{}{}{}}% + {\_}% +} +\def\codex #1{\tclose{#1}\endgroup} + +% An additional complication: the above will allow breaks after, e.g., +% each of the four underscores in __typeof__. This is undesirable in +% some manuals, especially if they don't have long identifiers in +% general. @allowcodebreaks provides a way to control this. +% +\newif\ifallowcodebreaks \allowcodebreakstrue + +\def\keywordtrue{true} +\def\keywordfalse{false} + +\parseargdef\allowcodebreaks{% + \def\txiarg{#1}% + \ifx\txiarg\keywordtrue + \allowcodebreakstrue + \else\ifx\txiarg\keywordfalse + \allowcodebreaksfalse + \else + \errhelp = \EMsimple + \errmessage{Unknown @allowcodebreaks option `\txiarg'}% + \fi\fi +} + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. + +% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), +% `example' (@kbd uses ttsl only inside of @example and friends), +% or `code' (@kbd uses normal tty font always). +\parseargdef\kbdinputstyle{% + \def\txiarg{#1}% + \ifx\txiarg\worddistinct + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% + \else\ifx\txiarg\wordexample + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% + \else\ifx\txiarg\wordcode + \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% + \else + \errhelp = \EMsimple + \errmessage{Unknown @kbdinputstyle option `\txiarg'}% + \fi\fi\fi +} +\def\worddistinct{distinct} +\def\wordexample{example} +\def\wordcode{code} + +% Default is `distinct.' +\kbdinputstyle distinct + +\def\xkey{\key} +\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% +\ifx\one\xkey\ifx\threex\three \key{#2}% +\else{\tclose{\kbdfont\look}}\fi +\else{\tclose{\kbdfont\look}}\fi} + +% For @indicateurl, @env, @command quotes seem unnecessary, so use \code. +\let\indicateurl=\code +\let\env=\code +\let\command=\code + +% @uref (abbreviation for `urlref') takes an optional (comma-separated) +% second argument specifying the text to display and an optional third +% arg as text to display instead of (rather than in addition to) the url +% itself. First (mandatory) arg is the url. Perhaps eventually put in +% a hypertex \special here. +% +\def\uref#1{\douref #1,,,\finish} +\def\douref#1,#2,#3,#4\finish{\begingroup + \unsepspaces + \pdfurl{#1}% + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt + \unhbox0 % third arg given, show only that + \else + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \ifpdf + \unhbox0 % PDF: 2nd arg given, show only it + \else + \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url + \fi + \else + \code{#1}% only url given, so show it + \fi + \fi + \endlink +\endgroup} + +% @url synonym for @uref, since that's how everyone uses it. +% +\let\url=\uref + +% rms does not like angle brackets --karl, 17may97. +% So now @email is just like @uref, unless we are pdf. +% +%\def\email#1{\angleleft{\tt #1}\angleright} +\ifpdf + \def\email#1{\doemail#1,,\finish} + \def\doemail#1,#2,#3\finish{\begingroup + \unsepspaces + \pdfurl{mailto:#1}% + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi + \endlink + \endgroup} +\else + \let\email=\uref +\fi + +% Check if we are currently using a typewriter font. Since all the +% Computer Modern typewriter fonts have zero interword stretch (and +% shrink), and it is reasonable to expect all typewriter fonts to have +% this property, we can check that font parameter. +% +\def\ifmonospace{\ifdim\fontdimen3\font=0pt } + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par} + +% @l was never documented to mean ``switch to the Lisp font'', +% and it is not used as such in any manual I can find. We need it for +% Polish suppressed-l. --karl, 22sep96. +%\def\l#1{{\li #1}\null} + +% Explicit font changes: @r, @sc, undocumented @ii. +\def\r#1{{\rm #1}} % roman font +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +% @acronym for "FBI", "NATO", and the like. +% We print this one point size smaller, since it's intended for +% all-uppercase. +% +\def\acronym#1{\doacronym #1,,\finish} +\def\doacronym#1,#2,#3\finish{% + {\selectfonts\lsize #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi +} + +% @abbr for "Comput. J." and the like. +% No font change, but don't do end-of-sentence spacing. +% +\def\abbr#1{\doabbr #1,,\finish} +\def\doabbr#1,#2,#3\finish{% + {\plainfrenchspacing #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi +} + +% @pounds{} is a sterling sign, which Knuth put in the CM italic font. +% +\def\pounds{{\it\$}} + +% @euro{} comes from a separate font, depending on the current style. +% We use the free feym* fonts from the eurosym package by Henrik +% Theiling, which support regular, slanted, bold and bold slanted (and +% "outlined" (blackboard board, sort of) versions, which we don't need). +% It is available from http://www.ctan.org/tex-archive/fonts/eurosym. +% +% Although only regular is the truly official Euro symbol, we ignore +% that. The Euro is designed to be slightly taller than the regular +% font height. +% +% feymr - regular +% feymo - slanted +% feybr - bold +% feybo - bold slanted +% +% There is no good (free) typewriter version, to my knowledge. +% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. +% Hmm. +% +% Also doesn't work in math. Do we need to do math with euro symbols? +% Hope not. +% +% +\def\euro{{\eurofont e}} +\def\eurofont{% + % We set the font at each command, rather than predefining it in + % \textfonts and the other font-switching commands, so that + % installations which never need the symbol don't have to have the + % font installed. + % + % There is only one designed size (nominal 10pt), so we always scale + % that to the current nominal size. + % + % By the way, simply using "at 1em" works for cmr10 and the like, but + % does not work for cmbx10 and other extended/shrunken fonts. + % + \def\eurosize{\csname\curfontsize nominalsize\endcsname}% + % + \ifx\curfontstyle\bfstylename + % bold: + \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize + \else + % regular: + \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize + \fi + \thiseurofont +} + +% @registeredsymbol - R in a circle. The font for the R should really +% be smaller yet, but lllsize is the best we can do for now. +% Adapted from the plain.tex definition of \copyright. +% +\def\registeredsymbol{% + $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}% + \hfil\crcr\Orb}}% + }$% +} + +% @textdegree - the normal degrees sign. +% +\def\textdegree{$^\circ$} + +% Laurent Siebenmann reports \Orb undefined with: +% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 +% so we'll define it if necessary. +% +\ifx\Orb\undefined +\def\Orb{\mathhexbox20D} +\fi + + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\newif\ifseenauthor +\newif\iffinishedtitlepage + +% Do an implicit @contents or @shortcontents after @end titlepage if the +% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. +% +\newif\ifsetcontentsaftertitlepage + \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue +\newif\ifsetshortcontentsaftertitlepage + \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue + +\parseargdef\shorttitlepage{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\envdef\titlepage{% + % Open one extra group, as we want to close it in the middle of \Etitlepage. + \begingroup + \parindent=0pt \textfonts + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \let\page = \oldpage + \page + \null + }% +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + % + % Need this before the \...aftertitlepage checks so that if they are + % in effect the toc pages will come out with page numbers. + \HEADINGSon + % + % If they want short, they certainly want long too. + \ifsetshortcontentsaftertitlepage + \shortcontents + \contents + \global\let\shortcontents = \relax + \global\let\contents = \relax + \fi + % + \ifsetcontentsaftertitlepage + \contents + \global\let\contents = \relax + \global\let\shortcontents = \relax + \fi +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +%%% Macros to be used within @titlepage: + +\let\subtitlerm=\tenrm +\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} + +\def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines + \let\tt=\authortt} + +\parseargdef\title{% + \checkenv\titlepage + \leftline{\titlefonts\rm #1} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt +} + +\parseargdef\subtitle{% + \checkenv\titlepage + {\subtitlefont \rightline{#1}}% +} + +% @author should come last, but may come many times. +% It can also be used inside @quotation. +% +\parseargdef\author{% + \def\temp{\quotation}% + \ifx\thisenv\temp + \def\quotationauthor{#1}% printed in \Equotation. + \else + \checkenv\titlepage + \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi + {\authorfont \leftline{#1}}% + \fi +} + + +%%% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks\evenheadline % headline on even pages +\newtoks\oddheadline % headline on odd pages +\newtoks\evenfootline % footline on even pages +\newtoks\oddfootline % footline on odd pages + +% Now make TeX use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + + +\def\evenheading{\parsearg\evenheadingxxx} +\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} +\def\evenheadingyyy #1\|#2\|#3\|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddheading{\parsearg\oddheadingxxx} +\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} +\def\oddheadingyyy #1\|#2\|#3\|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} +\def\evenfootingyyy #1\|#2\|#3\|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddfooting{\parsearg\oddfootingxxx} +\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} +\def\oddfootingyyy #1\|#2\|#3\|#4\finish{% + \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% + % + % Leave some space for the footline. Hopefully ok to assume + % @evenfooting will not be used by itself. + \global\advance\pageheight by -12pt + \global\advance\vsize by -12pt +} + +\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} + + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off at the start of a document, +% and turned `on' after @end titlepage. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\HEADINGSoff{% +\global\evenheadline={\hfil} \global\evenfootline={\hfil} +\global\oddheadline={\hfil} \global\oddfootline={\hfil}} +\HEADINGSoff +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} +\let\contentsalignmacro = \chappager + +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} + +% Subroutines used in generating headings +% This produces Day Month Year style of output. +% Only define if not already defined, in case a txi-??.tex file has set +% up a different format (e.g., txi-cs.tex does this). +\ifx\today\undefined +\def\today{% + \number\day\space + \ifcase\month + \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr + \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug + \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec + \fi + \space\number\year} +\fi + +% @settitle line... specifies the title of the document, for headings. +% It generates no output of its own. +\def\thistitle{\putwordNoTitle} +\def\settitle{\parsearg{\gdef\thistitle}} + + +\message{tables,} +% Tables -- @table, @ftable, @vtable, @item(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @ftable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemindicate{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. However, if + % what follows is an environment such as @example, there will be no + % \parskip glue; then the negative vskip we just inserted would + % cause the example and the item to crash together. So we use this + % bizarre value of 10001 as a signal to \aboveenvbreak to insert + % \parskip glue after all. Section titles are handled this way also. + % + \penalty 10001 + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. + \noindent + % Do this with kerns and \unhbox so that if there is a footnote in + % the item text, it can migrate to the main vertical list and + % eventually be printed. + \nobreak\kern-\tableindent + \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 + \unhbox0 + \nobreak\kern\dimen0 + \endgroup + \itemxneedsnegativevskiptrue + \fi +} + +\def\item{\errmessage{@item while not in a list environment}} +\def\itemx{\errmessage{@itemx while not in a list environment}} + +% @table, @ftable, @vtable. +\envdef\table{% + \let\itemindex\gobble + \tablecheck{table}% +} +\envdef\ftable{% + \def\itemindex ##1{\doind {fn}{\code{##1}}}% + \tablecheck{ftable}% +} +\envdef\vtable{% + \def\itemindex ##1{\doind {vr}{\code{##1}}}% + \tablecheck{vtable}% +} +\def\tablecheck#1{% + \ifnum \the\catcode`\^^M=\active + \endgroup + \errmessage{This command won't work in this context; perhaps the problem is + that we are \inenvironment\thisenv}% + \def\next{\doignore{#1}}% + \else + \let\next\tablex + \fi + \next +} +\def\tablex#1{% + \def\itemindicate{#1}% + \parsearg\tabley +} +\def\tabley#1{% + {% + \makevalueexpandable + \edef\temp{\noexpand\tablez #1\space\space\space}% + \expandafter + }\temp \endtablez +} +\def\tablez #1 #2 #3 #4\endtablez{% + \aboveenvbreak + \ifnum 0#1>0 \advance \leftskip by #1\mil \fi + \ifnum 0#2>0 \tableindent=#2\mil \fi + \ifnum 0#3>0 \advance \rightskip by #3\mil \fi + \itemmax=\tableindent + \advance \itemmax by -\itemmargin + \advance \leftskip by \tableindent + \exdentamount=\tableindent + \parindent = 0pt + \parskip = \smallskipamount + \ifdim \parskip=0pt \parskip=2pt \fi + \let\item = \internalBitem + \let\itemx = \internalBitemx +} +\def\Etable{\endgraf\afterenvbreak} +\let\Eftable\Etable +\let\Evtable\Etable +\let\Eitemize\Etable +\let\Eenumerate\Etable + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\envdef\itemize{\parsearg\doitemize} + +\def\doitemize#1{% + \aboveenvbreak + \itemmax=\itemindent + \advance\itemmax by -\itemmargin + \advance\leftskip by \itemindent + \exdentamount=\itemindent + \parindent=0pt + \parskip=\smallskipamount + \ifdim\parskip=0pt \parskip=2pt \fi + \def\itemcontents{#1}% + % @itemize with no arg is equivalent to @itemize @bullet. + \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi + \let\item=\itemizeitem +} + +% Definition of @item while inside @itemize and @enumerate. +% +\def\itemizeitem{% + \advance\itemno by 1 % for enumerations + {\let\par=\endgraf \smallbreak}% reasonable place to break + {% + % If the document has an @itemize directly after a section title, a + % \nobreak will be last on the list, and \sectionheading will have + % done a \vskip-\parskip. In that case, we don't want to zero + % parskip, or the item text will crash with the heading. On the + % other hand, when there is normal text preceding the item (as there + % usually is), we do want to zero parskip, or there would be too much + % space. In that case, we won't have a \nobreak before. At least + % that's the theory. + \ifnum\lastpenalty<10000 \parskip=0in \fi + \noindent + \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% + \vadjust{\penalty 1200}}% not good to break after first line of item. + \flushcr +} + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\envparseargdef\enumerate{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a . + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call \doitemize, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \doitemize{#1.}\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + + +% @multitable macros +% Amy Hendrickson, 8/18/94, 3/6/96 +% +% @multitable ... @end multitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @columnfractions .25 .3 .45 +% @item ... +% +% Numbers following @columnfractions are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab do not need to be on their own lines, but it will not hurt +% if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @end multitable + +% Default dimensions may be reset by user. +% @multitableparskip is vertical space between paragraphs in table. +% @multitableparindent is paragraph indent in table. +% @multitablecolmargin is horizontal space to be left between columns. +% @multitablelinespace is space to leave between table items, baseline +% to baseline. +% 0pt means it depends on current normal line spacing. +% +\newskip\multitableparskip +\newskip\multitableparindent +\newdimen\multitablecolspace +\newskip\multitablelinespace +\multitableparskip=0pt +\multitableparindent=6pt +\multitablecolspace=12pt +\multitablelinespace=0pt + +% Macros used to set up halign preamble: +% +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\columnfractions\relax +\def\xcolumnfractions{\columnfractions} +\newif\ifsetpercent + +% #1 is the @columnfraction, usually a decimal number like .5, but might +% be just 1. We just use it, whatever it is. +% +\def\pickupwholefraction#1 {% + \global\advance\colcount by 1 + \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% + \setuptable +} + +\newcount\colcount +\def\setuptable#1{% + \def\firstarg{#1}% + \ifx\firstarg\xendsetuptable + \let\go = \relax + \else + \ifx\firstarg\xcolumnfractions + \global\setpercenttrue + \else + \ifsetpercent + \let\go\pickupwholefraction + \else + \global\advance\colcount by 1 + \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a + % separator; typically that is always in the input, anyway. + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi + \fi + \ifx\go\pickupwholefraction + % Put the argument back for the \pickupwholefraction call, so + % we'll always have a period there to be parsed. + \def\go{\pickupwholefraction#1}% + \else + \let\go = \setuptable + \fi% + \fi + \go +} + +% multitable-only commands. +% +% @headitem starts a heading row, which we typeset in bold. +% Assignments have to be global since we are inside the implicit group +% of an alignment entry. Note that \everycr resets \everytab. +\def\headitem{\checkenv\multitable \crcr \global\everytab={\bf}\the\everytab}% +% +% A \tab used to include \hskip1sp. But then the space in a template +% line is not enough. That is bad. So let's go back to just `&' until +% we encounter the problem it was intended to solve again. +% --karl, nathan@acm.org, 20apr99. +\def\tab{\checkenv\multitable &\the\everytab}% + +% @multitable ... @end multitable definitions: +% +\newtoks\everytab % insert after every tab. +% +\envdef\multitable{% + \vskip\parskip + \startsavinginserts + % + % @item within a multitable starts a normal row. + % We use \def instead of \let so that if one of the multitable entries + % contains an @itemize, we don't choke on the \item (seen as \crcr aka + % \endtemplate) expanding \doitemize. + \def\item{\crcr}% + % + \tolerance=9500 + \hbadness=9500 + \setmultitablespacing + \parskip=\multitableparskip + \parindent=\multitableparindent + \overfullrule=0pt + \global\colcount=0 + % + \everycr = {% + \noalign{% + \global\everytab={}% + \global\colcount=0 % Reset the column counter. + % Check for saved footnotes, etc. + \checkinserts + % Keeps underfull box messages off when table breaks over pages. + %\filbreak + % Maybe so, but it also creates really weird page breaks when the + % table breaks over pages. Wouldn't \vfil be better? Wait until the + % problem manifests itself, so it can be fixed for real --karl. + }% + }% + % + \parsearg\domultitable +} +\def\domultitable#1{% + % To parse everything between @multitable and @item: + \setuptable#1 \endsetuptable + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. + \halign\bgroup &% + \global\advance\colcount by 1 + \multistrut + \vtop{% + % Use the current \colcount to find the correct column width: + \hsize=\expandafter\csname col\the\colcount\endcsname + % + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % + % If the user has set preamble in terms of percent of \hsize we will + % use that dimension as the width of the column, and the \leftskip + % will keep entries from bumping into each other. Table will start at + % left margin and final column will justify at right margin. + % + % Make sure we don't inherit \rightskip from the outer environment. + \rightskip=0pt + \ifnum\colcount=1 + % The first column will be indented with the surrounding text. + \advance\hsize by\leftskip + \else + \ifsetpercent \else + % If user has not set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace. + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: + \leftskip=\multitablecolspace + \fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighting sequences respectively + % marking characters. + \noindent\ignorespaces##\unskip\multistrut + }\cr +} +\def\Emultitable{% + \crcr + \egroup % end the \halign + \global\setpercentfalse +} + +\def\setmultitablespacing{% + \def\multistrut{\strut}% just use the standard line spacing + % + % Compute \multitablelinespace (if not defined by user) for use in + % \multitableparskip calculation. We used define \multistrut based on + % this, but (ironically) that caused the spacing to be off. + % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. +\ifdim\multitablelinespace=0pt +\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip +\global\advance\multitablelinespace by-\ht0 +\fi +%% Test to see if parskip is larger than space between lines of +%% table. If not, do nothing. +%% If so, set to same dimension as multitablelinespace. +\ifdim\multitableparskip>\multitablelinespace +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi% +\ifdim\multitableparskip=0pt +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi} + + +\message{conditionals,} + +% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, +% @ifnotxml always succeed. They currently do nothing; we don't +% attempt to check whether the conditionals are properly nested. But we +% have to remember that they are conditionals, so that @end doesn't +% attempt to close an environment group. +% +\def\makecond#1{% + \expandafter\let\csname #1\endcsname = \relax + \expandafter\let\csname iscond.#1\endcsname = 1 +} +\makecond{iftex} +\makecond{ifnotdocbook} +\makecond{ifnothtml} +\makecond{ifnotinfo} +\makecond{ifnotplaintext} +\makecond{ifnotxml} + +% Ignore @ignore, @ifhtml, @ifinfo, and the like. +% +\def\direntry{\doignore{direntry}} +\def\documentdescription{\doignore{documentdescription}} +\def\docbook{\doignore{docbook}} +\def\html{\doignore{html}} +\def\ifdocbook{\doignore{ifdocbook}} +\def\ifhtml{\doignore{ifhtml}} +\def\ifinfo{\doignore{ifinfo}} +\def\ifnottex{\doignore{ifnottex}} +\def\ifplaintext{\doignore{ifplaintext}} +\def\ifxml{\doignore{ifxml}} +\def\ignore{\doignore{ignore}} +\def\menu{\doignore{menu}} +\def\xml{\doignore{xml}} + +% Ignore text until a line `@end #1', keeping track of nested conditionals. +% +% A count to remember the depth of nesting. +\newcount\doignorecount + +\def\doignore#1{\begingroup + % Scan in ``verbatim'' mode: + \obeylines + \catcode`\@ = \other + \catcode`\{ = \other + \catcode`\} = \other + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \spaceisspace + % + % Count number of #1's that we've seen. + \doignorecount = 0 + % + % Swallow text until we reach the matching `@end #1'. + \dodoignore{#1}% +} + +{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. + \obeylines % + % + \gdef\dodoignore#1{% + % #1 contains the command name as a string, e.g., `ifinfo'. + % + % Define a command to find the next `@end #1'. + \long\def\doignoretext##1^^M@end #1{% + \doignoretextyyy##1^^M@#1\_STOP_}% + % + % And this command to find another #1 command, at the beginning of a + % line. (Otherwise, we would consider a line `@c @ifset', for + % example, to count as an @ifset for nesting.) + \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% + % + % And now expand that command. + \doignoretext ^^M% + }% +} + +\def\doignoreyyy#1{% + \def\temp{#1}% + \ifx\temp\empty % Nothing found. + \let\next\doignoretextzzz + \else % Found a nested condition, ... + \advance\doignorecount by 1 + \let\next\doignoretextyyy % ..., look for another. + % If we're here, #1 ends with ^^M\ifinfo (for example). + \fi + \next #1% the token \_STOP_ is present just after this macro. +} + +% We have to swallow the remaining "\_STOP_". +% +\def\doignoretextzzz#1{% + \ifnum\doignorecount = 0 % We have just found the outermost @end. + \let\next\enddoignore + \else % Still inside a nested condition. + \advance\doignorecount by -1 + \let\next\doignoretext % Look for the next @end. + \fi + \next +} + +% Finish off ignored text. +{ \obeylines% + % Ignore anything after the last `@end #1'; this matters in verbatim + % environments, where otherwise the newline after an ignored conditional + % would result in a blank line in the output. + \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% +} + + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. +% We rely on the fact that \parsearg sets \catcode`\ =10. +% +\parseargdef\set{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + {% + \makevalueexpandable + \def\temp{#2}% + \edef\next{\gdef\makecsname{SET#1}}% + \ifx\temp\empty + \next{}% + \else + \setzzz#2\endsetzzz + \fi + }% +} +% Remove the trailing space \setxxx inserted. +\def\setzzz#1 \endsetzzz{\next{#1}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\parseargdef\clear{% + {% + \makevalueexpandable + \global\expandafter\let\csname SET#1\endcsname=\relax + }% +} + +% @value{foo} gets the text saved in variable foo. +\def\value{\begingroup\makevalueexpandable\valuexxx} +\def\valuexxx#1{\expandablevalue{#1}\endgroup} +{ + \catcode`\- = \active \catcode`\_ = \active + % + \gdef\makevalueexpandable{% + \let\value = \expandablevalue + % We don't want these characters active, ... + \catcode`\-=\other \catcode`\_=\other + % ..., but we might end up with active ones in the argument if + % we're called from @code, as @code{@value{foo-bar_}}, though. + % So \let them to their normal equivalents. + \let-\realdash \let_\normalunderscore + } +} + +% We have this subroutine so that we can handle at least some @value's +% properly in indexes (we call \makevalueexpandable in \indexdummies). +% The command has to be fully expandable (if the variable is set), since +% the result winds up in the index file. This means that if the +% variable's value contains other Texinfo commands, it's almost certain +% it will fail (although perhaps we could fix that with sufficient work +% to do a one-level expansion on the result, instead of complete). +% +\def\expandablevalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + {[No value for ``#1'']}% + \message{Variable `#1', used in @value, is not set.}% + \else + \csname SET#1\endcsname + \fi +} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +% To get special treatment of `@end ifset,' call \makeond and the redefine. +% +\makecond{ifset} +\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} +\def\doifset#1#2{% + {% + \makevalueexpandable + \let\next=\empty + \expandafter\ifx\csname SET#2\endcsname\relax + #1% If not set, redefine \next. + \fi + \expandafter + }\next +} +\def\ifsetfail{\doignore{ifset}} + +% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +% The `\else' inside the `\doifset' parameter is a trick to reuse the +% above code: if the variable is not set, do nothing, if it is set, +% then redefine \next to \ifclearfail. +% +\makecond{ifclear} +\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} +\def\ifclearfail{\doignore{ifclear}} + +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory=\comment + +% @defininfoenclose. +\let\definfoenclose=\comment + + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within macros and \if's. +\edef\newwrite{\makecsname{ptexnewwrite}} + +% \newindex {foo} defines an index named foo. +% It automatically defines \fooindex such that +% \fooindex ...rest of line... puts an entry in the index foo. +% It also defines \fooindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is foo. +% The name of an index should be no more than 2 characters long +% for the sake of vms. +% +\def\newindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 % Open the file + \fi + \expandafter\xdef\csname#1index\endcsname{% % Define @#1index + \noexpand\doindex{#1}} +} + +% @defindex foo == \newindex{foo} +% +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. +% +\def\defcodeindex{\parsearg\newcodeindex} +% +\def\newcodeindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 + \fi + \expandafter\xdef\csname#1index\endcsname{% + \noexpand\docodeindex{#1}}% +} + + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +% +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +% +\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} +\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} + +% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), +% #3 the target index (bar). +\def\dosynindex#1#2#3{% + % Only do \closeout if we haven't already done it, else we'll end up + % closing the target index. + \expandafter \ifx\csname donesynindex#2\endcsname \undefined + % The \closeout helps reduce unnecessary open files; the limit on the + % Acorn RISC OS is a mere 16 files. + \expandafter\closeout\csname#2indfile\endcsname + \expandafter\let\csname\donesynindex#2\endcsname = 1 + \fi + % redefine \fooindfile: + \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname + \expandafter\let\csname#2indfile\endcsname=\temp + % redefine \fooindex: + \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% +} + +% Define \doindex, the driver for all \fooindex macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is "foo", the name of the index. + +% \doindex just uses \parsearg; it calls \doind for the actual work. +% This is because \doind is more useful to call from other macros. + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} +\def\singleindexer #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} +\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} + +% Take care of Texinfo commands that can appear in an index entry. +% Since there are some commands we want to expand, and others we don't, +% we have to laboriously prevent expansion for those that we don't. +% +\def\indexdummies{% + \escapechar = `\\ % use backslash in output files. + \def\@{@}% change to @@ when we switch to @ as escape char in index files. + \def\ {\realbackslash\space }% + % + % Need these in case \tex is in effect and \{ is a \delimiter again. + % But can't use \lbracecmd and \rbracecmd because texindex assumes + % braces and backslashes are used only as delimiters. + \let\{ = \mylbrace + \let\} = \myrbrace + % + % I don't entirely understand this, but when an index entry is + % generated from a macro call, the \endinput which \scanmacro inserts + % causes processing to be prematurely terminated. This is, + % apparently, because \indexsorttmp is fully expanded, and \endinput + % is an expandable command. The redefinition below makes \endinput + % disappear altogether for that purpose -- although logging shows that + % processing continues to some further point. On the other hand, it + % seems \endinput does not hurt in the printed index arg, since that + % is still getting written without apparent harm. + % + % Sample source (mac-idx3.tex, reported by Graham Percival to + % help-texinfo, 22may06): + % @macro funindex {WORD} + % @findex xyz + % @end macro + % ... + % @funindex commtest + % + % The above is not enough to reproduce the bug, but it gives the flavor. + % + % Sample whatsit resulting: + % .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}} + % + % So: + \let\endinput = \empty + % + % Do the redefinitions. + \commondummies +} + +% For the aux and toc files, @ is the escape character. So we want to +% redefine everything using @ as the escape character (instead of +% \realbackslash, still used for index files). When everything uses @, +% this will be simpler. +% +\def\atdummies{% + \def\@{@@}% + \def\ {@ }% + \let\{ = \lbraceatcmd + \let\} = \rbraceatcmd + % + % Do the redefinitions. + \commondummies + \otherbackslash +} + +% Called from \indexdummies and \atdummies. +% +\def\commondummies{% + % + % \definedummyword defines \#1 as \string\#1\space, thus effectively + % preventing its expansion. This is used only for control% words, + % not control letters, because the \space would be incorrect for + % control characters, but is needed to separate the control word + % from whatever follows. + % + % For control letters, we have \definedummyletter, which omits the + % space. + % + % These can be used both for control words that take an argument and + % those that do not. If it is followed by {arg} in the input, then + % that will dutifully get written to the index (or wherever). + % + \def\definedummyword ##1{\def##1{\string##1\space}}% + \def\definedummyletter##1{\def##1{\string##1}}% + \let\definedummyaccent\definedummyletter + % + \commondummiesnofonts + % + \definedummyletter\_% + % + % Non-English letters. + \definedummyword\AA + \definedummyword\AE + \definedummyword\L + \definedummyword\OE + \definedummyword\O + \definedummyword\aa + \definedummyword\ae + \definedummyword\l + \definedummyword\oe + \definedummyword\o + \definedummyword\ss + \definedummyword\exclamdown + \definedummyword\questiondown + \definedummyword\ordf + \definedummyword\ordm + % + % Although these internal commands shouldn't show up, sometimes they do. + \definedummyword\bf + \definedummyword\gtr + \definedummyword\hat + \definedummyword\less + \definedummyword\sf + \definedummyword\sl + \definedummyword\tclose + \definedummyword\tt + % + \definedummyword\LaTeX + \definedummyword\TeX + % + % Assorted special characters. + \definedummyword\bullet + \definedummyword\comma + \definedummyword\copyright + \definedummyword\registeredsymbol + \definedummyword\dots + \definedummyword\enddots + \definedummyword\equiv + \definedummyword\error + \definedummyword\euro + \definedummyword\expansion + \definedummyword\minus + \definedummyword\pounds + \definedummyword\point + \definedummyword\print + \definedummyword\result + \definedummyword\textdegree + % + % We want to disable all macros so that they are not expanded by \write. + \macrolist + % + \normalturnoffactive + % + % Handle some cases of @value -- where it does not contain any + % (non-fully-expandable) commands. + \makevalueexpandable +} + +% \commondummiesnofonts: common to \commondummies and \indexnofonts. +% +\def\commondummiesnofonts{% + % Control letters and accents. + \definedummyletter\!% + \definedummyaccent\"% + \definedummyaccent\'% + \definedummyletter\*% + \definedummyaccent\,% + \definedummyletter\.% + \definedummyletter\/% + \definedummyletter\:% + \definedummyaccent\=% + \definedummyletter\?% + \definedummyaccent\^% + \definedummyaccent\`% + \definedummyaccent\~% + \definedummyword\u + \definedummyword\v + \definedummyword\H + \definedummyword\dotaccent + \definedummyword\ringaccent + \definedummyword\tieaccent + \definedummyword\ubaraccent + \definedummyword\udotaccent + \definedummyword\dotless + % + % Texinfo font commands. + \definedummyword\b + \definedummyword\i + \definedummyword\r + \definedummyword\sc + \definedummyword\t + % + % Commands that take arguments. + \definedummyword\acronym + \definedummyword\cite + \definedummyword\code + \definedummyword\command + \definedummyword\dfn + \definedummyword\emph + \definedummyword\env + \definedummyword\file + \definedummyword\kbd + \definedummyword\key + \definedummyword\math + \definedummyword\option + \definedummyword\pxref + \definedummyword\ref + \definedummyword\samp + \definedummyword\strong + \definedummyword\tie + \definedummyword\uref + \definedummyword\url + \definedummyword\var + \definedummyword\verb + \definedummyword\w + \definedummyword\xref +} + +% \indexnofonts is used when outputting the strings to sort the index +% by, and when constructing control sequence names. It eliminates all +% control sequences and just writes whatever the best ASCII sort string +% would be for a given command (usually its argument). +% +\def\indexnofonts{% + % Accent commands should become @asis. + \def\definedummyaccent##1{\let##1\asis}% + % We can just ignore other control letters. + \def\definedummyletter##1{\let##1\empty}% + % Hopefully, all control words can become @asis. + \let\definedummyword\definedummyaccent + % + \commondummiesnofonts + % + % Don't no-op \tt, since it isn't a user-level command + % and is used in the definitions of the active chars like <, >, |, etc. + % Likewise with the other plain tex font commands. + %\let\tt=\asis + % + \def\ { }% + \def\@{@}% + % how to handle braces? + \def\_{\normalunderscore}% + % + % Non-English letters. + \def\AA{AA}% + \def\AE{AE}% + \def\L{L}% + \def\OE{OE}% + \def\O{O}% + \def\aa{aa}% + \def\ae{ae}% + \def\l{l}% + \def\oe{oe}% + \def\o{o}% + \def\ss{ss}% + \def\exclamdown{!}% + \def\questiondown{?}% + \def\ordf{a}% + \def\ordm{o}% + % + \def\LaTeX{LaTeX}% + \def\TeX{TeX}% + % + % Assorted special characters. + % (The following {} will end up in the sort string, but that's ok.) + \def\bullet{bullet}% + \def\comma{,}% + \def\copyright{copyright}% + \def\registeredsymbol{R}% + \def\dots{...}% + \def\enddots{...}% + \def\equiv{==}% + \def\error{error}% + \def\euro{euro}% + \def\expansion{==>}% + \def\minus{-}% + \def\pounds{pounds}% + \def\point{.}% + \def\print{-|}% + \def\result{=>}% + \def\textdegree{degrees}% + % + % We need to get rid of all macros, leaving only the arguments (if present). + % Of course this is not nearly correct, but it is the best we can do for now. + % makeinfo does not expand macros in the argument to @deffn, which ends up + % writing an index entry, and texindex isn't prepared for an index sort entry + % that starts with \. + % + % Since macro invocations are followed by braces, we can just redefine them + % to take a single TeX argument. The case of a macro invocation that + % goes to end-of-line is not handled. + % + \macrolist +} + +\let\indexbackslash=0 %overridden during \printindex. +\let\SETmarginindex=\relax % put index entries in margin (undocumented)? + +% Most index entries go through here, but \dosubind is the general case. +% #1 is the index name, #2 is the entry text. +\def\doind#1#2{\dosubind{#1}{#2}{}} + +% Workhorse for all \fooindexes. +% #1 is name of index, #2 is stuff to put there, #3 is subentry -- +% empty if called from \doind, as we usually are (the main exception +% is with most defuns, which call us directly). +% +\def\dosubind#1#2#3{% + \iflinks + {% + % Store the main index entry text (including the third arg). + \toks0 = {#2}% + % If third arg is present, precede it with a space. + \def\thirdarg{#3}% + \ifx\thirdarg\empty \else + \toks0 = \expandafter{\the\toks0 \space #3}% + \fi + % + \edef\writeto{\csname#1indfile\endcsname}% + % + \ifvmode + \dosubindsanitize + \else + \dosubindwrite + \fi + }% + \fi +} + +% Write the entry in \toks0 to the index file: +% +\def\dosubindwrite{% + % Put the index entry in the margin if desired. + \ifx\SETmarginindex\relax\else + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}% + \fi + % + % Remember, we are within a group. + \indexdummies % Must do this here, since \bf, etc expand at this stage + \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now + % so it will be output as is; and it will print as backslash. + % + % Process the index entry with all font commands turned off, to + % get the string to sort by. + {\indexnofonts + \edef\temp{\the\toks0}% need full expansion + \xdef\indexsorttmp{\temp}% + }% + % + % Set up the complete index entry, with both the sort key and + % the original text, including any font commands. We write + % three arguments to \entry to the .?? file (four in the + % subentry case), texindex reduces to two when writing the .??s + % sorted result. + \edef\temp{% + \write\writeto{% + \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}% + }% + \temp +} + +% Take care of unwanted page breaks: +% +% If a skip is the last thing on the list now, preserve it +% by backing up by \lastskip, doing the \write, then inserting +% the skip again. Otherwise, the whatsit generated by the +% \write will make \lastskip zero. The result is that sequences +% like this: +% @end defun +% @tindex whatever +% @defun ... +% will have extra space inserted, because the \medbreak in the +% start of the @defun won't see the skip inserted by the @end of +% the previous defun. +% +% But don't do any of this if we're not in vertical mode. We +% don't want to do a \vskip and prematurely end a paragraph. +% +% Avoid page breaks due to these extra skips, too. +% +% But wait, there is a catch there: +% We'll have to check whether \lastskip is zero skip. \ifdim is not +% sufficient for this purpose, as it ignores stretch and shrink parts +% of the skip. The only way seems to be to check the textual +% representation of the skip. +% +% The following is almost like \def\zeroskipmacro{0.0pt} except that +% the ``p'' and ``t'' characters have catcode \other, not 11 (letter). +% +\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} +% +% ..., ready, GO: +% +\def\dosubindsanitize{% + % \lastskip and \lastpenalty cannot both be nonzero simultaneously. + \skip0 = \lastskip + \edef\lastskipmacro{\the\lastskip}% + \count255 = \lastpenalty + % + % If \lastskip is nonzero, that means the last item was a + % skip. And since a skip is discardable, that means this + % -\skip0 glue we're inserting is preceded by a + % non-discardable item, therefore it is not a potential + % breakpoint, therefore no \nobreak needed. + \ifx\lastskipmacro\zeroskipmacro + \else + \vskip-\skip0 + \fi + % + \dosubindwrite + % + \ifx\lastskipmacro\zeroskipmacro + % If \lastskip was zero, perhaps the last item was a penalty, and + % perhaps it was >=10000, e.g., a \nobreak. In that case, we want + % to re-insert the same penalty (values >10000 are used for various + % signals); since we just inserted a non-discardable item, any + % following glue (such as a \parskip) would be a breakpoint. For example: + % + % @deffn deffn-whatever + % @vindex index-whatever + % Description. + % would allow a break between the index-whatever whatsit + % and the "Description." paragraph. + \ifnum\count255>9999 \penalty\count255 \fi + \else + % On the other hand, if we had a nonzero \lastskip, + % this make-up glue would be preceded by a non-discardable item + % (the whatsit from the \write), so we must insert a \nobreak. + \nobreak\vskip\skip0 + \fi +} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% @printindex causes a particular index (the ??s file) to get printed. +% It does not print any chapter heading (usually an @unnumbered). +% +\parseargdef\printindex{\begingroup + \dobreak \chapheadingskip{10000}% + % + \smallfonts \rm + \tolerance = 9500 + \everypar = {}% don't want the \kern\-parindent from indentation suppression. + % + % See if the index file exists and is nonempty. + % Change catcode of @ here so that if the index file contains + % \initial {@} + % as its first line, TeX doesn't complain about mismatched braces + % (because it thinks @} is a control sequence). + \catcode`\@ = 11 + \openin 1 \jobname.#1s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + \putwordIndexNonexistent + \else + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \temp + \ifeof 1 + \putwordIndexIsEmpty + \else + % Index files are almost Texinfo source, but we use \ as the escape + % character. It would be better to use @, but that's too big a change + % to make right now. + \def\indexbackslash{\backslashcurfont}% + \catcode`\\ = 0 + \escapechar = `\\ + \begindoublecolumns + \input \jobname.#1s + \enddoublecolumns + \fi + \fi + \closein 1 +\endgroup} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +\def\initial#1{{% + % Some minor font changes for the special characters. + \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt + % + % Remove any glue we may have, we'll be inserting our own. + \removelastskip + % + % We like breaks before the index initials, so insert a bonus. + \nobreak + \vskip 0pt plus 3\baselineskip + \penalty 0 + \vskip 0pt plus -3\baselineskip + % + % Typeset the initial. Making this add up to a whole number of + % baselineskips increases the chance of the dots lining up from column + % to column. It still won't often be perfect, because of the stretch + % we need before each entry, but it's better. + % + % No shrink because it confuses \balancecolumns. + \vskip 1.67\baselineskip plus .5\baselineskip + \leftline{\secbf #1}% + % Do our best not to break after the initial. + \nobreak + \vskip .33\baselineskip plus .1\baselineskip +}} + +% \entry typesets a paragraph consisting of the text (#1), dot leaders, and +% then page number (#2) flushed to the right margin. It is used for index +% and table of contents entries. The paragraph is indented by \leftskip. +% +% A straightforward implementation would start like this: +% \def\entry#1#2{... +% But this frozes the catcodes in the argument, and can cause problems to +% @code, which sets - active. This problem was fixed by a kludge--- +% ``-'' was active throughout whole index, but this isn't really right. +% +% The right solution is to prevent \entry from swallowing the whole text. +% --kasal, 21nov03 +\def\entry{% + \begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent = 2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % A bit of stretch before each entry for the benefit of balancing + % columns. + \vskip 0pt plus1pt + % + % Swallow the left brace of the text (first parameter): + \afterassignment\doentry + \let\temp = +} +\def\doentry{% + \bgroup % Instead of the swallowed brace. + \noindent + \aftergroup\finishentry + % And now comes the text of the entry. +} +\def\finishentry#1{% + % #1 is the page number. + % + % The following is kludged to not output a line of dots in the index if + % there are no page numbers. The next person who breaks this will be + % cursed by a Unix daemon. + \def\tempa{{\rm }}% + \def\tempb{#1}% + \edef\tempc{\tempa}% + \edef\tempd{\tempb}% + \ifx\tempc\tempd + \ % + \else + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ifpdf + \pdfgettoks#1.% + \ \the\toksA + \else + \ #1% + \fi + \fi + \par + \endgroup +} + +% Like plain.tex's \dotfill, except uses up at least 1 em. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill} + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm +\def\secondary#1#2{{% + \parfillskip=0in + \parskip=0in + \hangindent=1in + \hangafter=1 + \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill + \ifpdf + \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. + \else + #2 + \fi + \par +}} + +% Define two-column mode, which we use to typeset indexes. +% Adapted from the TeXbook, page 416, which is to say, +% the manmac.tex format used to print the TeXbook itself. +\catcode`\@=11 + +\newbox\partialpage +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns + % Grab any single-column material above us. + \output = {% + % + % Here is a possibility not foreseen in manmac: if we accumulate a + % whole lot of material, we might end up calling this \output + % routine twice in a row (see the doublecol-lose test, which is + % essentially a couple of indexes with @setchapternewpage off). In + % that case we just ship out what is in \partialpage with the normal + % output routine. Generally, \partialpage will be empty when this + % runs and this will be a no-op. See the indexspread.tex test case. + \ifvoid\partialpage \else + \onepageout{\pagecontents\partialpage}% + \fi + % + \global\setbox\partialpage = \vbox{% + % Unvbox the main output page. + \unvbox\PAGE + \kern-\topskip \kern\baselineskip + }% + }% + \eject % run that output routine to set \partialpage + % + % Use the double-column output routine for subsequent pages. + \output = {\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it in one place. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +-<1pt) + % as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \vsize = 2\vsize +} + +% The double-column output routine for all double-column pages except +% the last. +% +\def\doublecolumnout{% + \splittopskip=\topskip \splitmaxdepth=\maxdepth + % Get the available space for the double columns -- the normal + % (undoubled) page height minus any material left over from the + % previous page. + \dimen@ = \vsize + \divide\dimen@ by 2 + \advance\dimen@ by -\ht\partialpage + % + % box0 will be the left-hand column, box2 the right. + \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ + \onepageout\pagesofar + \unvbox255 + \penalty\outputpenalty +} +% +% Re-output the contents of the output page -- any previous material, +% followed by the two boxes we just split, in box0 and box2. +\def\pagesofar{% + \unvbox\partialpage + % + \hsize = \doublecolumnhsize + \wd0=\hsize \wd2=\hsize + \hbox to\pagewidth{\box0\hfil\box2}% +} +% +% All done with double columns. +\def\enddoublecolumns{% + \output = {% + % Split the last of the double-column material. Leave it on the + % current page, no automatic page break. + \balancecolumns + % + % If we end up splitting too much material for the current page, + % though, there will be another page break right after this \output + % invocation ends. Having called \balancecolumns once, we do not + % want to call it again. Therefore, reset \output to its normal + % definition right away. (We hope \balancecolumns will never be + % called on to balance too much material, but if it is, this makes + % the output somewhat more palatable.) + \global\output = {\onepageout{\pagecontents\PAGE}}% + }% + \eject + \endgroup % started in \begindoublecolumns + % + % \pagegoal was set to the doubled \vsize above, since we restarted + % the current page. We're now back to normal single-column + % typesetting, so reset \pagegoal to the normal \vsize (after the + % \endgroup where \vsize got restored). + \pagegoal = \vsize +} +% +% Called at the end of the double column material. +\def\balancecolumns{% + \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. + \dimen@ = \ht0 + \advance\dimen@ by \topskip + \advance\dimen@ by-\baselineskip + \divide\dimen@ by 2 % target to split to + %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}% + \splittopskip = \topskip + % Loop until we get a decent breakpoint. + {% + \vbadness = 10000 + \loop + \global\setbox3 = \copy0 + \global\setbox1 = \vsplit3 to \dimen@ + \ifdim\ht3>\dimen@ + \global\advance\dimen@ by 1pt + \repeat + }% + %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}% + \setbox0=\vbox to\dimen@{\unvbox1}% + \setbox2=\vbox to\dimen@{\unvbox3}% + % + \pagesofar +} +\catcode`\@ = \other + + +\message{sectioning,} +% Chapters, sections, etc. + +% \unnumberedno is an oxymoron, of course. But we count the unnumbered +% sections so that we can refer to them unambiguously in the pdf +% outlines by their "section number". We avoid collisions with chapter +% numbers by starting them at 10000. (If a document ever has 10000 +% chapters, we're in trouble anyway, I'm sure.) +\newcount\unnumberedno \unnumberedno = 10000 +\newcount\chapno +\newcount\secno \secno=0 +\newcount\subsecno \subsecno=0 +\newcount\subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount\appendixno \appendixno = `\@ +% +% \def\appendixletter{\char\the\appendixno} +% We do the following ugly conditional instead of the above simple +% construct for the sake of pdftex, which needs the actual +% letter in the expansion, not just typeset. +% +\def\appendixletter{% + \ifnum\appendixno=`A A% + \else\ifnum\appendixno=`B B% + \else\ifnum\appendixno=`C C% + \else\ifnum\appendixno=`D D% + \else\ifnum\appendixno=`E E% + \else\ifnum\appendixno=`F F% + \else\ifnum\appendixno=`G G% + \else\ifnum\appendixno=`H H% + \else\ifnum\appendixno=`I I% + \else\ifnum\appendixno=`J J% + \else\ifnum\appendixno=`K K% + \else\ifnum\appendixno=`L L% + \else\ifnum\appendixno=`M M% + \else\ifnum\appendixno=`N N% + \else\ifnum\appendixno=`O O% + \else\ifnum\appendixno=`P P% + \else\ifnum\appendixno=`Q Q% + \else\ifnum\appendixno=`R R% + \else\ifnum\appendixno=`S S% + \else\ifnum\appendixno=`T T% + \else\ifnum\appendixno=`U U% + \else\ifnum\appendixno=`V V% + \else\ifnum\appendixno=`W W% + \else\ifnum\appendixno=`X X% + \else\ifnum\appendixno=`Y Y% + \else\ifnum\appendixno=`Z Z% + % The \the is necessary, despite appearances, because \appendixletter is + % expanded while writing the .toc file. \char\appendixno is not + % expandable, thus it is written literally, thus all appendixes come out + % with the same letter (or @) in the toc without it. + \else\char\the\appendixno + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} + +% Each @chapter defines this as the name of the chapter. +% page headings and footings can use it. @section does likewise. +% However, they are not reliable, because we don't use marks. +\def\thischapter{} +\def\thissection{} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% we only have subsub. +\chardef\maxseclevel = 3 +% +% A numbered section within an unnumbered changes to unnumbered too. +% To achive this, remember the "biggest" unnum. sec. we are currently in: +\chardef\unmlevel = \maxseclevel +% +% Trace whether the current chapter is an appendix or not: +% \chapheadtype is "N" or "A", unnumbered chapters are ignored. +\def\chapheadtype{N} + +% Choose a heading macro +% #1 is heading type +% #2 is heading level +% #3 is text for heading +\def\genhead#1#2#3{% + % Compute the abs. sec. level: + \absseclevel=#2 + \advance\absseclevel by \secbase + % Make sure \absseclevel doesn't fall outside the range: + \ifnum \absseclevel < 0 + \absseclevel = 0 + \else + \ifnum \absseclevel > 3 + \absseclevel = 3 + \fi + \fi + % The heading type: + \def\headtype{#1}% + \if \headtype U% + \ifnum \absseclevel < \unmlevel + \chardef\unmlevel = \absseclevel + \fi + \else + % Check for appendix sections: + \ifnum \absseclevel = 0 + \edef\chapheadtype{\headtype}% + \else + \if \headtype A\if \chapheadtype N% + \errmessage{@appendix... within a non-appendix chapter}% + \fi\fi + \fi + % Check for numbered within unnumbered: + \ifnum \absseclevel > \unmlevel + \def\headtype{U}% + \else + \chardef\unmlevel = 3 + \fi + \fi + % Now print the heading: + \if \headtype U% + \ifcase\absseclevel + \unnumberedzzz{#3}% + \or \unnumberedseczzz{#3}% + \or \unnumberedsubseczzz{#3}% + \or \unnumberedsubsubseczzz{#3}% + \fi + \else + \if \headtype A% + \ifcase\absseclevel + \appendixzzz{#3}% + \or \appendixsectionzzz{#3}% + \or \appendixsubseczzz{#3}% + \or \appendixsubsubseczzz{#3}% + \fi + \else + \ifcase\absseclevel + \chapterzzz{#3}% + \or \seczzz{#3}% + \or \numberedsubseczzz{#3}% + \or \numberedsubsubseczzz{#3}% + \fi + \fi + \fi + \suppressfirstparagraphindent +} + +% an interface: +\def\numhead{\genhead N} +\def\apphead{\genhead A} +\def\unnmhead{\genhead U} + +% @chapter, @appendix, @unnumbered. Increment top-level counter, reset +% all lower-level sectioning counters to zero. +% +% Also set \chaplevelprefix, which we prepend to @float sequence numbers +% (e.g., figures), q.v. By default (before any chapter), that is empty. +\let\chaplevelprefix = \empty +% +\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz#1{% + % section resetting is \global in case the chapter is in a group, such + % as an @include file. + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\chapno by 1 + % + % Used for \float. + \gdef\chaplevelprefix{\the\chapno.}% + \resetallfloatnos + % + \message{\putwordChapter\space \the\chapno}% + % + % Write the actual heading. + \chapmacro{#1}{Ynumbered}{\the\chapno}% + % + % So @section and the like are numbered underneath this chapter. + \global\let\section = \numberedsec + \global\let\subsection = \numberedsubsec + \global\let\subsubsection = \numberedsubsubsec +} + +\outer\parseargdef\appendix{\apphead0{#1}} % normally apphead0 calls appendixzzz +\def\appendixzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\appendixno by 1 + \gdef\chaplevelprefix{\appendixletter.}% + \resetallfloatnos + % + \def\appendixnum{\putwordAppendix\space \appendixletter}% + \message{\appendixnum}% + % + \chapmacro{#1}{Yappendix}{\appendixletter}% + % + \global\let\section = \appendixsec + \global\let\subsection = \appendixsubsec + \global\let\subsubsection = \appendixsubsubsec +} + +\outer\parseargdef\unnumbered{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz +\def\unnumberedzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\unnumberedno by 1 + % + % Since an unnumbered has no number, no prefix for figures. + \global\let\chaplevelprefix = \empty + \resetallfloatnos + % + % This used to be simply \message{#1}, but TeX fully expands the + % argument to \message. Therefore, if #1 contained @-commands, TeX + % expanded them. For example, in `@unnumbered The @cite{Book}', TeX + % expanded @cite (which turns out to cause errors because \cite is meant + % to be executed, not expanded). + % + % Anyway, we don't want the fully-expanded definition of @cite to appear + % as a result of the \message, we just want `@cite' itself. We use + % \the to achieve this: TeX expands \the only once, + % simply yielding the contents of . (We also do this for + % the toc entries.) + \toks0 = {#1}% + \message{(\the\toks0)}% + % + \chapmacro{#1}{Ynothing}{\the\unnumberedno}% + % + \global\let\section = \unnumberedsec + \global\let\subsection = \unnumberedsubsec + \global\let\subsubsection = \unnumberedsubsubsec +} + +% @centerchap is like @unnumbered, but the heading is centered. +\outer\parseargdef\centerchap{% + % Well, we could do the following in a group, but that would break + % an assumption that \chapmacro is called at the outermost level. + % Thus we are safer this way: --kasal, 24feb04 + \let\centerparametersmaybe = \centerparameters + \unnmhead0{#1}% + \let\centerparametersmaybe = \relax +} + +% @top is like @unnumbered. +\let\top\unnumbered + +% Sections. +\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz +\def\seczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% +} + +\outer\parseargdef\appendixsection{\apphead1{#1}} % normally calls appendixsectionzzz +\def\appendixsectionzzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% +} +\let\appendixsec\appendixsection + +\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} % normally calls unnumberedseczzz +\def\unnumberedseczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% +} + +% Subsections. +\outer\parseargdef\numberedsubsec{\numhead2{#1}} % normally calls numberedsubseczzz +\def\numberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% +} + +\outer\parseargdef\appendixsubsec{\apphead2{#1}} % normally calls appendixsubseczzz +\def\appendixsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno}% +} + +\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} %normally calls unnumberedsubseczzz +\def\unnumberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno}% +} + +% Subsubsections. +\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} % normally numberedsubsubseczzz +\def\numberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynumbered}% + {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} % normally appendixsubsubseczzz +\def\appendixsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} %normally unnumberedsubsubseczzz +\def\unnumberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\let\section = \numberedsec +\let\subsection = \numberedsubsec +\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +% NOTE on use of \vbox for chapter headings, section headings, and such: +% 1) We use \vbox rather than the earlier \line to permit +% overlong headings to fold. +% 2) \hyphenpenalty is set to 10000 because hyphenation in a +% heading is obnoxious; this forbids it. +% 3) Likewise, headings look best if no \parindent is used, and +% if justification is not attempted. Hence \raggedright. + + +\def\majorheading{% + {\advance\chapheadingskip by 10pt \chapbreak }% + \parsearg\chapheadingzzz +} + +\def\chapheading{\chapbreak \parsearg\chapheadingzzz} +\def\chapheadingzzz#1{% + {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}% + \bigskip \par\penalty 200\relax + \suppressfirstparagraphindent +} + +% @heading, @subheading, @subsubheading. +\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +%%% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +%%% Define plain chapter starts, and page on/off switching for it +% Parameter controlling skip before chapter headings (if needed) + +\newskip\chapheadingskip + +\def\chapbreak{\dobreak \chapheadingskip {-4000}} +\def\chappager{\par\vfill\supereject} +\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{% +\global\let\contentsalignmacro = \chapoddpage +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +% Chapter opening. +% +% #1 is the text, #2 is the section type (Ynumbered, Ynothing, +% Yappendix, Yomitfromtoc), #3 the chapter number. +% +% To test against our argument. +\def\Ynothingkeyword{Ynothing} +\def\Yomitfromtockeyword{Yomitfromtoc} +\def\Yappendixkeyword{Yappendix} +% +\def\chapmacro#1#2#3{% + \pchapsepmacro + {% + \chapfonts \rm + % + % Have to define \thissection before calling \donoderef, because the + % xref code eventually uses it. On the other hand, it has to be called + % after \pchapsepmacro, or the headline will change too soon. + \gdef\thissection{#1}% + \gdef\thischaptername{#1}% + % + % Only insert the separating space if we have a chapter/appendix + % number, and don't print the unnumbered ``number''. + \def\temptype{#2}% + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unnchap}% + \gdef\thischapternum{}% + \gdef\thischapter{#1}% + \else\ifx\temptype\Yomitfromtockeyword + \setbox0 = \hbox{}% contents like unnumbered, but no toc entry + \def\toctype{omit}% + \gdef\thischapternum{}% + \gdef\thischapter{}% + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% + \def\toctype{app}% + \xdef\thischapternum{\appendixletter}% + % We don't substitute the actual chapter name into \thischapter + % because we don't want its macros evaluated now. And we don't + % use \thissection because that changes with each section. + % + \xdef\thischapter{\putwordAppendix{} \appendixletter: + \noexpand\thischaptername}% + \else + \setbox0 = \hbox{#3\enspace}% + \def\toctype{numchap}% + \xdef\thischapternum{\the\chapno}% + \xdef\thischapter{\putwordChapter{} \the\chapno: + \noexpand\thischaptername}% + \fi\fi\fi + % + % Write the toc entry for this chapter. Must come before the + % \donoderef, because we include the current node name in the toc + % entry, and \donoderef resets it to empty. + \writetocentry{\toctype}{#1}{#3}% + % + % For pdftex, we have to write out the node definition (aka, make + % the pdfdest) after any page break, but before the actual text has + % been typeset. If the destination for the pdf outline is after the + % text, then jumping from the outline may wind up with the text not + % being visible, for instance under high magnification. + \donoderef{#2}% + % + % Typeset the actual heading. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright + \hangindent=\wd0 \centerparametersmaybe + \unhbox0 #1\par}% + }% + \nobreak\bigskip % no page break after a chapter title + \nobreak +} + +% @centerchap -- centered and unnumbered. +\let\centerparametersmaybe = \relax +\def\centerparameters{% + \advance\rightskip by 3\rightskip + \leftskip = \rightskip + \parfillskip = 0pt +} + + +% I don't think this chapter style is supported any more, so I'm not +% updating it with the new noderef stuff. We'll see. --karl, 11aug03. +% +\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} +% +\def\unnchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\nobreak +} +\def\chfopen #1#2{\chapoddpage {\chapfonts +\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% +\par\penalty 5000 % +} +\def\centerchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt + \hfill {\rm #1}\hfill}}\bigskip \par\nobreak +} +\def\CHAPFopen{% + \global\let\chapmacro=\chfopen + \global\let\centerchapmacro=\centerchfopen} + + +% Section titles. These macros combine the section number parts and +% call the generic \sectionheading to do the printing. +% +\newskip\secheadingskip +\def\secheadingbreak{\dobreak \secheadingskip{-1000}} + +% Subsection titles. +\newskip\subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} + +% Subsubsection titles. +\def\subsubsecheadingskip{\subsecheadingskip} +\def\subsubsecheadingbreak{\subsecheadingbreak} + + +% Print any size, any type, section title. +% +% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is +% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the +% section number. +% +\def\sectionheading#1#2#3#4{% + {% + % Switch to the right set of fonts. + \csname #2fonts\endcsname \rm + % + % Insert space above the heading. + \csname #2headingbreak\endcsname + % + % Only insert the space after the number if we have a section number. + \def\sectionlevel{#2}% + \def\temptype{#3}% + % + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unn}% + \gdef\thissection{#1}% + \else\ifx\temptype\Yomitfromtockeyword + % for @headings -- no section number, don't include in toc, + % and don't redefine \thissection. + \setbox0 = \hbox{}% + \def\toctype{omit}% + \let\sectionlevel=\empty + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{#4\enspace}% + \def\toctype{app}% + \gdef\thissection{#1}% + \else + \setbox0 = \hbox{#4\enspace}% + \def\toctype{num}% + \gdef\thissection{#1}% + \fi\fi\fi + % + % Write the toc entry (before \donoderef). See comments in \chapmacro. + \writetocentry{\toctype\sectionlevel}{#1}{#4}% + % + % Write the node reference (= pdf destination for pdftex). + % Again, see comments in \chapmacro. + \donoderef{#3}% + % + % Interline glue will be inserted when the vbox is completed. + % That glue will be a valid breakpoint for the page, since it'll be + % preceded by a whatsit (usually from the \donoderef, or from the + % \writetocentry if there was no node). We don't want to allow that + % break, since then the whatsits could end up on page n while the + % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. + \nobreak + % + % Output the actual section heading. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright + \hangindent=\wd0 % zero if no section number + \unhbox0 #1}% + }% + % Add extra space after the heading -- half of whatever came above it. + % Don't allow stretch, though. + \kern .5 \csname #2headingskip\endcsname + % + % Do not let the kern be a potential breakpoint, as it would be if it + % was followed by glue. + \nobreak + % + % We'll almost certainly start a paragraph next, so don't let that + % glue accumulate. (Not a breakpoint because it's preceded by a + % discardable item.) + \vskip-\parskip + % + % This is purely so the last item on the list is a known \penalty > + % 10000. This is so \startdefun can avoid allowing breakpoints after + % section headings. Otherwise, it would insert a valid breakpoint between: + % + % @section sec-whatever + % @deffn def-whatever + \penalty 10001 +} + + +\message{toc,} +% Table of contents. +\newwrite\tocfile + +% Write an entry to the toc file, opening it if necessary. +% Called from @chapter, etc. +% +% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} +% We append the current node name (if any) and page number as additional +% arguments for the \{chap,sec,...}entry macros which will eventually +% read this. The node name is used in the pdf outlines as the +% destination to jump to. +% +% We open the .toc file for writing here instead of at @setfilename (or +% any other fixed time) so that @contents can be anywhere in the document. +% But if #1 is `omit', then we don't do anything. This is used for the +% table of contents chapter openings themselves. +% +\newif\iftocfileopened +\def\omitkeyword{omit}% +% +\def\writetocentry#1#2#3{% + \edef\writetoctype{#1}% + \ifx\writetoctype\omitkeyword \else + \iftocfileopened\else + \immediate\openout\tocfile = \jobname.toc + \global\tocfileopenedtrue + \fi + % + \iflinks + {\atdummies + \edef\temp{% + \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% + \temp + }% + \fi + \fi + % + % Tell \shipout to create a pdf destination on each page, if we're + % writing pdf. These are used in the table of contents. We can't + % just write one on every page because the title pages are numbered + % 1 and 2 (the page numbers aren't printed), and so are the first + % two pages of the document. Thus, we'd have two destinations named + % `1', and two named `2'. + \ifpdf \global\pdfmakepagedesttrue \fi +} + + +% These characters do not print properly in the Computer Modern roman +% fonts, so we must take special care. This is more or less redundant +% with the Texinfo input format setup at the end of this file. +% +\def\activecatcodes{% + \catcode`\"=\active + \catcode`\$=\active + \catcode`\<=\active + \catcode`\>=\active + \catcode`\\=\active + \catcode`\^=\active + \catcode`\_=\active + \catcode`\|=\active + \catcode`\~=\active +} + + +% Read the toc file, which is essentially Texinfo input. +\def\readtocfile{% + \setupdatafile + \activecatcodes + \input \jobname.toc +} + +\newskip\contentsrightmargin \contentsrightmargin=1in +\newcount\savepageno +\newcount\lastnegativepageno \lastnegativepageno = -1 + +% Prepare to read what we've written to \tocfile. +% +\def\startcontents#1{% + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. Thus, we maintain + % \contentsalignmacro in parallel with \pagealignmacro. + % From: Torbjorn Granlund + \contentsalignmacro + \immediate\closeout\tocfile + % + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \def\thischapter{}% + \chapmacro{#1}{Yomitfromtoc}{}% + % + \savepageno = \pageno + \begingroup % Set up to handle contents files properly. + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. + % + % Roman numerals for page numbers. + \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi +} + + +% Normal (long) toc. +\def\contents{% + \startcontents{\putwordTOC}% + \openin 1 \jobname.toc + \ifeof 1 \else + \readtocfile + \fi + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \ifeof 1 \else + \pdfmakeoutlines + \fi + \closein 1 + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} + +% And just the chapters. +\def\summarycontents{% + \startcontents{\putwordShortTOC}% + % + \let\numchapentry = \shortchapentry + \let\appentry = \shortchapentry + \let\unnchapentry = \shortunnchapentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf + \let\sl=\shortcontsl \let\tt=\shortconttt + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\numsecentry##1##2##3##4{} + \let\appsecentry = \numsecentry + \let\unnsecentry = \numsecentry + \let\numsubsecentry = \numsecentry + \let\appsubsecentry = \numsecentry + \let\unnsubsecentry = \numsecentry + \let\numsubsubsecentry = \numsecentry + \let\appsubsubsecentry = \numsecentry + \let\unnsubsubsecentry = \numsecentry + \openin 1 \jobname.toc + \ifeof 1 \else + \readtocfile + \fi + \closein 1 + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} +\let\shortcontents = \summarycontents + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g., `A' for an appendix, or `3' for a chapter. +% +\def\shortchaplabel#1{% + % This space should be enough, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % But use \hss just in case. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + % + % We'd like to right-justify chapter numbers, but that looks strange + % with appendix letters. And right-justifying numbers and + % left-justifying letters looks strange when there is less than 10 + % chapters. Have to read the whole toc once to know how many chapters + % there are before deciding ... + \hbox to 1em{#1\hss}% +} + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Chapters, in the main contents. +\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} +% +% Chapters, in the short toc. +% See comments in \dochapentry re vbox and related settings. +\def\shortchapentry#1#2#3#4{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% +} + +% Appendices, in the main contents. +% Need the word Appendix, and a fixed-size box. +% +\def\appendixbox#1{% + % We use M since it's probably the widest letter. + \setbox0 = \hbox{\putwordAppendix{} M}% + \hbox to \wd0{\putwordAppendix{} #1\hss}} +% +\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}} + +% Unnumbered chapters. +\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} +\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} + +% Sections. +\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} +\let\appsecentry=\numsecentry +\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} + +% Subsections. +\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} +\let\appsubsecentry=\numsubsecentry +\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} + +% And subsubsections. +\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} +\let\appsubsubsecentry=\numsubsubsecentry +\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} + +% This parameter controls the indentation of the various levels. +% Same as \defaultparindent. +\newdimen\tocindent \tocindent = 15pt + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we want it to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip + \begingroup + \chapentryfonts + \tocentry{#1}{\dopageno\bgroup#2\egroup}% + \endgroup + \nobreak\vskip .25\baselineskip plus.1\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +% We use the same \entry macro as for the index entries. +\let\tocentry = \entry + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\def\subsecentryfonts{\textfonts} +\def\subsubsecentryfonts{\textfonts} + + +\message{environments,} +% @foo ... @end foo. + +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +% +% Since these characters are used in examples, it should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% +\def\point{$\star$} +\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} +\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% The @error{} command. +% Adapted from the TeXbook's \boxit. +% +\newbox\errorbox +% +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \reducedsf error\kern-1.5pt} +% +\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{% + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} +% +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @tex ... @end tex escapes into raw Tex temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain tex @ character. + +\envdef\tex{% + \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 + \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 + \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie + \catcode `\%=14 + \catcode `\+=\other + \catcode `\"=\other + \catcode `\|=\other + \catcode `\<=\other + \catcode `\>=\other + \escapechar=`\\ + % + \let\b=\ptexb + \let\bullet=\ptexbullet + \let\c=\ptexc + \let\,=\ptexcomma + \let\.=\ptexdot + \let\dots=\ptexdots + \let\equiv=\ptexequiv + \let\!=\ptexexclam + \let\i=\ptexi + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \let\{=\ptexlbrace + \let\+=\tabalign + \let\}=\ptexrbrace + \let\/=\ptexslash + \let\*=\ptexstar + \let\t=\ptext + \let\frenchspacing=\plainfrenchspacing + % + \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% + \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% + \def\@{@}% +} +% There is no need to define \Etex. + +% Define @lisp ... @end lisp. +% @lisp environment forms a group so it can rebind things, +% including the definition of @end lisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip. +% +\def\aboveenvbreak{{% + % =10000 instead of <10000 because of a special case in \itemzzz and + % \sectionheading, q.v. + \ifnum \lastpenalty=10000 \else + \advance\envskipamount by \parskip + \endgraf + \ifdim\lastskip<\envskipamount + \removelastskip + % it's not a good place to break if the last penalty was \nobreak + % or better ... + \ifnum\lastpenalty<10000 \penalty-50 \fi + \vskip\envskipamount + \fi + \fi +}} + +\let\afterenvbreak = \aboveenvbreak + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will +% also clear it, so that its embedded environments do the narrowing again. +\let\nonarrowing=\relax + +% @cartouche ... @end cartouche: draw rectangle w/rounded corners around +% environment contents. +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\envdef\cartouche{% + \ifhmode\par\fi % can't be in the midst of a paragraph. + \startsavinginserts + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt % we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18.4pt % allow for 3pt kerns on either + % side, and for 6pt waste from + % each corner char, and rule thickness + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing = t% + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \kern3pt + \hsize=\cartinner + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip + \comment % For explanation, see the end of \def\group. +} +\def\Ecartouche{% + \ifhmode\par\fi + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup + \checkinserts +} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\def\nonfillstart{% + \aboveenvbreak + \hfuzz = 12pt % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + \parindent = 0pt + \emergencystretch = 0pt % don't try to avoid overfull boxes + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \let\exdent=\nofillexdent +} + +% If you want all examples etc. small: @set dispenvsize small. +% If you want even small examples the full size: @set dispenvsize nosmall. +% This affects the following displayed environments: +% @example, @display, @format, @lisp +% +\def\smallword{small} +\def\nosmallword{nosmall} +\let\SETdispenvsize\relax +\def\setnormaldispenv{% + \ifx\SETdispenvsize\smallword + \smallexamplefonts \rm + \fi +} +\def\setsmalldispenv{% + \ifx\SETdispenvsize\nosmallword + \else + \smallexamplefonts \rm + \fi +} + +% We often define two environments, @foo and @smallfoo. +% Let's do it by one command: +\def\makedispenv #1#2{ + \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2} + \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2} + \expandafter\let\csname E#1\endcsname \afterenvbreak + \expandafter\let\csname Esmall#1\endcsname \afterenvbreak +} + +% Define two synonyms: +\def\maketwodispenvs #1#2#3{ + \makedispenv{#1}{#3} + \makedispenv{#2}{#3} +} + +% @lisp: indented, narrowed, typewriter font; @example: same as @lisp. +% +% @smallexample and @smalllisp: use smaller fonts. +% Originally contributed by Pavel@xerox. +% +\maketwodispenvs {lisp}{example}{% + \nonfillstart + \tt\quoteexpand + \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. + \gobble % eat return +} +% @display/@smalldisplay: same as @lisp except keep current font. +% +\makedispenv {display}{% + \nonfillstart + \gobble +} + +% @format/@smallformat: same as @display except don't narrow margins. +% +\makedispenv{format}{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} + +% @flushleft: same as @format, but doesn't obey \SETdispenvsize. +\envdef\flushleft{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} +\let\Eflushleft = \afterenvbreak + +% @flushright. +% +\envdef\flushright{% + \let\nonarrowing = t% + \nonfillstart + \advance\leftskip by 0pt plus 1fill + \gobble +} +\let\Eflushright = \afterenvbreak + + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. We keep \parskip nonzero in general, since +% we're doing normal filling. So, when using \aboveenvbreak and +% \afterenvbreak, temporarily make \parskip 0. +% +\envdef\quotation{% + {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip + \parindent=0pt + % + % @cartouche defines \nonarrowing to inhibit narrowing at next level down. + \ifx\nonarrowing\relax + \advance\leftskip by \lispnarrowing + \advance\rightskip by \lispnarrowing + \exdentamount = \lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \parsearg\quotationlabel +} + +% We have retained a nonzero parskip for the environment, since we're +% doing normal filling. +% +\def\Equotation{% + \par + \ifx\quotationauthor\undefined\else + % indent a bit. + \leftline{\kern 2\leftskip \sl ---\quotationauthor}% + \fi + {\parskip=0pt \afterenvbreak}% +} + +% If we're given an argument, typeset it in bold with a colon after. +\def\quotationlabel#1{% + \def\temp{#1}% + \ifx\temp\empty \else + {\bf #1: }% + \fi +} + + +% LaTeX-like @verbatim...@end verbatim and @verb{...} +% If we want to allow any as delimiter, +% we need the curly braces so that makeinfo sees the @verb command, eg: +% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org +% +% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. +% +% [Knuth] p.344; only we need to do the other characters Texinfo sets +% active too. Otherwise, they get lost as the first character on a +% verbatim line. +\def\dospecials{% + \do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% + \do\<\do\>\do\|\do\@\do+\do\"% +} +% +% [Knuth] p. 380 +\def\uncatcodespecials{% + \def\do##1{\catcode`##1=\other}\dospecials} +% +% [Knuth] pp. 380,381,391 +% Disable Spanish ligatures ?` and !` of \tt font +\begingroup + \catcode`\`=\active\gdef`{\relax\lq} +\endgroup +% +% Setup for the @verb command. +% +% Eight spaces for a tab +\begingroup + \catcode`\^^I=\active + \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} +\endgroup +% +\def\setupverb{% + \tt % easiest (and conventionally used) font for verbatim + \def\par{\leavevmode\endgraf}% + \catcode`\`=\active + \tabeightspaces + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces +} + +% Setup for the @verbatim environment +% +% Real tab expansion +\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount +% +\def\starttabbox{\setbox0=\hbox\bgroup} + +% Allow an option to not replace quotes with a regular directed right +% quote/apostrophe (char 0x27), but instead use the undirected quote +% from cmtt (char 0x0d). The undirected quote is ugly, so don't make it +% the default, but it works for pasting with more pdf viewers (at least +% evince), the lilypond developers report. xpdf does work with the +% regular 0x27. +% +\def\codequoteright{% + \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax + '% + \else + \char'15 + \fi +} +% +% and a similar option for the left quote char vs. a grave accent. +% Modern fonts display ASCII 0x60 as a grave accent, so some people like +% the code environments to do likewise. +% +\def\codequoteleft{% + \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax + `% + \else + \char'22 + \fi +} +% +\begingroup + \catcode`\^^I=\active + \gdef\tabexpand{% + \catcode`\^^I=\active + \def^^I{\leavevmode\egroup + \dimen0=\wd0 % the width so far, or since the previous tab + \divide\dimen0 by\tabw + \multiply\dimen0 by\tabw % compute previous multiple of \tabw + \advance\dimen0 by\tabw % advance to next multiple of \tabw + \wd0=\dimen0 \box0 \starttabbox + }% + } + \catcode`\'=\active + \gdef\rquoteexpand{\catcode\rquoteChar=\active \def'{\codequoteright}}% + % + \catcode`\`=\active + \gdef\lquoteexpand{\catcode\lquoteChar=\active \def`{\codequoteleft}}% + % + \gdef\quoteexpand{\rquoteexpand \lquoteexpand}% +\endgroup + +% start the verbatim environment. +\def\setupverbatim{% + \let\nonarrowing = t% + \nonfillstart + % Easiest (and conventionally used) font for verbatim + \tt + \def\par{\leavevmode\egroup\box0\endgraf}% + \catcode`\`=\active + \tabexpand + \quoteexpand + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces + \everypar{\starttabbox}% +} + +% Do the @verb magic: verbatim text is quoted by unique +% delimiter characters. Before first delimiter expect a +% right brace, after last delimiter expect closing brace: +% +% \def\doverb'{'#1'}'{#1} +% +% [Knuth] p. 382; only eat outer {} +\begingroup + \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other + \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] +\endgroup +% +\def\verb{\begingroup\setupverb\doverb} +% +% +% Do the @verbatim magic: define the macro \doverbatim so that +% the (first) argument ends when '@end verbatim' is reached, ie: +% +% \def\doverbatim#1@end verbatim{#1} +% +% For Texinfo it's a lot easier than for LaTeX, +% because texinfo's \verbatim doesn't stop at '\end{verbatim}': +% we need not redefine '\', '{' and '}'. +% +% Inspired by LaTeX's verbatim command set [latex.ltx] +% +\begingroup + \catcode`\ =\active + \obeylines % + % ignore everything up to the first ^^M, that's the newline at the end + % of the @verbatim input line itself. Otherwise we get an extra blank + % line in the output. + \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% + % We really want {...\end verbatim} in the body of the macro, but + % without the active space; thus we have to use \xdef and \gobble. +\endgroup +% +\envdef\verbatim{% + \setupverbatim\doverbatim +} +\let\Everbatim = \afterenvbreak + + +% @verbatiminclude FILE - insert text of file in verbatim environment. +% +\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} +% +\def\doverbatiminclude#1{% + {% + \makevalueexpandable + \setupverbatim + \input #1 + \afterenvbreak + }% +} + +% @copying ... @end copying. +% Save the text away for @insertcopying later. +% +% We save the uninterpreted tokens, rather than creating a box. +% Saving the text in a box would be much easier, but then all the +% typesetting commands (@smallbook, font changes, etc.) have to be done +% beforehand -- and a) we want @copying to be done first in the source +% file; b) letting users define the frontmatter in as flexible order as +% possible is very desirable. +% +\def\copying{\checkenv{}\begingroup\scanargctxt\docopying} +\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} +% +\def\insertcopying{% + \begingroup + \parindent = 0pt % paragraph indentation looks wrong on title page + \scanexp\copyingtext + \endgroup +} + +\message{defuns,} +% @defun etc. + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deflastargmargin \deflastargmargin=18pt + +% Start the processing of @deffn: +\def\startdefun{% + \ifnum\lastpenalty<10000 + \medbreak + \else + % If there are two @def commands in a row, we'll have a \nobreak, + % which is there to keep the function description together with its + % header. But if there's nothing but headers, we need to allow a + % break somewhere. Check specifically for penalty 10002, inserted + % by \defargscommonending, instead of 10000, since the sectioning + % commands also insert a nobreak penalty, and we don't want to allow + % a break between a section heading and a defun. + % + \ifnum\lastpenalty=10002 \penalty2000 \fi + % + % Similarly, after a section heading, do not allow a break. + % But do insert the glue. + \medskip % preceded by discardable penalty, so not a breakpoint + \fi + % + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent +} + +\def\dodefunx#1{% + % First, check whether we are in the right environment: + \checkenv#1% + % + % As above, allow line break if we have multiple x headers in a row. + % It's not a great place, though. + \ifnum\lastpenalty=10002 \penalty3000 \fi + % + % And now, it's time to reuse the body of the original defun: + \expandafter\gobbledefun#1% +} +\def\gobbledefun#1\startdefun{} + +% \printdefunline \deffnheader{text} +% +\def\printdefunline#1#2{% + \begingroup + % call \deffnheader: + #1#2 \endheader + % common ending: + \interlinepenalty = 10000 + \advance\rightskip by 0pt plus 1fil + \endgraf + \nobreak\vskip -\parskip + \penalty 10002 % signal to \startdefun and \dodefunx + % Some of the @defun-type tags do not enable magic parentheses, + % rendering the following check redundant. But we don't optimize. + \checkparencounts + \endgroup +} + +\def\Edefun{\endgraf\medbreak} + +% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; +% the only thing remainnig is to define \deffnheader. +% +\def\makedefun#1{% + \expandafter\let\csname E#1\endcsname = \Edefun + \edef\temp{\noexpand\domakedefun + \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% + \temp +} + +% \domakedefun \deffn \deffnx \deffnheader +% +% Define \deffn and \deffnx, without parameters. +% \deffnheader has to be defined explicitly. +% +\def\domakedefun#1#2#3{% + \envdef#1{% + \startdefun + \parseargusing\activeparens{\printdefunline#3}% + }% + \def#2{\dodefunx#1}% + \def#3% +} + +%%% Untyped functions: + +% @deffn category name args +\makedefun{deffn}{\deffngeneral{}} + +% @deffn category class name args +\makedefun{defop}#1 {\defopon{#1\ \putwordon}} + +% \defopon {category on}class name args +\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deffngeneral {subind}category name args +% +\def\deffngeneral#1#2 #3 #4\endheader{% + % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}. + \dosubind{fn}{\code{#3}}{#1}% + \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% +} + +%%% Typed functions: + +% @deftypefn category type name args +\makedefun{deftypefn}{\deftypefngeneral{}} + +% @deftypeop category class type name args +\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} + +% \deftypeopon {category on}class type name args +\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deftypefngeneral {subind}category type name args +% +\def\deftypefngeneral#1#2 #3 #4 #5\endheader{% + \dosubind{fn}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +%%% Typed variables: + +% @deftypevr category type var args +\makedefun{deftypevr}{\deftypecvgeneral{}} + +% @deftypecv category class type var args +\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} + +% \deftypecvof {category of}class type var args +\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } + +% \deftypecvgeneral {subind}category type var args +% +\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% + \dosubind{vr}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +%%% Untyped variables: + +% @defvr category var args +\makedefun{defvr}#1 {\deftypevrheader{#1} {} } + +% @defcv category class var args +\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} + +% \defcvof {category of}class var args +\def\defcvof#1#2 {\deftypecvof{#1}#2 {} } + +%%% Type: +% @deftp category name args +\makedefun{deftp}#1 #2 #3\endheader{% + \doind{tp}{\code{#2}}% + \defname{#1}{}{#2}\defunargs{#3\unskip}% +} + +% Remaining @defun-like shortcuts: +\makedefun{defun}{\deffnheader{\putwordDeffunc} } +\makedefun{defmac}{\deffnheader{\putwordDefmac} } +\makedefun{defspec}{\deffnheader{\putwordDefspec} } +\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } +\makedefun{defvar}{\defvrheader{\putwordDefvar} } +\makedefun{defopt}{\defvrheader{\putwordDefopt} } +\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } +\makedefun{defmethod}{\defopon\putwordMethodon} +\makedefun{deftypemethod}{\deftypeopon\putwordMethodon} +\makedefun{defivar}{\defcvof\putwordInstanceVariableof} +\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} + +% \defname, which formats the name of the @def (not the args). +% #1 is the category, such as "Function". +% #2 is the return type, if any. +% #3 is the function name. +% +% We are followed by (but not passed) the arguments, if any. +% +\def\defname#1#2#3{% + % Get the values of \leftskip and \rightskip as they were outside the @def... + \advance\leftskip by -\defbodyindent + % + % How we'll format the type name. Putting it in brackets helps + % distinguish it from the body text that may end up on the next line + % just below it. + \def\temp{#1}% + \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} + % + % Figure out line sizes for the paragraph shape. + % The first line needs space for \box0; but if \rightskip is nonzero, + % we need only space for the part of \box0 which exceeds it: + \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip + % The continuations: + \dimen2=\hsize \advance\dimen2 by -\defargsindent + % (plain.tex says that \dimen1 should be used only as global.) + \parshape 2 0in \dimen0 \defargsindent \dimen2 + % + % Put the type name to the right margin. + \noindent + \hbox to 0pt{% + \hfil\box0 \kern-\hsize + % \hsize has to be shortened this way: + \kern\leftskip + % Intentionally do not respect \rightskip, since we need the space. + }% + % + % Allow all lines to be underfull without complaint: + \tolerance=10000 \hbadness=10000 + \exdentamount=\defbodyindent + {% + % defun fonts. We use typewriter by default (used to be bold) because: + % . we're printing identifiers, they should be in tt in principle. + % . in languages with many accents, such as Czech or French, it's + % common to leave accents off identifiers. The result looks ok in + % tt, but exceedingly strange in rm. + % . we don't want -- and --- to be treated as ligatures. + % . this still does not fix the ?` and !` ligatures, but so far no + % one has made identifiers using them :). + \df \tt + \def\temp{#2}% return value type + \ifx\temp\empty\else \tclose{\temp} \fi + #3% output function name + }% + {\rm\enskip}% hskip 0.5 em of \tenrm + % + \boldbrax + % arguments will be output next, if any. +} + +% Print arguments in slanted roman (not ttsl), inconsistently with using +% tt for the name. This is because literal text is sometimes needed in +% the argument list (groff manual), and ttsl and tt are not very +% distinguishable. Prevent hyphenation at `-' chars. +% +\def\defunargs#1{% + % use sl by default (not ttsl), + % tt for the names. + \df \sl \hyphenchar\font=0 + % + % On the other hand, if an argument has two dashes (for instance), we + % want a way to get ttsl. Let's try @var for that. + \let\var=\ttslanted + #1% + \sl\hyphenchar\font=45 +} + +% We want ()&[] to print specially on the defun line. +% +\def\activeparens{% + \catcode`\(=\active \catcode`\)=\active + \catcode`\[=\active \catcode`\]=\active + \catcode`\&=\active +} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +{ + \activeparens + \global\let(=\lparen \global\let)=\rparen + \global\let[=\lbrack \global\let]=\rbrack + \global\let& = \& + + \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} + \gdef\magicamp{\let&=\amprm} +} + +\newcount\parencount + +% If we encounter &foo, then turn on ()-hacking afterwards +\newif\ifampseen +\def\amprm#1 {\ampseentrue{\bf\ }} + +\def\parenfont{% + \ifampseen + % At the first level, print parens in roman, + % otherwise use the default font. + \ifnum \parencount=1 \rm \fi + \else + % The \sf parens (in \boldbrax) actually are a little bolder than + % the contained text. This is especially needed for [ and ] . + \sf + \fi +} +\def\infirstlevel#1{% + \ifampseen + \ifnum\parencount=1 + #1% + \fi + \fi +} +\def\bfafterword#1 {#1 \bf} + +\def\opnr{% + \global\advance\parencount by 1 + {\parenfont(}% + \infirstlevel \bfafterword +} +\def\clnr{% + {\parenfont)}% + \infirstlevel \sl + \global\advance\parencount by -1 +} + +\newcount\brackcount +\def\lbrb{% + \global\advance\brackcount by 1 + {\bf[}% +} +\def\rbrb{% + {\bf]}% + \global\advance\brackcount by -1 +} + +\def\checkparencounts{% + \ifnum\parencount=0 \else \badparencount \fi + \ifnum\brackcount=0 \else \badbrackcount \fi +} +\def\badparencount{% + \errmessage{Unbalanced parentheses in @def}% + \global\parencount=0 +} +\def\badbrackcount{% + \errmessage{Unbalanced square braces in @def}% + \global\brackcount=0 +} + + +\message{macros,} +% @macro. + +% To do this right we need a feature of e-TeX, \scantokens, +% which we arrange to emulate with a temporary file in ordinary TeX. +\ifx\eTeXversion\undefined + \newwrite\macscribble + \def\scantokens#1{% + \toks0={#1}% + \immediate\openout\macscribble=\jobname.tmp + \immediate\write\macscribble{\the\toks0}% + \immediate\closeout\macscribble + \input \jobname.tmp + } +\fi + +\def\scanmacro#1{% + \begingroup + \newlinechar`\^^M + \let\xeatspaces\eatspaces + % Undo catcode changes of \startcontents and \doprintindex + % When called from @insertcopying or (short)caption, we need active + % backslash to get it printed correctly. Previously, we had + % \catcode`\\=\other instead. We'll see whether a problem appears + % with macro expansion. --kasal, 19aug04 + \catcode`\@=0 \catcode`\\=\active \escapechar=`\@ + % ... and \example + \spaceisspace + % + % Append \endinput to make sure that TeX does not see the ending newline. + % I've verified that it is necessary both for e-TeX and for ordinary TeX + % --kasal, 29nov03 + \scantokens{#1\endinput}% + \endgroup +} + +\def\scanexp#1{% + \edef\temp{\noexpand\scanmacro{#1}}% + \temp +} + +\newcount\paramno % Count of parameters +\newtoks\macname % Macro name +\newif\ifrecursive % Is it recursive? + +% List of all defined macros in the form +% \definedummyword\macro1\definedummyword\macro2... +% Currently is also contains all @aliases; the list can be split +% if there is a need. +\def\macrolist{} + +% Add the macro to \macrolist +\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} +\def\addtomacrolistxxx#1{% + \toks0 = \expandafter{\macrolist\definedummyword#1}% + \xdef\macrolist{\the\toks0}% +} + +% Utility routines. +% This does \let #1 = #2, with \csnames; that is, +% \let \csname#1\endcsname = \csname#2\endcsname +% (except of course we have to play expansion games). +% +\def\cslet#1#2{% + \expandafter\let + \csname#1\expandafter\endcsname + \csname#2\endcsname +} + +% Trim leading and trailing spaces off a string. +% Concepts from aro-bend problem 15 (see CTAN). +{\catcode`\@=11 +\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} +\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} +\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} +\def\unbrace#1{#1} +\unbrace{\gdef\trim@@@ #1 } #2@{#1} +} + +% Trim a single trailing ^^M off a string. +{\catcode`\^^M=\other \catcode`\Q=3% +\gdef\eatcr #1{\eatcra #1Q^^MQ}% +\gdef\eatcra#1^^MQ{\eatcrb#1Q}% +\gdef\eatcrb#1Q#2Q{#1}% +} + +% Macro bodies are absorbed as an argument in a context where +% all characters are catcode 10, 11 or 12, except \ which is active +% (as in normal texinfo). It is necessary to change the definition of \. + +% It's necessary to have hard CRs when the macro is executed. This is +% done by making ^^M (\endlinechar) catcode 12 when reading the macro +% body, and then making it the \newlinechar in \scanmacro. + +\def\scanctxt{% + \catcode`\"=\other + \catcode`\+=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\@=\other + \catcode`\^=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\~=\other +} + +\def\scanargctxt{% + \scanctxt + \catcode`\\=\other + \catcode`\^^M=\other +} + +\def\macrobodyctxt{% + \scanctxt + \catcode`\{=\other + \catcode`\}=\other + \catcode`\^^M=\other + \usembodybackslash +} + +\def\macroargctxt{% + \scanctxt + \catcode`\\=\other +} + +% \mbodybackslash is the definition of \ in @macro bodies. +% It maps \foo\ => \csname macarg.foo\endcsname => #N +% where N is the macro parameter number. +% We define \csname macarg.\endcsname to be \realbackslash, so +% \\ in macro replacement text gets you a backslash. + +{\catcode`@=0 @catcode`@\=@active + @gdef@usembodybackslash{@let\=@mbodybackslash} + @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} +} +\expandafter\def\csname macarg.\endcsname{\realbackslash} + +\def\macro{\recursivefalse\parsearg\macroxxx} +\def\rmacro{\recursivetrue\parsearg\macroxxx} + +\def\macroxxx#1{% + \getargs{#1}% now \macname is the macname and \argl the arglist + \ifx\argl\empty % no arguments + \paramno=0% + \else + \expandafter\parsemargdef \argl;% + \fi + \if1\csname ismacro.\the\macname\endcsname + \message{Warning: redefining \the\macname}% + \else + \expandafter\ifx\csname \the\macname\endcsname \relax + \else \errmessage{Macro name \the\macname\space already defined}\fi + \global\cslet{macsave.\the\macname}{\the\macname}% + \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% + \addtomacrolist{\the\macname}% + \fi + \begingroup \macrobodyctxt + \ifrecursive \expandafter\parsermacbody + \else \expandafter\parsemacbody + \fi} + +\parseargdef\unmacro{% + \if1\csname ismacro.#1\endcsname + \global\cslet{#1}{macsave.#1}% + \global\expandafter\let \csname ismacro.#1\endcsname=0% + % Remove the macro name from \macrolist: + \begingroup + \expandafter\let\csname#1\endcsname \relax + \let\definedummyword\unmacrodo + \xdef\macrolist{\macrolist}% + \endgroup + \else + \errmessage{Macro #1 not defined}% + \fi +} + +% Called by \do from \dounmacro on each macro. The idea is to omit any +% macro definitions that have been changed to \relax. +% +\def\unmacrodo#1{% + \ifx #1\relax + % remove this + \else + \noexpand\definedummyword \noexpand#1% + \fi +} + +% This makes use of the obscure feature that if the last token of a +% is #, then the preceding argument is delimited by +% an opening brace, and that opening brace is not consumed. +\def\getargs#1{\getargsxxx#1{}} +\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} +\def\getmacname #1 #2\relax{\macname={#1}} +\def\getmacargs#1{\def\argl{#1}} + +% Parse the optional {params} list. Set up \paramno and \paramlist +% so \defmacro knows what to do. Define \macarg.blah for each blah +% in the params list, to be ##N where N is the position in that list. +% That gets used by \mbodybackslash (above). + +% We need to get `macro parameter char #' into several definitions. +% The technique used is stolen from LaTeX: let \hash be something +% unexpandable, insert that wherever you need a #, and then redefine +% it to # just before using the token list produced. +% +% The same technique is used to protect \eatspaces till just before +% the macro is used. + +\def\parsemargdef#1;{\paramno=0\def\paramlist{}% + \let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,} +\def\parsemargdefxxx#1,{% + \if#1;\let\next=\relax + \else \let\next=\parsemargdefxxx + \advance\paramno by 1% + \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname + {\xeatspaces{\hash\the\paramno}}% + \edef\paramlist{\paramlist\hash\the\paramno,}% + \fi\next} + +% These two commands read recursive and nonrecursive macro bodies. +% (They're different since rec and nonrec macros end differently.) + +\long\def\parsemacbody#1@end macro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% +\long\def\parsermacbody#1@end rmacro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% + +% This defines the macro itself. There are six cases: recursive and +% nonrecursive macros of zero, one, and many arguments. +% Much magic with \expandafter here. +% \xdef is used so that macro definitions will survive the file +% they're defined in; @include reads the file inside a group. +\def\defmacro{% + \let\hash=##% convert placeholders to macro parameter chars + \ifrecursive + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\scanmacro{\temp}}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup\noexpand\scanmacro{\temp}}% + \else % many + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{\egroup\noexpand\scanmacro{\temp}}% + \fi + \else + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \else % many + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \expandafter\noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \fi + \fi} + +\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} + +% \braceorline decides whether the next nonwhitespace character is a +% {. If so it reads up to the closing }, if not, it reads the whole +% line. Whatever was read is then fed to the next control sequence +% as an argument (by \parsebrace or \parsearg) +\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} +\def\braceorlinexxx{% + \ifx\nchar\bgroup\else + \expandafter\parsearg + \fi \macnamexxx} + + +% @alias. +% We need some trickery to remove the optional spaces around the equal +% sign. Just make them active and then expand them all to nothing. +\def\alias{\parseargusing\obeyspaces\aliasxxx} +\def\aliasxxx #1{\aliasyyy#1\relax} +\def\aliasyyy #1=#2\relax{% + {% + \expandafter\let\obeyedspace=\empty + \addtomacrolist{#1}% + \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% + }% + \next +} + + +\message{cross references,} + +\newwrite\auxfile + +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% @inforef is relatively simple. +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +% @node's only job in TeX is to define \lastnode, which is used in +% cross-references. The @node line might or might not have commas, and +% might or might not have spaces before the first comma, like: +% @node foo , bar , ... +% We don't want such trailing spaces in the node name. +% +\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} +% +% also remove a trailing comma, in case of something like this: +% @node Help-Cross, , , Cross-refs +\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} +\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}} + +\let\nwnode=\node +\let\lastnode=\empty + +% Write a cross-reference definition for the current node. #1 is the +% type (Ynumbered, Yappendix, Ynothing). +% +\def\donoderef#1{% + \ifx\lastnode\empty\else + \setref{\lastnode}{#1}% + \global\let\lastnode=\empty + \fi +} + +% @anchor{NAME} -- define xref target at arbitrary point. +% +\newcount\savesfregister +% +\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} +\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} +\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} + +% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an +% anchor), which consists of three parts: +% 1) NAME-title - the current sectioning name taken from \thissection, +% or the anchor name. +% 2) NAME-snt - section number and type, passed as the SNT arg, or +% empty for anchors. +% 3) NAME-pg - the page number. +% +% This is called from \donoderef, \anchor, and \dofloat. In the case of +% floats, there is an additional part, which is not written here: +% 4) NAME-lof - the text as it should appear in a @listoffloats. +% +\def\setref#1#2{% + \pdfmkdest{#1}% + \iflinks + {% + \atdummies % preserve commands, but don't expand them + \edef\writexrdef##1##2{% + \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef + ##1}{##2}}% these are parameters of \writexrdef + }% + \toks0 = \expandafter{\thissection}% + \immediate \writexrdef{title}{\the\toks0 }% + \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. + \writexrdef{pg}{\folio}% will be written later, during \shipout + }% + \fi +} + +% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is +% the node name, #2 the name of the Info cross-reference, #3 the printed +% node name, #4 the name of the Info file, #5 the name of the printed +% manual. All but the node name can be omitted. +% +\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} +\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} +\def\ref#1{\xrefX[#1,,,,,,,]} +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \unsepspaces + \def\printedmanual{\ignorespaces #5}% + \def\printedrefname{\ignorespaces #3}% + \setbox1=\hbox{\printedmanual\unskip}% + \setbox0=\hbox{\printedrefname\unskip}% + \ifdim \wd0 = 0pt + % No printed node name was explicitly given. + \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax + % Use the node name inside the square brackets. + \def\printedrefname{\ignorespaces #1}% + \else + % Use the actual chapter/section title appear inside + % the square brackets. Use the real section title if we have it. + \ifdim \wd1 > 0pt + % It is in another manual, so we don't have it. + \def\printedrefname{\ignorespaces #1}% + \else + \ifhavexrefs + % We know the real title if we have the xref values. + \def\printedrefname{\refx{#1-title}{}}% + \else + % Otherwise just copy the Info node name. + \def\printedrefname{\ignorespaces #1}% + \fi% + \fi + \fi + \fi + % + % Make link in pdf output. + \ifpdf + \leavevmode + \getfilename{#4}% + {\turnoffactive + % See comments at \activebackslashdouble. + {\activebackslashdouble \xdef\pdfxrefdest{#1}% + \backslashparens\pdfxrefdest}% + % + \ifnum\filenamelength>0 + \startlink attr{/Border [0 0 0]}% + goto file{\the\filename.pdf} name{\pdfxrefdest}% + \else + \startlink attr{/Border [0 0 0]}% + goto name{\pdfmkpgn{\pdfxrefdest}}% + \fi + }% + \linkcolor + \fi + % + % Float references are printed completely differently: "Figure 1.2" + % instead of "[somenode], p.3". We distinguish them by the + % LABEL-title being set to a magic string. + {% + % Have to otherify everything special to allow the \csname to + % include an _ in the xref name, etc. + \indexnofonts + \turnoffactive + \expandafter\global\expandafter\let\expandafter\Xthisreftitle + \csname XR#1-title\endcsname + }% + \iffloat\Xthisreftitle + % If the user specified the print name (third arg) to the ref, + % print it instead of our usual "Figure 1.2". + \ifdim\wd0 = 0pt + \refx{#1-snt}{}% + \else + \printedrefname + \fi + % + % if the user also gave the printed manual name (fifth arg), append + % "in MANUALNAME". + \ifdim \wd1 > 0pt + \space \putwordin{} \cite{\printedmanual}% + \fi + \else + % node/anchor (non-float) references. + % + % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not + % insert empty discretionaries after hyphens, which means that it will + % not find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, this + % is a loss. Therefore, we give the text of the node name again, so it + % is as if TeX is seeing it for the first time. + \ifdim \wd1 > 0pt + \putwordsection{} ``\printedrefname'' \putwordin{} \cite{\printedmanual}% + \else + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\turnoffactive + % Only output a following space if the -snt ref is nonempty; for + % @unnumbered and @anchor, it won't be. + \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% + \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi + }% + % output the `[mynode]' via a macro so it can be overridden. + \xrefprintnodename\printedrefname + % + % But we always want a comma and a space: + ,\space + % + % output the `page 3'. + \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + \fi + \fi + \endlink +\endgroup} + +% This macro is called from \xrefX for the `[nodename]' part of xref +% output. It's a separate macro only so it can be changed more easily, +% since square brackets don't work well in some documents. Particularly +% one that Bob is working on :). +% +\def\xrefprintnodename#1{[#1]} + +% Things referred to by \setref. +% +\def\Ynothing{} +\def\Yomitfromtoc{} +\def\Ynumbered{% + \ifnum\secno=0 + \putwordChapter@tie \the\chapno + \else \ifnum\subsecno=0 + \putwordSection@tie \the\chapno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno + \else + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} +\def\Yappendix{% + \ifnum\secno=0 + \putwordAppendix@tie @char\the\appendixno{}% + \else \ifnum\subsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno + \else + \putwordSection@tie + @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. +% +\def\refx#1#2{% + {% + \indexnofonts + \otherbackslash + \expandafter\global\expandafter\let\expandafter\thisrefX + \csname XR#1\endcsname + }% + \ifx\thisrefX\relax + % If not defined, say something at least. + \angleleft un\-de\-fined\angleright + \iflinks + \ifhavexrefs + \message{\linenumber Undefined cross reference `#1'.}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \fi + \else + % It's defined, so just use it. + \thisrefX + \fi + #2% Output the suffix in any case. +} + +% This is the macro invoked by entries in the aux file. Usually it's +% just a \def (we prepend XR to the control sequence name to avoid +% collisions). But if this is a float type, we have more work to do. +% +\def\xrdef#1#2{% + \expandafter\gdef\csname XR#1\endcsname{#2}% remember this xref value. + % + % Was that xref control sequence that we just defined for a float? + \expandafter\iffloat\csname XR#1\endcsname + % it was a float, and we have the (safe) float type in \iffloattype. + \expandafter\let\expandafter\floatlist + \csname floatlist\iffloattype\endcsname + % + % Is this the first time we've seen this float type? + \expandafter\ifx\floatlist\relax + \toks0 = {\do}% yes, so just \do + \else + % had it before, so preserve previous elements in list. + \toks0 = \expandafter{\floatlist\do}% + \fi + % + % Remember this xref in the control sequence \floatlistFLOATTYPE, + % for later use in \listoffloats. + \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0{#1}}% + \fi +} + +% Read the last existing aux file, if any. No error if none exists. +% +\def\tryauxfile{% + \openin 1 \jobname.aux + \ifeof 1 \else + \readdatafile{aux}% + \global\havexrefstrue + \fi + \closein 1 +} + +\def\setupdatafile{% + \catcode`\^^@=\other + \catcode`\^^A=\other + \catcode`\^^B=\other + \catcode`\^^C=\other + \catcode`\^^D=\other + \catcode`\^^E=\other + \catcode`\^^F=\other + \catcode`\^^G=\other + \catcode`\^^H=\other + \catcode`\^^K=\other + \catcode`\^^L=\other + \catcode`\^^N=\other + \catcode`\^^P=\other + \catcode`\^^Q=\other + \catcode`\^^R=\other + \catcode`\^^S=\other + \catcode`\^^T=\other + \catcode`\^^U=\other + \catcode`\^^V=\other + \catcode`\^^W=\other + \catcode`\^^X=\other + \catcode`\^^Z=\other + \catcode`\^^[=\other + \catcode`\^^\=\other + \catcode`\^^]=\other + \catcode`\^^^=\other + \catcode`\^^_=\other + % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc. + % in xref tags, i.e., node names. But since ^^e4 notation isn't + % supported in the main text, it doesn't seem desirable. Furthermore, + % that is not enough: for node names that actually contain a ^ + % character, we would end up writing a line like this: 'xrdef {'hat + % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first + % argument, and \hat is not an expandable control sequence. It could + % all be worked out, but why? Either we support ^^ or we don't. + % + % The other change necessary for this was to define \auxhat: + % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter + % and then to call \auxhat in \setq. + % + \catcode`\^=\other + % + % Special characters. Should be turned off anyway, but... + \catcode`\~=\other + \catcode`\[=\other + \catcode`\]=\other + \catcode`\"=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\$=\other + \catcode`\#=\other + \catcode`\&=\other + \catcode`\%=\other + \catcode`+=\other % avoid \+ for paranoia even though we've turned it off + % + % This is to support \ in node names and titles, since the \ + % characters end up in a \csname. It's easier than + % leaving it active and making its active definition an actual \ + % character. What I don't understand is why it works in the *value* + % of the xrdef. Seems like it should be a catcode12 \, and that + % should not typeset properly. But it works, so I'm moving on for + % now. --karl, 15jan04. + \catcode`\\=\other + % + % Make the characters 128-255 be printing characters. + {% + \count1=128 + \def\loop{% + \catcode\count1=\other + \advance\count1 by 1 + \ifnum \count1<256 \loop \fi + }% + }% + % + % @ is our escape character in .aux files, and we need braces. + \catcode`\{=1 + \catcode`\}=2 + \catcode`\@=0 +} + +\def\readdatafile#1{% +\begingroup + \setupdatafile + \input\jobname.#1 +\endgroup} + +\message{insertions,} +% including footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. (Generally, numeric constants should always be followed by a +% space to prevent strange expansion errors.) +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for info output only. +\let\footnotestyle=\comment + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \dofootnote +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +% Oh yes, they do; otherwise, @ifset (and anything else that uses +% \parseargline) fails inside footnotes because the tokens are fixed when +% the footnote is read. --karl, 16nov96. +% +\gdef\dofootnote{% + \insert\footins\bgroup + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \hsize=\pagewidth + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + \smallfonts \rm + % + % Because we use hanging indentation in footnotes, a @noindent appears + % to exdent this text, so make it be a no-op. makeinfo does not use + % hanging indentation so @noindent can still be needed within footnote + % text after an @example or the like (not that this is good style). + \let\noindent = \relax + % + % Hang the footnote text off the number. Use \everypar in case the + % footnote extends for more than one paragraph. + \everypar = {\hang}% + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + \futurelet\next\fo@t +} +}%end \catcode `\@=11 + +% In case a @footnote appears in a vbox, save the footnote text and create +% the real \insert just after the vbox finished. Otherwise, the insertion +% would be lost. +% Similarily, if a @footnote appears inside an alignment, save the footnote +% text to a box and make the \insert when a row of the table is finished. +% And the same can be done for other insert classes. --kasal, 16nov03. + +% Replace the \insert primitive by a cheating macro. +% Deeper inside, just make sure that the saved insertions are not spilled +% out prematurely. +% +\def\startsavinginserts{% + \ifx \insert\ptexinsert + \let\insert\saveinsert + \else + \let\checkinserts\relax + \fi +} + +% This \insert replacement works for both \insert\footins{foo} and +% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. +% +\def\saveinsert#1{% + \edef\next{\noexpand\savetobox \makeSAVEname#1}% + \afterassignment\next + % swallow the left brace + \let\temp = +} +\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} +\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} + +\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} + +\def\placesaveins#1{% + \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname + {\box#1}% +} + +% eat @SAVE -- beware, all of them have catcode \other: +{ + \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) + \gdef\gobblesave @SAVE{} +} + +% initialization: +\def\newsaveins #1{% + \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% + \next +} +\def\newsaveinsX #1{% + \csname newbox\endcsname #1% + \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts + \checksaveins #1}% +} + +% initialize: +\let\checkinserts\empty +\newsaveins\footins +\newsaveins\margin + + +% @image. We use the macros from epsf.tex to support this. +% If epsf.tex is not installed and @image is used, we complain. +% +% Check for and read epsf.tex up front. If we read it only at @image +% time, we might be inside a group, and then its definitions would get +% undone and the next image would fail. +\openin 1 = epsf.tex +\ifeof 1 \else + % Do not bother showing banner with epsf.tex v2.7k (available in + % doc/epsf.tex and on ctan). + \def\epsfannounce{\toks0 = }% + \input epsf.tex +\fi +\closein 1 +% +% We will only complain once about lack of epsf.tex. +\newif\ifwarnednoepsf +\newhelp\noepsfhelp{epsf.tex must be installed for images to + work. It is also included in the Texinfo distribution, or you can get + it from ftp://tug.org/tex/epsf.tex.} +% +\def\image#1{% + \ifx\epsfbox\undefined + \ifwarnednoepsf \else + \errhelp = \noepsfhelp + \errmessage{epsf.tex not found, images will be ignored}% + \global\warnednoepsftrue + \fi + \else + \imagexxx #1,,,,,\finish + \fi +} +% +% Arguments to @image: +% #1 is (mandatory) image filename; we tack on .eps extension. +% #2 is (optional) width, #3 is (optional) height. +% #4 is (ignored optional) html alt text. +% #5 is (ignored optional) extension. +% #6 is just the usual extra ignored arg for parsing this stuff. +\newif\ifimagevmode +\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup + \catcode`\^^M = 5 % in case we're inside an example + \normalturnoffactive % allow _ et al. in names + % If the image is by itself, center it. + \ifvmode + \imagevmodetrue + \nobreak\bigskip + % Usually we'll have text after the image which will insert + % \parskip glue, so insert it here too to equalize the space + % above and below. + \nobreak\vskip\parskip + \nobreak + \line\bgroup + \fi + % + % Output the image. + \ifpdf + \dopdfimage{#1}{#2}{#3}% + \else + % \epsfbox itself resets \epsf?size at each figure. + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi + \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi + \epsfbox{#1.eps}% + \fi + % + \ifimagevmode \egroup \bigbreak \fi % space after the image +\endgroup} + + +% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, +% etc. We don't actually implement floating yet, we always include the +% float "here". But it seemed the best name for the future. +% +\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} + +% There may be a space before second and/or third parameter; delete it. +\def\eatcommaspace#1, {#1,} + +% #1 is the optional FLOATTYPE, the text label for this float, typically +% "Figure", "Table", "Example", etc. Can't contain commas. If omitted, +% this float will not be numbered and cannot be referred to. +% +% #2 is the optional xref label. Also must be present for the float to +% be referable. +% +% #3 is the optional positioning argument; for now, it is ignored. It +% will somehow specify the positions allowed to float to (here, top, bottom). +% +% We keep a separate counter for each FLOATTYPE, which we reset at each +% chapter-level command. +\let\resetallfloatnos=\empty +% +\def\dofloat#1,#2,#3,#4\finish{% + \let\thiscaption=\empty + \let\thisshortcaption=\empty + % + % don't lose footnotes inside @float. + % + % BEWARE: when the floats start float, we have to issue warning whenever an + % insert appears inside a float which could possibly float. --kasal, 26may04 + % + \startsavinginserts + % + % We can't be used inside a paragraph. + \par + % + \vtop\bgroup + \def\floattype{#1}% + \def\floatlabel{#2}% + \def\floatloc{#3}% we do nothing with this yet. + % + \ifx\floattype\empty + \let\safefloattype=\empty + \else + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + \fi + % + % If label is given but no type, we handle that as the empty type. + \ifx\floatlabel\empty \else + % We want each FLOATTYPE to be numbered separately (Figure 1, + % Table 1, Figure 2, ...). (And if no label, no number.) + % + \expandafter\getfloatno\csname\safefloattype floatno\endcsname + \global\advance\floatno by 1 + % + {% + % This magic value for \thissection is output by \setref as the + % XREFLABEL-title value. \xrefX uses it to distinguish float + % labels (which have a completely different output format) from + % node and anchor labels. And \xrdef uses it to construct the + % lists of floats. + % + \edef\thissection{\floatmagic=\safefloattype}% + \setref{\floatlabel}{Yfloat}% + }% + \fi + % + % start with \parskip glue, I guess. + \vskip\parskip + % + % Don't suppress indentation if a float happens to start a section. + \restorefirstparagraphindent +} + +% we have these possibilities: +% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap +% @float Foo,lbl & no caption: Foo 1.1 +% @float Foo & @caption{Cap}: Foo: Cap +% @float Foo & no caption: Foo +% @float ,lbl & Caption{Cap}: 1.1: Cap +% @float ,lbl & no caption: 1.1 +% @float & @caption{Cap}: Cap +% @float & no caption: +% +\def\Efloat{% + \let\floatident = \empty + % + % In all cases, if we have a float type, it comes first. + \ifx\floattype\empty \else \def\floatident{\floattype}\fi + % + % If we have an xref label, the number comes next. + \ifx\floatlabel\empty \else + \ifx\floattype\empty \else % if also had float type, need tie first. + \appendtomacro\floatident{\tie}% + \fi + % the number. + \appendtomacro\floatident{\chaplevelprefix\the\floatno}% + \fi + % + % Start the printed caption with what we've constructed in + % \floatident, but keep it separate; we need \floatident again. + \let\captionline = \floatident + % + \ifx\thiscaption\empty \else + \ifx\floatident\empty \else + \appendtomacro\captionline{: }% had ident, so need a colon between + \fi + % + % caption text. + \appendtomacro\captionline{\scanexp\thiscaption}% + \fi + % + % If we have anything to print, print it, with space before. + % Eventually this needs to become an \insert. + \ifx\captionline\empty \else + \vskip.5\parskip + \captionline + % + % Space below caption. + \vskip\parskip + \fi + % + % If have an xref label, write the list of floats info. Do this + % after the caption, to avoid chance of it being a breakpoint. + \ifx\floatlabel\empty \else + % Write the text that goes in the lof to the aux file as + % \floatlabel-lof. Besides \floatident, we include the short + % caption if specified, else the full caption if specified, else nothing. + {% + \atdummies + % + % since we read the caption text in the macro world, where ^^M + % is turned into a normal character, we have to scan it back, so + % we don't write the literal three characters "^^M" into the aux file. + \scanexp{% + \xdef\noexpand\gtemp{% + \ifx\thisshortcaption\empty + \thiscaption + \else + \thisshortcaption + \fi + }% + }% + \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident + \ifx\gtemp\empty \else : \gtemp \fi}}% + }% + \fi + \egroup % end of \vtop + % + % place the captured inserts + % + % BEWARE: when the floats start floating, we have to issue warning + % whenever an insert appears inside a float which could possibly + % float. --kasal, 26may04 + % + \checkinserts +} + +% Append the tokens #2 to the definition of macro #1, not expanding either. +% +\def\appendtomacro#1#2{% + \expandafter\def\expandafter#1\expandafter{#1#2}% +} + +% @caption, @shortcaption +% +\def\caption{\docaption\thiscaption} +\def\shortcaption{\docaption\thisshortcaption} +\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} +\def\defcaption#1#2{\egroup \def#1{#2}} + +% The parameter is the control sequence identifying the counter we are +% going to use. Create it if it doesn't exist and assign it to \floatno. +\def\getfloatno#1{% + \ifx#1\relax + % Haven't seen this figure type before. + \csname newcount\endcsname #1% + % + % Remember to reset this floatno at the next chap. + \expandafter\gdef\expandafter\resetallfloatnos + \expandafter{\resetallfloatnos #1=0 }% + \fi + \let\floatno#1% +} + +% \setref calls this to get the XREFLABEL-snt value. We want an @xref +% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we +% first read the @float command. +% +\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% + +% Magic string used for the XREFLABEL-title value, so \xrefX can +% distinguish floats from other xref types. +\def\floatmagic{!!float!!} + +% #1 is the control sequence we are passed; we expand into a conditional +% which is true if #1 represents a float ref. That is, the magic +% \thissection value which we \setref above. +% +\def\iffloat#1{\expandafter\doiffloat#1==\finish} +% +% #1 is (maybe) the \floatmagic string. If so, #2 will be the +% (safe) float type for this float. We set \iffloattype to #2. +% +\def\doiffloat#1=#2=#3\finish{% + \def\temp{#1}% + \def\iffloattype{#2}% + \ifx\temp\floatmagic +} + +% @listoffloats FLOATTYPE - print a list of floats like a table of contents. +% +\parseargdef\listoffloats{% + \def\floattype{#1}% floattype + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + % + % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. + \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax + \ifhavexrefs + % if the user said @listoffloats foo but never @float foo. + \message{\linenumber No `\safefloattype' floats to list.}% + \fi + \else + \begingroup + \leftskip=\tocindent % indent these entries like a toc + \let\do=\listoffloatsdo + \csname floatlist\safefloattype\endcsname + \endgroup + \fi +} + +% This is called on each entry in a list of floats. We're passed the +% xref label, in the form LABEL-title, which is how we save it in the +% aux file. We strip off the -title and look up \XRLABEL-lof, which +% has the text we're supposed to typeset here. +% +% Figures without xref labels will not be included in the list (since +% they won't appear in the aux file). +% +\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} +\def\listoffloatsdoentry#1-title\finish{{% + % Can't fully expand XR#1-lof because it can contain anything. Just + % pass the control sequence. On the other hand, XR#1-pg is just the + % page number, and we want to fully expand that so we can get a link + % in pdf output. + \toksA = \expandafter{\csname XR#1-lof\endcsname}% + % + % use the same \entry macro we use to generate the TOC and index. + \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% + \writeentry +}} + +\message{localization,} +% and i18n. + +% @documentlanguage is usually given very early, just after +% @setfilename. If done too late, it may not override everything +% properly. Single argument is the language abbreviation. +% It would be nice if we could set up a hyphenation file here. +% +\parseargdef\documentlanguage{% + \tex % read txi-??.tex file in plain TeX. + % Read the file if it exists. + \openin 1 txi-#1.tex + \ifeof 1 + \errhelp = \nolanghelp + \errmessage{Cannot read language file txi-#1.tex}% + \else + \input txi-#1.tex + \fi + \closein 1 + \endgroup +} +\newhelp\nolanghelp{The given language definition file cannot be found or +is empty. Maybe you need to install it? In the current directory +should work if nowhere else does.} + + +% @documentencoding should change something in TeX eventually, most +% likely, but for now just recognize it. +\let\documentencoding = \comment + + +% Page size parameters. +% +\newdimen\defaultparindent \defaultparindent = 15pt + +\chapheadingskip = 15pt plus 4pt minus 2pt +\secheadingskip = 12pt plus 3pt minus 2pt +\subsecheadingskip = 9pt plus 2pt minus 2pt + +% Prevent underfull vbox error messages. +\vbadness = 10000 + +% Don't be so finicky about underfull hboxes, either. +\hbadness = 2000 + +% Following George Bush, just get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. We call this whenever the paper size is set. +% +\def\setemergencystretch{% + \ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% + \else + \emergencystretch = .15\hsize + \fi +} + +% Parameters in order: 1) textheight; 2) textwidth; +% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; +% 7) physical page height; 8) physical page width. +% +% We also call \setleading{\textleading}, so the caller should define +% \textleading. The caller should also set \parskip. +% +\def\internalpagesizes#1#2#3#4#5#6#7#8{% + \voffset = #3\relax + \topskip = #6\relax + \splittopskip = \topskip + % + \vsize = #1\relax + \advance\vsize by \topskip + \outervsize = \vsize + \advance\outervsize by 2\topandbottommargin + \pageheight = \vsize + % + \hsize = #2\relax + \outerhsize = \hsize + \advance\outerhsize by 0.5in + \pagewidth = \hsize + % + \normaloffset = #4\relax + \bindingoffset = #5\relax + % + \ifpdf + \pdfpageheight #7\relax + \pdfpagewidth #8\relax + \fi + % + \setleading{\textleading} + % + \parindent = \defaultparindent + \setemergencystretch +} + +% @letterpaper (the default). +\def\letterpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % If page is nothing but text, make it come out even. + \internalpagesizes{46\baselineskip}{6in}% + {\voffset}{.25in}% + {\bindingoffset}{36pt}% + {11in}{8.5in}% +}} + +% Use @smallbook to reset parameters for 7x9.25 trim size. +\def\smallbook{{\globaldefs = 1 + \parskip = 2pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.5in}{5in}% + {\voffset}{.25in}% + {\bindingoffset}{16pt}% + {9.25in}{7in}% + % + \lispnarrowing = 0.3in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .5cm +}} + +% Use @smallerbook to reset parameters for 6x9 trim size. +% (Just testing, parameters still in flux.) +\def\smallerbook{{\globaldefs = 1 + \parskip = 1.5pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.4in}{4.8in}% + {-.2in}{-.4in}% + {0pt}{14pt}% + {9in}{6in}% + % + \lispnarrowing = 0.25in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .4cm +}} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % Double-side printing via postscript on Laserjet 4050 + % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. + % To change the settings for a different printer or situation, adjust + % \normaloffset until the front-side and back-side texts align. Then + % do the same for \bindingoffset. You can set these for testing in + % your texinfo source file like this: + % @tex + % \global\normaloffset = -6mm + % \global\bindingoffset = 10mm + % @end tex + \internalpagesizes{51\baselineskip}{160mm} + {\voffset}{\hoffset}% + {\bindingoffset}{44pt}% + {297mm}{210mm}% + % + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = 5mm +}} + +% Use @afivepaper to print on European A5 paper. +% From romildo@urano.iceb.ufop.br, 2 July 2000. +% He also recommends making @example and @lisp be small. +\def\afivepaper{{\globaldefs = 1 + \parskip = 2pt plus 1pt minus 0.1pt + \textleading = 12.5pt + % + \internalpagesizes{160mm}{120mm}% + {\voffset}{\hoffset}% + {\bindingoffset}{8pt}% + {210mm}{148mm}% + % + \lispnarrowing = 0.2in + \tolerance = 800 + \hfuzz = 1.2pt + \contentsrightmargin = 0pt + \defbodyindent = 2mm + \tableindent = 12mm +}} + +% A specific text layout, 24x15cm overall, intended for A4 paper. +\def\afourlatex{{\globaldefs = 1 + \afourpaper + \internalpagesizes{237mm}{150mm}% + {\voffset}{4.6mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + % + % Must explicitly reset to 0 because we call \afourpaper. + \globaldefs = 0 +}} + +% Use @afourwide to print on A4 paper in landscape format. +\def\afourwide{{\globaldefs = 1 + \afourpaper + \internalpagesizes{241mm}{165mm}% + {\voffset}{-2.95mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + \globaldefs = 0 +}} + +% @pagesizes TEXTHEIGHT[,TEXTWIDTH] +% Perhaps we should allow setting the margins, \topskip, \parskip, +% and/or leading, also. Or perhaps we should compute them somehow. +% +\parseargdef\pagesizes{\pagesizesyyy #1,,\finish} +\def\pagesizesyyy#1,#2,#3\finish{{% + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi + \globaldefs = 1 + % + \parskip = 3pt plus 2pt minus 1pt + \setleading{\textleading}% + % + \dimen0 = #1 + \advance\dimen0 by \voffset + % + \dimen2 = \hsize + \advance\dimen2 by \normaloffset + % + \internalpagesizes{#1}{\hsize}% + {\voffset}{\normaloffset}% + {\bindingoffset}{44pt}% + {\dimen0}{\dimen2}% +}} + +% Set default to letter. +% +\letterpaper + + +\message{and turning on texinfo input format.} + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other +\catcode`\~=\other +\catcode`\^=\other +\catcode`\_=\other +\catcode`\|=\other +\catcode`\<=\other +\catcode`\>=\other +\catcode`\+=\other +\catcode`\$=\other +\def\normaldoublequote{"} +\def\normaltilde{~} +\def\normalcaret{^} +\def\normalunderscore{_} +\def\normalverticalbar{|} +\def\normalless{<} +\def\normalgreater{>} +\def\normalplus{+} +\def\normaldollar{$}%$ font-lock fix + +% This macro is used to make a character print one way in \tt +% (where it can probably be output as-is), and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} + +% Same as above, but check for italic font. Actually this also catches +% non-italic slanted fonts since it is impossible to distinguish them from +% italic fonts. But since this is only used by $ and it uses \sl anyway +% this is not a problem. +\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} + +% Turn off all special characters except @ +% (and those which the user can use as if they were ordinary). +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. + +\catcode`\"=\active +\def\activedoublequote{{\tt\char34}} +\let"=\activedoublequote +\catcode`\~=\active +\def~{{\tt\char126}} +\chardef\hat=`\^ +\catcode`\^=\active +\def^{{\tt \hat}} + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +\let\realunder=_ +% Subroutine for the previous macro. +\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } + +\catcode`\|=\active +\def|{{\tt\char124}} +\chardef \less=`\< +\catcode`\<=\active +\def<{{\tt \less}} +\chardef \gtr=`\> +\catcode`\>=\active +\def>{{\tt \gtr}} +\catcode`\+=\active +\def+{{\tt \char 43}} +\catcode`\$=\active +\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix + +% If a .fmt file is being used, characters that might appear in a file +% name cannot be active until we have parsed the command line. +% So turn them off again, and have \everyjob (or @setfilename) turn them on. +% \otherifyactive is called near the end of this file. +\def\otherifyactive{\catcode`+=\other \catcode`\_=\other} + +% Used sometimes to turn off (effectively) the active characters even after +% parsing them. +\def\turnoffactive{% + \normalturnoffactive + \otherbackslash +} + +\catcode`\@=0 + +% \backslashcurfont outputs one backslash character in current font, +% as in \char`\\. +\global\chardef\backslashcurfont=`\\ +\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work + +% \realbackslash is an actual character `\' with catcode other, and +% \doublebackslash is two of them (for the pdf outlines). +{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}} + +% In texinfo, backslash is an active character; it prints the backslash +% in fixed width font. +\catcode`\\=\active +@def@normalbackslash{{@tt@backslashcurfont}} +% On startup, @fixbackslash assigns: +% @let \ = @normalbackslash + +% \rawbackslash defines an active \ to do \backslashcurfont. +% \otherbackslash defines an active \ to be a literal `\' character with +% catcode other. +@gdef@rawbackslash{@let\=@backslashcurfont} +@gdef@otherbackslash{@let\=@realbackslash} + +% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of +% the literal character `\'. +% +@def@normalturnoffactive{% + @let\=@normalbackslash + @let"=@normaldoublequote + @let~=@normaltilde + @let^=@normalcaret + @let_=@normalunderscore + @let|=@normalverticalbar + @let<=@normalless + @let>=@normalgreater + @let+=@normalplus + @let$=@normaldollar %$ font-lock fix + @unsepspaces +} + +% Make _ and + \other characters, temporarily. +% This is canceled by @fixbackslash. +@otherifyactive + +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% +@gdef@eatinput input texinfo{@fixbackslash} +@global@let\ = @eatinput + +% On the other hand, perhaps the file did not have a `\input texinfo'. Then +% the first `\' in the file would cause an error. This macro tries to fix +% that, assuming it is called before the first `\' could plausibly occur. +% Also turn back on active characters that might appear in the input +% file name, in case not using a pre-dumped format. +% +@gdef@fixbackslash{% + @ifx\@eatinput @let\ = @normalbackslash @fi + @catcode`+=@active + @catcode`@_=@active +} + +% Say @foo, not \foo, in error messages. +@escapechar = `@@ + +% These look ok in all fonts, so just make them not special. +@catcode`@& = @other +@catcode`@# = @other +@catcode`@% = @other + + +@c Local variables: +@c eval: (add-hook 'write-file-hooks 'time-stamp) +@c page-delimiter: "^\\\\message" +@c time-stamp-start: "def\\\\texinfoversion{" +@c time-stamp-format: "%:y-%02m-%02d.%02H" +@c time-stamp-end: "}" +@c End: + +@c vim:sw=2: + +@ignore + arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115 +@end ignore diff --git a/src/bin/m4/doc/version.texi b/src/bin/m4/doc/version.texi new file mode 100644 index 0000000000..c9d4e2fb98 --- /dev/null +++ b/src/bin/m4/doc/version.texi @@ -0,0 +1,4 @@ +@set UPDATED 20 November 2006 +@set UPDATED-MONTH November 2006 +@set EDITION 1.4.8 +@set VERSION 1.4.8 diff --git a/src/bin/m4/lib/Jamfile b/src/bin/m4/lib/Jamfile new file mode 100644 index 0000000000..ae3f5a3401 --- /dev/null +++ b/src/bin/m4/lib/Jamfile @@ -0,0 +1,34 @@ +SubDir HAIKU_TOP src bin m4 lib ; + +SubDirCcFlags -DHAVE_CONFIG_H ; + +SubDirSysHdrs $(SUBDIR) ; +SubDirSysHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ; + +StaticLibrary libm4.a : + __fpending.c + allocsa.c + clean-temp.c + cloexec.c + close-stream.c + closeout.c + dup-safer.c + error.c + exitfail.c + fatal-signal.c + fd-safer.c + fopen-safer.c + gl_avltree_oset.c + gl_linkedhash_list.c + mkdtemp.c + mkstemp-safer.c + quotearg.c + strstr.c + tempname.c + tmpdir.c + verror.c + xalloc-die.c + xasprintf.c + xmalloc.c + xvasprintf.c +; diff --git a/src/bin/m4/lib/Makefile.am b/src/bin/m4/lib/Makefile.am new file mode 100644 index 0000000000..8a0fe98fe6 --- /dev/null +++ b/src/bin/m4/lib/Makefile.am @@ -0,0 +1,558 @@ +## DO NOT EDIT! GENERATED AUTOMATICALLY! +## Process this file with automake to produce Makefile.in. +# Copyright (C) 2004-2006 Free Software Foundation, Inc. +# +# This file is free software, distributed under the terms of the GNU +# General Public License. As a special exception to the GNU General +# Public License, this file may be distributed as part of a program +# that contains a configuration script generated by Autoconf, under +# the same distribution terms as the rest of that program. +# +# Generated by gnulib-tool. +# Reproduce by: gnulib-tool --import --dir=. --lib=libm4 --source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=. --no-libtool --macro-prefix=M4 avltree-oset binary-io clean-temp cloexec close-stream closeout config-h error fdl fopen-safer free gendocs getopt gnupload mkstemp obstack regex stdbool stdlib-safer strstr strtol unlocked-io verror xalloc xvasprintf + +AUTOMAKE_OPTIONS = 1.5 gnits + +noinst_LIBRARIES = libm4.a + +libm4_a_SOURCES = +libm4_a_LIBADD = $(M4_LIBOBJS) +libm4_a_DEPENDENCIES = $(M4_LIBOBJS) +EXTRA_libm4_a_SOURCES = +noinst_HEADERS = +EXTRA_DIST = +BUILT_SOURCES = +SUFFIXES = +MOSTLYCLEANFILES = core *.stackdump +MOSTLYCLEANDIRS = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = + +AM_CPPFLAGS = + +## begin gnulib module alloca + + +EXTRA_DIST += alloca.c + +EXTRA_libm4_a_SOURCES += alloca.c + +libm4_a_LIBADD += @ALLOCA@ +libm4_a_DEPENDENCIES += @ALLOCA@ +## end gnulib module alloca + +## begin gnulib module alloca-opt + +BUILT_SOURCES += $(ALLOCA_H) + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +alloca.h: alloca_.h + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + cat $(srcdir)/alloca_.h; \ + } > $@-t + mv -f $@-t $@ +MOSTLYCLEANFILES += alloca.h alloca.h-t + +EXTRA_DIST += alloca_.h + +## end gnulib module alloca-opt + +## begin gnulib module allocsa + +libm4_a_SOURCES += allocsa.h allocsa.c + +EXTRA_DIST += allocsa.valgrind + +## end gnulib module allocsa + +## begin gnulib module avltree-oset + +libm4_a_SOURCES += gl_avltree_oset.h gl_avltree_oset.c gl_anytree_oset.h + +## end gnulib module avltree-oset + +## begin gnulib module binary-io + +libm4_a_SOURCES += binary-io.h + +## end gnulib module binary-io + +## begin gnulib module clean-temp + +libm4_a_SOURCES += clean-temp.h clean-temp.c + +## end gnulib module clean-temp + +## begin gnulib module cloexec + + +EXTRA_DIST += cloexec.c cloexec.h + +EXTRA_libm4_a_SOURCES += cloexec.c + +## end gnulib module cloexec + +## begin gnulib module close-stream + + +EXTRA_DIST += close-stream.c close-stream.h + +EXTRA_libm4_a_SOURCES += close-stream.c + +## end gnulib module close-stream + +## begin gnulib module closeout + + +EXTRA_DIST += closeout.c closeout.h + +EXTRA_libm4_a_SOURCES += closeout.c + +## end gnulib module closeout + +## begin gnulib module error + + +EXTRA_DIST += error.c error.h + +EXTRA_libm4_a_SOURCES += error.c + +## end gnulib module error + +## begin gnulib module exit + +libm4_a_SOURCES += exit.h + +## end gnulib module exit + +## begin gnulib module exitfail + + +EXTRA_DIST += exitfail.c exitfail.h + +EXTRA_libm4_a_SOURCES += exitfail.c + +## end gnulib module exitfail + +## begin gnulib module fatal-signal + +libm4_a_SOURCES += fatal-signal.h fatal-signal.c + +## end gnulib module fatal-signal + +## begin gnulib module fopen-safer + + +EXTRA_DIST += fopen-safer.c stdio--.h stdio-safer.h + +EXTRA_libm4_a_SOURCES += fopen-safer.c + +## end gnulib module fopen-safer + +## begin gnulib module fpending + + +EXTRA_DIST += __fpending.c __fpending.h + +EXTRA_libm4_a_SOURCES += __fpending.c + +## end gnulib module fpending + +## begin gnulib module free + + +EXTRA_DIST += free.c + +EXTRA_libm4_a_SOURCES += free.c + +## end gnulib module free + +## begin gnulib module getopt + +BUILT_SOURCES += $(GETOPT_H) + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +getopt.h: getopt_.h + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + cat $(srcdir)/getopt_.h; \ + } > $@-t + mv -f $@-t $@ +MOSTLYCLEANFILES += getopt.h getopt.h-t + +EXTRA_DIST += getopt.c getopt1.c getopt_.h getopt_int.h + +EXTRA_libm4_a_SOURCES += getopt.c getopt1.c + +## end gnulib module getopt + +## begin gnulib module gettext-h + +libm4_a_SOURCES += gettext.h + +## end gnulib module gettext-h + +## begin gnulib module linkedhash-list + +libm4_a_SOURCES += gl_linkedhash_list.h gl_linkedhash_list.c gl_anyhash_list1.h gl_anyhash_list2.h gl_anylinked_list1.h gl_anylinked_list2.h + +## end gnulib module linkedhash-list + +## begin gnulib module list + +libm4_a_SOURCES += gl_list.h gl_list.c + +## end gnulib module list + +## begin gnulib module malloc + + +EXTRA_DIST += malloc.c + +EXTRA_libm4_a_SOURCES += malloc.c + +## end gnulib module malloc + +## begin gnulib module mbchar + + +EXTRA_DIST += mbchar.c mbchar.h + +EXTRA_libm4_a_SOURCES += mbchar.c + +## end gnulib module mbchar + +## begin gnulib module mbuiter + +libm4_a_SOURCES += mbuiter.h + +## end gnulib module mbuiter + +## begin gnulib module memchr + + +EXTRA_DIST += memchr.c + +EXTRA_libm4_a_SOURCES += memchr.c + +## end gnulib module memchr + +## begin gnulib module mkdtemp + + +EXTRA_DIST += mkdtemp.c mkdtemp.h + +EXTRA_libm4_a_SOURCES += mkdtemp.c + +## end gnulib module mkdtemp + +## begin gnulib module mkstemp + + +EXTRA_DIST += mkstemp.c mkstemp.h + +EXTRA_libm4_a_SOURCES += mkstemp.c + +## end gnulib module mkstemp + +## begin gnulib module obstack + + +EXTRA_DIST += obstack.c obstack.h + +EXTRA_libm4_a_SOURCES += obstack.c + +## end gnulib module obstack + +## begin gnulib module oset + +libm4_a_SOURCES += gl_oset.h gl_oset.c + +## end gnulib module oset + +## begin gnulib module pathmax + + +EXTRA_DIST += pathmax.h + +## end gnulib module pathmax + +## begin gnulib module quotearg + + +EXTRA_DIST += quotearg.c quotearg.h + +EXTRA_libm4_a_SOURCES += quotearg.c + +## end gnulib module quotearg + +## begin gnulib module regex + + +EXTRA_DIST += regcomp.c regex.c regex.h regex_internal.c regex_internal.h regexec.c + +EXTRA_libm4_a_SOURCES += regcomp.c regex.c regex_internal.c regexec.c + +## end gnulib module regex + +## begin gnulib module sigprocmask + + +EXTRA_DIST += sigprocmask.c sigprocmask.h + +EXTRA_libm4_a_SOURCES += sigprocmask.c + +## end gnulib module sigprocmask + +## begin gnulib module size_max + +libm4_a_SOURCES += size_max.h + +## end gnulib module size_max + +## begin gnulib module stdbool + +BUILT_SOURCES += $(STDBOOL_H) + +# We need the following in order to create when the system +# doesn't have one that works. +stdbool.h: stdbool_.h + rm -f $@-t $@ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's/@''HAVE__BOOL''@/$(HAVE__BOOL)/g' < $(srcdir)/stdbool_.h; \ + } > $@-t + mv $@-t $@ +MOSTLYCLEANFILES += stdbool.h stdbool.h-t + +EXTRA_DIST += stdbool_.h + +## end gnulib module stdbool + +## begin gnulib module stdint + +BUILT_SOURCES += $(STDINT_H) + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +stdint.h: stdint_.h + rm -f $@-t $@ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's/@''HAVE_WCHAR_H''@/$(HAVE_WCHAR_H)/g' \ + -e 's/@''HAVE_STDINT_H''@/$(HAVE_STDINT_H)/g' \ + -e 's|@''ABSOLUTE_STDINT_H''@|$(ABSOLUTE_STDINT_H)|g' \ + -e 's/@''HAVE_SYS_TYPES_H''@/$(HAVE_SYS_TYPES_H)/g' \ + -e 's/@''HAVE_INTTYPES_H''@/$(HAVE_INTTYPES_H)/g' \ + -e 's/@''HAVE_SYS_INTTYPES_H''@/$(HAVE_SYS_INTTYPES_H)/g' \ + -e 's/@''HAVE_SYS_BITYPES_H''@/$(HAVE_SYS_BITYPES_H)/g' \ + -e 's/@''HAVE_LONG_LONG_INT''@/$(HAVE_LONG_LONG_INT)/g' \ + -e 's/@''HAVE_UNSIGNED_LONG_LONG_INT''@/$(HAVE_UNSIGNED_LONG_LONG_INT)/g' \ + -e 's/@''BITSIZEOF_PTRDIFF_T''@/$(BITSIZEOF_PTRDIFF_T)/g' \ + -e 's/@''PTRDIFF_T_SUFFIX''@/$(PTRDIFF_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_SIG_ATOMIC_T''@/$(BITSIZEOF_SIG_ATOMIC_T)/g' \ + -e 's/@''HAVE_SIGNED_SIG_ATOMIC_T''@/$(HAVE_SIGNED_SIG_ATOMIC_T)/g' \ + -e 's/@''SIG_ATOMIC_T_SUFFIX''@/$(SIG_ATOMIC_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_SIZE_T''@/$(BITSIZEOF_SIZE_T)/g' \ + -e 's/@''SIZE_T_SUFFIX''@/$(SIZE_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_WCHAR_T''@/$(BITSIZEOF_WCHAR_T)/g' \ + -e 's/@''HAVE_SIGNED_WCHAR_T''@/$(HAVE_SIGNED_WCHAR_T)/g' \ + -e 's/@''WCHAR_T_SUFFIX''@/$(WCHAR_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_WINT_T''@/$(BITSIZEOF_WINT_T)/g' \ + -e 's/@''HAVE_SIGNED_WINT_T''@/$(HAVE_SIGNED_WINT_T)/g' \ + -e 's/@''WINT_T_SUFFIX''@/$(WINT_T_SUFFIX)/g' \ + < $(srcdir)/stdint_.h; \ + } > $@-t + mv $@-t $@ +MOSTLYCLEANFILES += stdint.h stdint.h-t + +EXTRA_DIST += stdint_.h + +## end gnulib module stdint + +## begin gnulib module stdlib-safer + + +EXTRA_DIST += mkstemp-safer.c stdlib--.h stdlib-safer.h + +EXTRA_libm4_a_SOURCES += mkstemp-safer.c + +## end gnulib module stdlib-safer + +## begin gnulib module strcase + + +EXTRA_DIST += strcase.h strcasecmp.c strncasecmp.c + +EXTRA_libm4_a_SOURCES += strcasecmp.c strncasecmp.c + +## end gnulib module strcase + +## begin gnulib module strnlen1 + +libm4_a_SOURCES += strnlen1.h strnlen1.c + +## end gnulib module strnlen1 + +## begin gnulib module strstr + + +EXTRA_DIST += strstr.c strstr.h + +EXTRA_libm4_a_SOURCES += strstr.c + +## end gnulib module strstr + +## begin gnulib module strtol + + +EXTRA_DIST += strtol.c + +EXTRA_libm4_a_SOURCES += strtol.c + +## end gnulib module strtol + +## begin gnulib module sys_stat + +BUILT_SOURCES += $(SYS_STAT_H) + +# We need the following in order to create when the system +# has one that is incomplete. +sys/stat.h: stat_.h + test -d sys || mkdir sys + rm -f $@-t $@ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's|@''ABSOLUTE_SYS_STAT_H''@|$(ABSOLUTE_SYS_STAT_H)|g' \ + < $(srcdir)/stat_.h; \ + } > $@-t + mv $@-t $@ +MOSTLYCLEANFILES += sys/stat.h sys/stat.h-t +MOSTLYCLEANDIRS += sys + +EXTRA_DIST += stat_.h + +## end gnulib module sys_stat + +## begin gnulib module tempname + + +EXTRA_DIST += tempname.c tempname.h + +EXTRA_libm4_a_SOURCES += tempname.c + +## end gnulib module tempname + +## begin gnulib module tmpdir + +libm4_a_SOURCES += tmpdir.h tmpdir.c + +## end gnulib module tmpdir + +## begin gnulib module unistd + +BUILT_SOURCES += $(UNISTD_H) + +# We need the following in order to create an empty placeholder for +# when the system doesn't have one. +unistd.h: + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + echo '/* Empty placeholder for $@. */'; \ + } > $@ +MOSTLYCLEANFILES += unistd.h + +## end gnulib module unistd + +## begin gnulib module unistd-safer + + +EXTRA_DIST += dup-safer.c fd-safer.c pipe-safer.c unistd--.h unistd-safer.h + +EXTRA_libm4_a_SOURCES += dup-safer.c fd-safer.c pipe-safer.c + +## end gnulib module unistd-safer + +## begin gnulib module unlocked-io + + +EXTRA_DIST += unlocked-io.h + +## end gnulib module unlocked-io + +## begin gnulib module vasnprintf + + +EXTRA_DIST += asnprintf.c printf-args.c printf-args.h printf-parse.c printf-parse.h vasnprintf.c vasnprintf.h + +EXTRA_libm4_a_SOURCES += asnprintf.c printf-args.c printf-parse.c vasnprintf.c + +## end gnulib module vasnprintf + +## begin gnulib module vasprintf + + +EXTRA_DIST += asprintf.c vasprintf.c vasprintf.h + +EXTRA_libm4_a_SOURCES += asprintf.c vasprintf.c + +## end gnulib module vasprintf + +## begin gnulib module verify + +libm4_a_SOURCES += verify.h + +## end gnulib module verify + +## begin gnulib module verror + +libm4_a_SOURCES += verror.h verror.c + +## end gnulib module verror + +## begin gnulib module wcwidth + +libm4_a_SOURCES += wcwidth.h + +## end gnulib module wcwidth + +## begin gnulib module xalloc + + +EXTRA_DIST += xalloc.h xmalloc.c + +EXTRA_libm4_a_SOURCES += xmalloc.c + +## end gnulib module xalloc + +## begin gnulib module xalloc-die + +libm4_a_SOURCES += xalloc-die.c + +## end gnulib module xalloc-die + +## begin gnulib module xallocsa + +libm4_a_SOURCES += xallocsa.h xallocsa.c + +## end gnulib module xallocsa + +## begin gnulib module xsize + +libm4_a_SOURCES += xsize.h + +## end gnulib module xsize + +## begin gnulib module xvasprintf + +libm4_a_SOURCES += xvasprintf.h xvasprintf.c xasprintf.c + +EXTRA_DIST += xalloc.h + +## end gnulib module xvasprintf + + +mostlyclean-local: mostlyclean-generic + @for dir in '' $(MOSTLYCLEANDIRS); do \ + if test -n "$$dir" && test -d $$dir; then \ + echo "rmdir $$dir"; rmdir $$dir; \ + fi; \ + done diff --git a/src/bin/m4/lib/Makefile.in b/src/bin/m4/lib/Makefile.in new file mode 100644 index 0000000000..08a7af9d93 --- /dev/null +++ b/src/bin/m4/lib/Makefile.in @@ -0,0 +1,692 @@ +# Makefile.in generated by automake 1.10 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Copyright (C) 2004-2006 Free Software Foundation, Inc. +# +# This file is free software, distributed under the terms of the GNU +# General Public License. As a special exception to the GNU General +# Public License, this file may be distributed as part of a program +# that contains a configuration script generated by Autoconf, under +# the same distribution terms as the rest of that program. +# +# Generated by gnulib-tool. +# Reproduce by: gnulib-tool --import --dir=. --lib=libm4 --source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=. --no-libtool --macro-prefix=M4 avltree-oset binary-io clean-temp cloexec close-stream closeout config-h error fdl fopen-safer free gendocs getopt gnupload mkstemp obstack regex stdbool stdlib-safer strstr strtol unlocked-io verror xalloc xvasprintf + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = lib +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/config.hin alloca.c +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \ + $(top_srcdir)/m4/alloca.m4 $(top_srcdir)/m4/allocsa.m4 \ + $(top_srcdir)/m4/cloexec.m4 $(top_srcdir)/m4/close-stream.m4 \ + $(top_srcdir)/m4/closeout.m4 $(top_srcdir)/m4/codeset.m4 \ + $(top_srcdir)/m4/config-h.m4 $(top_srcdir)/m4/eealloc.m4 \ + $(top_srcdir)/m4/eoverflow.m4 $(top_srcdir)/m4/error.m4 \ + $(top_srcdir)/m4/exitfail.m4 $(top_srcdir)/m4/extensions.m4 \ + $(top_srcdir)/m4/fatal-signal.m4 $(top_srcdir)/m4/fpending.m4 \ + $(top_srcdir)/m4/free.m4 $(top_srcdir)/m4/getopt.m4 \ + $(top_srcdir)/m4/gl_list.m4 $(top_srcdir)/m4/gnulib-comp.m4 \ + $(top_srcdir)/m4/inline.m4 $(top_srcdir)/m4/intmax_t.m4 \ + $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/longdouble.m4 \ + $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/mbchar.m4 \ + $(top_srcdir)/m4/mbiter.m4 $(top_srcdir)/m4/mbrtowc.m4 \ + $(top_srcdir)/m4/mbstate_t.m4 $(top_srcdir)/m4/memchr.m4 \ + $(top_srcdir)/m4/mkdtemp.m4 $(top_srcdir)/m4/mkstemp.m4 \ + $(top_srcdir)/m4/pathmax.m4 $(top_srcdir)/m4/quotearg.m4 \ + $(top_srcdir)/m4/regex.m4 $(top_srcdir)/m4/sig_atomic_t.m4 \ + $(top_srcdir)/m4/signalblocking.m4 \ + $(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/ssize_t.m4 \ + $(top_srcdir)/m4/stdarg.m4 $(top_srcdir)/m4/stdbool.m4 \ + $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/m4/stdint_h.m4 \ + $(top_srcdir)/m4/stdio-safer.m4 \ + $(top_srcdir)/m4/stdlib-safer.m4 $(top_srcdir)/m4/strcase.m4 \ + $(top_srcdir)/m4/strstr.m4 $(top_srcdir)/m4/strtol.m4 \ + $(top_srcdir)/m4/sys_stat_h.m4 $(top_srcdir)/m4/tempname.m4 \ + $(top_srcdir)/m4/tmpdir.m4 $(top_srcdir)/m4/ulonglong.m4 \ + $(top_srcdir)/m4/unistd-safer.m4 $(top_srcdir)/m4/unistd_h.m4 \ + $(top_srcdir)/m4/unlocked-io.m4 $(top_srcdir)/m4/vasnprintf.m4 \ + $(top_srcdir)/m4/vasprintf.m4 $(top_srcdir)/m4/wchar_t.m4 \ + $(top_srcdir)/m4/wcwidth.m4 $(top_srcdir)/m4/wint_t.m4 \ + $(top_srcdir)/m4/xalloc.m4 $(top_srcdir)/m4/xsize.m4 \ + $(top_srcdir)/m4/xvasprintf.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libm4_a_AR = $(AR) $(ARFLAGS) +am__DEPENDENCIES_1 = +am_libm4_a_OBJECTS = allocsa.$(OBJEXT) gl_avltree_oset.$(OBJEXT) \ + clean-temp.$(OBJEXT) fatal-signal.$(OBJEXT) \ + gl_linkedhash_list.$(OBJEXT) gl_list.$(OBJEXT) \ + gl_oset.$(OBJEXT) strnlen1.$(OBJEXT) tmpdir.$(OBJEXT) \ + verror.$(OBJEXT) xalloc-die.$(OBJEXT) xallocsa.$(OBJEXT) \ + xvasprintf.$(OBJEXT) xasprintf.$(OBJEXT) +libm4_a_OBJECTS = $(am_libm4_a_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libm4_a_SOURCES) $(EXTRA_libm4_a_SOURCES) +DIST_SOURCES = $(libm4_a_SOURCES) $(EXTRA_libm4_a_SOURCES) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ABSOLUTE_STDINT_H = @ABSOLUTE_STDINT_H@ +ABSOLUTE_SYS_STAT_H = @ABSOLUTE_SYS_STAT_H@ +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +ALLOCA_H = @ALLOCA_H@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@ +BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@ +BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@ +BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@ +BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EOVERFLOW = @EOVERFLOW@ +EXEEXT = @EXEEXT@ +GETOPT_H = @GETOPT_H@ +GREP = @GREP@ +HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ +HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@ +HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ +HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ +HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ +HAVE_STDINT_H = @HAVE_STDINT_H@ +HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@ +HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@ +HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ +HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@ +HAVE_WCHAR_H = @HAVE_WCHAR_H@ +HAVE__BOOL = @HAVE__BOOL@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBM4_LIBDEPS = @LIBM4_LIBDEPS@ +LIBM4_LTLIBDEPS = @LIBM4_LTLIBDEPS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +M4_LIBOBJS = @M4_LIBOBJS@ +M4_LTLIBOBJS = @M4_LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@ +SIZE_T_SUFFIX = @SIZE_T_SUFFIX@ +STDBOOL_H = @STDBOOL_H@ +STDINT_H = @STDINT_H@ +STRIP = @STRIP@ +SYS_STAT_H = @SYS_STAT_H@ +UNISTD_H = @UNISTD_H@ +VERSION = @VERSION@ +WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ +WINT_T_SUFFIX = @WINT_T_SUFFIX@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = 1.5 gnits +noinst_LIBRARIES = libm4.a +libm4_a_SOURCES = allocsa.h allocsa.c gl_avltree_oset.h \ + gl_avltree_oset.c gl_anytree_oset.h binary-io.h clean-temp.h \ + clean-temp.c exit.h fatal-signal.h fatal-signal.c gettext.h \ + gl_linkedhash_list.h gl_linkedhash_list.c gl_anyhash_list1.h \ + gl_anyhash_list2.h gl_anylinked_list1.h gl_anylinked_list2.h \ + gl_list.h gl_list.c mbuiter.h gl_oset.h gl_oset.c size_max.h \ + strnlen1.h strnlen1.c tmpdir.h tmpdir.c verify.h verror.h \ + verror.c wcwidth.h xalloc-die.c xallocsa.h xallocsa.c xsize.h \ + xvasprintf.h xvasprintf.c xasprintf.c +libm4_a_LIBADD = $(M4_LIBOBJS) @ALLOCA@ +libm4_a_DEPENDENCIES = $(M4_LIBOBJS) @ALLOCA@ +EXTRA_libm4_a_SOURCES = alloca.c cloexec.c close-stream.c closeout.c \ + error.c exitfail.c fopen-safer.c __fpending.c free.c getopt.c \ + getopt1.c malloc.c mbchar.c memchr.c mkdtemp.c mkstemp.c \ + obstack.c quotearg.c regcomp.c regex.c regex_internal.c \ + regexec.c sigprocmask.c mkstemp-safer.c strcasecmp.c \ + strncasecmp.c strstr.c strtol.c tempname.c dup-safer.c \ + fd-safer.c pipe-safer.c asnprintf.c printf-args.c \ + printf-parse.c vasnprintf.c asprintf.c vasprintf.c xmalloc.c +noinst_HEADERS = +EXTRA_DIST = alloca.c alloca_.h allocsa.valgrind cloexec.c cloexec.h \ + close-stream.c close-stream.h closeout.c closeout.h error.c \ + error.h exitfail.c exitfail.h fopen-safer.c stdio--.h \ + stdio-safer.h __fpending.c __fpending.h free.c getopt.c \ + getopt1.c getopt_.h getopt_int.h malloc.c mbchar.c mbchar.h \ + memchr.c mkdtemp.c mkdtemp.h mkstemp.c mkstemp.h obstack.c \ + obstack.h pathmax.h quotearg.c quotearg.h regcomp.c regex.c \ + regex.h regex_internal.c regex_internal.h regexec.c \ + sigprocmask.c sigprocmask.h stdbool_.h stdint_.h \ + mkstemp-safer.c stdlib--.h stdlib-safer.h strcase.h \ + strcasecmp.c strncasecmp.c strstr.c strstr.h strtol.c stat_.h \ + tempname.c tempname.h dup-safer.c fd-safer.c pipe-safer.c \ + unistd--.h unistd-safer.h unlocked-io.h asnprintf.c \ + printf-args.c printf-args.h printf-parse.c printf-parse.h \ + vasnprintf.c vasnprintf.h asprintf.c vasprintf.c vasprintf.h \ + xalloc.h xmalloc.c xalloc.h +BUILT_SOURCES = $(ALLOCA_H) $(GETOPT_H) $(STDBOOL_H) $(STDINT_H) \ + $(SYS_STAT_H) $(UNISTD_H) +SUFFIXES = +MOSTLYCLEANFILES = core *.stackdump alloca.h alloca.h-t getopt.h \ + getopt.h-t stdbool.h stdbool.h-t stdint.h stdint.h-t \ + sys/stat.h sys/stat.h-t unistd.h +MOSTLYCLEANDIRS = sys +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = +AM_CPPFLAGS = +all: $(BUILT_SOURCES) config.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits lib/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnits lib/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +config.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/config.hin $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status lib/config.h +$(srcdir)/config.hin: $(am__configure_deps) + cd $(top_srcdir) && $(AUTOHEADER) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libm4.a: $(libm4_a_OBJECTS) $(libm4_a_DEPENDENCIES) + -rm -f libm4.a + $(libm4_a_AR) libm4.a $(libm4_a_OBJECTS) $(libm4_a_LIBADD) + $(RANLIB) libm4.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/alloca.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/__fpending.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloca.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/allocsa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asnprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clean-temp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cloexec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/close-stream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/closeout.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dup-safer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exitfail.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fatal-signal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fd-safer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fopen-safer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/free.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gl_avltree_oset.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gl_linkedhash_list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gl_list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gl_oset.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbchar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memchr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkdtemp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkstemp-safer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkstemp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obstack.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pipe-safer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printf-args.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printf-parse.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quotearg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regcomp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex_internal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regexec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sigprocmask.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strcasecmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strncasecmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strnlen1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strstr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strtol.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tempname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tmpdir.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vasnprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vasprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verror.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xalloc-die.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xallocsa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xasprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmalloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xvasprintf.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) config.hin $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) config.hin $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) config.hin $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) config.hin $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LIBRARIES) $(HEADERS) config.h +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf $(DEPDIR) ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf $(DEPDIR) ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-local + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES ctags distclean distclean-compile \ + distclean-generic distclean-hdr distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-local pdf pdf-am ps ps-am tags \ + uninstall uninstall-am + + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +alloca.h: alloca_.h + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + cat $(srcdir)/alloca_.h; \ + } > $@-t + mv -f $@-t $@ + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +getopt.h: getopt_.h + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + cat $(srcdir)/getopt_.h; \ + } > $@-t + mv -f $@-t $@ + +# We need the following in order to create when the system +# doesn't have one that works. +stdbool.h: stdbool_.h + rm -f $@-t $@ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's/@''HAVE__BOOL''@/$(HAVE__BOOL)/g' < $(srcdir)/stdbool_.h; \ + } > $@-t + mv $@-t $@ + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +stdint.h: stdint_.h + rm -f $@-t $@ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's/@''HAVE_WCHAR_H''@/$(HAVE_WCHAR_H)/g' \ + -e 's/@''HAVE_STDINT_H''@/$(HAVE_STDINT_H)/g' \ + -e 's|@''ABSOLUTE_STDINT_H''@|$(ABSOLUTE_STDINT_H)|g' \ + -e 's/@''HAVE_SYS_TYPES_H''@/$(HAVE_SYS_TYPES_H)/g' \ + -e 's/@''HAVE_INTTYPES_H''@/$(HAVE_INTTYPES_H)/g' \ + -e 's/@''HAVE_SYS_INTTYPES_H''@/$(HAVE_SYS_INTTYPES_H)/g' \ + -e 's/@''HAVE_SYS_BITYPES_H''@/$(HAVE_SYS_BITYPES_H)/g' \ + -e 's/@''HAVE_LONG_LONG_INT''@/$(HAVE_LONG_LONG_INT)/g' \ + -e 's/@''HAVE_UNSIGNED_LONG_LONG_INT''@/$(HAVE_UNSIGNED_LONG_LONG_INT)/g' \ + -e 's/@''BITSIZEOF_PTRDIFF_T''@/$(BITSIZEOF_PTRDIFF_T)/g' \ + -e 's/@''PTRDIFF_T_SUFFIX''@/$(PTRDIFF_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_SIG_ATOMIC_T''@/$(BITSIZEOF_SIG_ATOMIC_T)/g' \ + -e 's/@''HAVE_SIGNED_SIG_ATOMIC_T''@/$(HAVE_SIGNED_SIG_ATOMIC_T)/g' \ + -e 's/@''SIG_ATOMIC_T_SUFFIX''@/$(SIG_ATOMIC_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_SIZE_T''@/$(BITSIZEOF_SIZE_T)/g' \ + -e 's/@''SIZE_T_SUFFIX''@/$(SIZE_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_WCHAR_T''@/$(BITSIZEOF_WCHAR_T)/g' \ + -e 's/@''HAVE_SIGNED_WCHAR_T''@/$(HAVE_SIGNED_WCHAR_T)/g' \ + -e 's/@''WCHAR_T_SUFFIX''@/$(WCHAR_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_WINT_T''@/$(BITSIZEOF_WINT_T)/g' \ + -e 's/@''HAVE_SIGNED_WINT_T''@/$(HAVE_SIGNED_WINT_T)/g' \ + -e 's/@''WINT_T_SUFFIX''@/$(WINT_T_SUFFIX)/g' \ + < $(srcdir)/stdint_.h; \ + } > $@-t + mv $@-t $@ + +# We need the following in order to create when the system +# has one that is incomplete. +sys/stat.h: stat_.h + test -d sys || mkdir sys + rm -f $@-t $@ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's|@''ABSOLUTE_SYS_STAT_H''@|$(ABSOLUTE_SYS_STAT_H)|g' \ + < $(srcdir)/stat_.h; \ + } > $@-t + mv $@-t $@ + +# We need the following in order to create an empty placeholder for +# when the system doesn't have one. +unistd.h: + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + echo '/* Empty placeholder for $@. */'; \ + } > $@ + +mostlyclean-local: mostlyclean-generic + @for dir in '' $(MOSTLYCLEANDIRS); do \ + if test -n "$$dir" && test -d $$dir; then \ + echo "rmdir $$dir"; rmdir $$dir; \ + fi; \ + done +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/bin/m4/lib/__fpending.c b/src/bin/m4/lib/__fpending.c new file mode 100644 index 0000000000..221aee69de --- /dev/null +++ b/src/bin/m4/lib/__fpending.c @@ -0,0 +1,30 @@ +/* __fpending.c -- return the number of pending output bytes on a stream + Copyright (C) 2000, 2004, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Jim Meyering. */ + +#include + +#include "__fpending.h" + +/* Return the number of pending (aka buffered, unflushed) + bytes on the stream, FP, that is open for writing. */ +size_t +__fpending (FILE *fp) +{ + return PENDING_OUTPUT_N_BYTES; +} diff --git a/src/bin/m4/lib/__fpending.h b/src/bin/m4/lib/__fpending.h new file mode 100644 index 0000000000..8a8aabcc22 --- /dev/null +++ b/src/bin/m4/lib/__fpending.h @@ -0,0 +1,34 @@ +/* Declare __fpending. + + Copyright (C) 2000, 2003, 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Written by Jim Meyering. */ + +#include +#include + +#ifndef HAVE_DECL___FPENDING +"this configure-time declaration test was not run" +#endif + +#if HAVE_DECL___FPENDING +# if HAVE_STDIO_EXT_H +# include +# endif +#else +size_t __fpending (FILE *); +#endif diff --git a/src/bin/m4/lib/alloca.c b/src/bin/m4/lib/alloca.c new file mode 100644 index 0000000000..3a1f4e273a --- /dev/null +++ b/src/bin/m4/lib/alloca.c @@ -0,0 +1,489 @@ +/* alloca.c -- allocate automatically reclaimed memory + (Mostly) portable public-domain implementation -- D A Gwyn + + This implementation of the PWB library alloca function, + which is used to allocate space off the run-time stack so + that it is automatically reclaimed upon procedure exit, + was inspired by discussions with J. Q. Johnson of Cornell. + J.Otto Tennant contributed the Cray support. + + There are some preprocessor constants that can + be defined when compiling for your specific system, for + improved efficiency; however, the defaults should be okay. + + The general concept of this implementation is to keep + track of all alloca-allocated blocks, and reclaim any + that are found to be deeper in the stack than the current + invocation. This heuristic does not reclaim storage as + soon as it becomes invalid, but it will do so eventually. + + As a special case, alloca(0) reclaims storage without + allocating any. It is a good idea to use alloca(0) in + your main control loop, etc. to force garbage collection. */ + +#include + +#include + +#include +#include + +#ifdef emacs +# include "lisp.h" +# include "blockinput.h" +# ifdef EMACS_FREE +# undef free +# define free EMACS_FREE +# endif +#else +# define memory_full() abort () +#endif + +/* If compiling with GCC 2, this file's not needed. */ +#if !defined (__GNUC__) || __GNUC__ < 2 + +/* If someone has defined alloca as a macro, + there must be some other way alloca is supposed to work. */ +# ifndef alloca + +# ifdef emacs +# ifdef static +/* actually, only want this if static is defined as "" + -- this is for usg, in which emacs must undefine static + in order to make unexec workable + */ +# ifndef STACK_DIRECTION +you +lose +-- must know STACK_DIRECTION at compile-time +/* Using #error here is not wise since this file should work for + old and obscure compilers. */ +# endif /* STACK_DIRECTION undefined */ +# endif /* static */ +# endif /* emacs */ + +/* If your stack is a linked list of frames, you have to + provide an "address metric" ADDRESS_FUNCTION macro. */ + +# if defined (CRAY) && defined (CRAY_STACKSEG_END) +long i00afunc (); +# define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg)) +# else +# define ADDRESS_FUNCTION(arg) &(arg) +# endif + +/* Define STACK_DIRECTION if you know the direction of stack + growth for your system; otherwise it will be automatically + deduced at run-time. + + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ + +# ifndef STACK_DIRECTION +# define STACK_DIRECTION 0 /* Direction unknown. */ +# endif + +# if STACK_DIRECTION != 0 + +# define STACK_DIR STACK_DIRECTION /* Known at compile-time. */ + +# else /* STACK_DIRECTION == 0; need run-time code. */ + +static int stack_dir; /* 1 or -1 once known. */ +# define STACK_DIR stack_dir + +static void +find_stack_direction (void) +{ + static char *addr = NULL; /* Address of first `dummy', once known. */ + auto char dummy; /* To get stack address. */ + + if (addr == NULL) + { /* Initial entry. */ + addr = ADDRESS_FUNCTION (dummy); + + find_stack_direction (); /* Recurse once. */ + } + else + { + /* Second entry. */ + if (ADDRESS_FUNCTION (dummy) > addr) + stack_dir = 1; /* Stack grew upward. */ + else + stack_dir = -1; /* Stack grew downward. */ + } +} + +# endif /* STACK_DIRECTION == 0 */ + +/* An "alloca header" is used to: + (a) chain together all alloca'ed blocks; + (b) keep track of stack depth. + + It is very important that sizeof(header) agree with malloc + alignment chunk size. The following default should work okay. */ + +# ifndef ALIGN_SIZE +# define ALIGN_SIZE sizeof(double) +# endif + +typedef union hdr +{ + char align[ALIGN_SIZE]; /* To force sizeof(header). */ + struct + { + union hdr *next; /* For chaining headers. */ + char *deep; /* For stack depth measure. */ + } h; +} header; + +static header *last_alloca_header = NULL; /* -> last alloca header. */ + +/* Return a pointer to at least SIZE bytes of storage, + which will be automatically reclaimed upon exit from + the procedure that called alloca. Originally, this space + was supposed to be taken from the current stack frame of the + caller, but that method cannot be made to work for some + implementations of C, for example under Gould's UTX/32. */ + +void * +alloca (size_t size) +{ + auto char probe; /* Probes stack depth: */ + register char *depth = ADDRESS_FUNCTION (probe); + +# if STACK_DIRECTION == 0 + if (STACK_DIR == 0) /* Unknown growth direction. */ + find_stack_direction (); +# endif + + /* Reclaim garbage, defined as all alloca'd storage that + was allocated from deeper in the stack than currently. */ + + { + register header *hp; /* Traverses linked list. */ + +# ifdef emacs + BLOCK_INPUT; +# endif + + for (hp = last_alloca_header; hp != NULL;) + if ((STACK_DIR > 0 && hp->h.deep > depth) + || (STACK_DIR < 0 && hp->h.deep < depth)) + { + register header *np = hp->h.next; + + free (hp); /* Collect garbage. */ + + hp = np; /* -> next header. */ + } + else + break; /* Rest are not deeper. */ + + last_alloca_header = hp; /* -> last valid storage. */ + +# ifdef emacs + UNBLOCK_INPUT; +# endif + } + + if (size == 0) + return NULL; /* No allocation required. */ + + /* Allocate combined header + user data storage. */ + + { + /* Address of header. */ + register header *new; + + size_t combined_size = sizeof (header) + size; + if (combined_size < sizeof (header)) + memory_full (); + + new = malloc (combined_size); + + if (! new) + memory_full (); + + new->h.next = last_alloca_header; + new->h.deep = depth; + + last_alloca_header = new; + + /* User storage begins just after header. */ + + return (void *) (new + 1); + } +} + +# if defined (CRAY) && defined (CRAY_STACKSEG_END) + +# ifdef DEBUG_I00AFUNC +# include +# endif + +# ifndef CRAY_STACK +# define CRAY_STACK +# ifndef CRAY2 +/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */ +struct stack_control_header + { + long shgrow:32; /* Number of times stack has grown. */ + long shaseg:32; /* Size of increments to stack. */ + long shhwm:32; /* High water mark of stack. */ + long shsize:32; /* Current size of stack (all segments). */ + }; + +/* The stack segment linkage control information occurs at + the high-address end of a stack segment. (The stack + grows from low addresses to high addresses.) The initial + part of the stack segment linkage control information is + 0200 (octal) words. This provides for register storage + for the routine which overflows the stack. */ + +struct stack_segment_linkage + { + long ss[0200]; /* 0200 overflow words. */ + long sssize:32; /* Number of words in this segment. */ + long ssbase:32; /* Offset to stack base. */ + long:32; + long sspseg:32; /* Offset to linkage control of previous + segment of stack. */ + long:32; + long sstcpt:32; /* Pointer to task common address block. */ + long sscsnm; /* Private control structure number for + microtasking. */ + long ssusr1; /* Reserved for user. */ + long ssusr2; /* Reserved for user. */ + long sstpid; /* Process ID for pid based multi-tasking. */ + long ssgvup; /* Pointer to multitasking thread giveup. */ + long sscray[7]; /* Reserved for Cray Research. */ + long ssa0; + long ssa1; + long ssa2; + long ssa3; + long ssa4; + long ssa5; + long ssa6; + long ssa7; + long sss0; + long sss1; + long sss2; + long sss3; + long sss4; + long sss5; + long sss6; + long sss7; + }; + +# else /* CRAY2 */ +/* The following structure defines the vector of words + returned by the STKSTAT library routine. */ +struct stk_stat + { + long now; /* Current total stack size. */ + long maxc; /* Amount of contiguous space which would + be required to satisfy the maximum + stack demand to date. */ + long high_water; /* Stack high-water mark. */ + long overflows; /* Number of stack overflow ($STKOFEN) calls. */ + long hits; /* Number of internal buffer hits. */ + long extends; /* Number of block extensions. */ + long stko_mallocs; /* Block allocations by $STKOFEN. */ + long underflows; /* Number of stack underflow calls ($STKRETN). */ + long stko_free; /* Number of deallocations by $STKRETN. */ + long stkm_free; /* Number of deallocations by $STKMRET. */ + long segments; /* Current number of stack segments. */ + long maxs; /* Maximum number of stack segments so far. */ + long pad_size; /* Stack pad size. */ + long current_address; /* Current stack segment address. */ + long current_size; /* Current stack segment size. This + number is actually corrupted by STKSTAT to + include the fifteen word trailer area. */ + long initial_address; /* Address of initial segment. */ + long initial_size; /* Size of initial segment. */ + }; + +/* The following structure describes the data structure which trails + any stack segment. I think that the description in 'asdef' is + out of date. I only describe the parts that I am sure about. */ + +struct stk_trailer + { + long this_address; /* Address of this block. */ + long this_size; /* Size of this block (does not include + this trailer). */ + long unknown2; + long unknown3; + long link; /* Address of trailer block of previous + segment. */ + long unknown5; + long unknown6; + long unknown7; + long unknown8; + long unknown9; + long unknown10; + long unknown11; + long unknown12; + long unknown13; + long unknown14; + }; + +# endif /* CRAY2 */ +# endif /* not CRAY_STACK */ + +# ifdef CRAY2 +/* Determine a "stack measure" for an arbitrary ADDRESS. + I doubt that "lint" will like this much. */ + +static long +i00afunc (long *address) +{ + struct stk_stat status; + struct stk_trailer *trailer; + long *block, size; + long result = 0; + + /* We want to iterate through all of the segments. The first + step is to get the stack status structure. We could do this + more quickly and more directly, perhaps, by referencing the + $LM00 common block, but I know that this works. */ + + STKSTAT (&status); + + /* Set up the iteration. */ + + trailer = (struct stk_trailer *) (status.current_address + + status.current_size + - 15); + + /* There must be at least one stack segment. Therefore it is + a fatal error if "trailer" is null. */ + + if (trailer == 0) + abort (); + + /* Discard segments that do not contain our argument address. */ + + while (trailer != 0) + { + block = (long *) trailer->this_address; + size = trailer->this_size; + if (block == 0 || size == 0) + abort (); + trailer = (struct stk_trailer *) trailer->link; + if ((block <= address) && (address < (block + size))) + break; + } + + /* Set the result to the offset in this segment and add the sizes + of all predecessor segments. */ + + result = address - block; + + if (trailer == 0) + { + return result; + } + + do + { + if (trailer->this_size <= 0) + abort (); + result += trailer->this_size; + trailer = (struct stk_trailer *) trailer->link; + } + while (trailer != 0); + + /* We are done. Note that if you present a bogus address (one + not in any segment), you will get a different number back, formed + from subtracting the address of the first block. This is probably + not what you want. */ + + return (result); +} + +# else /* not CRAY2 */ +/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP. + Determine the number of the cell within the stack, + given the address of the cell. The purpose of this + routine is to linearize, in some sense, stack addresses + for alloca. */ + +static long +i00afunc (long address) +{ + long stkl = 0; + + long size, pseg, this_segment, stack; + long result = 0; + + struct stack_segment_linkage *ssptr; + + /* Register B67 contains the address of the end of the + current stack segment. If you (as a subprogram) store + your registers on the stack and find that you are past + the contents of B67, you have overflowed the segment. + + B67 also points to the stack segment linkage control + area, which is what we are really interested in. */ + + stkl = CRAY_STACKSEG_END (); + ssptr = (struct stack_segment_linkage *) stkl; + + /* If one subtracts 'size' from the end of the segment, + one has the address of the first word of the segment. + + If this is not the first segment, 'pseg' will be + nonzero. */ + + pseg = ssptr->sspseg; + size = ssptr->sssize; + + this_segment = stkl - size; + + /* It is possible that calling this routine itself caused + a stack overflow. Discard stack segments which do not + contain the target address. */ + + while (!(this_segment <= address && address <= stkl)) + { +# ifdef DEBUG_I00AFUNC + fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl); +# endif + if (pseg == 0) + break; + stkl = stkl - pseg; + ssptr = (struct stack_segment_linkage *) stkl; + size = ssptr->sssize; + pseg = ssptr->sspseg; + this_segment = stkl - size; + } + + result = address - this_segment; + + /* If you subtract pseg from the current end of the stack, + you get the address of the previous stack segment's end. + This seems a little convoluted to me, but I'll bet you save + a cycle somewhere. */ + + while (pseg != 0) + { +# ifdef DEBUG_I00AFUNC + fprintf (stderr, "%011o %011o\n", pseg, size); +# endif + stkl = stkl - pseg; + ssptr = (struct stack_segment_linkage *) stkl; + size = ssptr->sssize; + pseg = ssptr->sspseg; + result += size; + } + return (result); +} + +# endif /* not CRAY2 */ +# endif /* CRAY */ + +# endif /* no alloca */ +#endif /* not GCC version 2 */ diff --git a/src/bin/m4/lib/alloca_.h b/src/bin/m4/lib/alloca_.h new file mode 100644 index 0000000000..dd0b3e98f5 --- /dev/null +++ b/src/bin/m4/lib/alloca_.h @@ -0,0 +1,54 @@ +/* Memory allocation on the stack. + + Copyright (C) 1995, 1999, 2001, 2002, 2003, 2004, 2006 Free Software + Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. */ + +/* Avoid using the symbol _ALLOCA_H here, as Bison assumes _ALLOCA_H + means there is a real alloca function. */ +#ifndef _GNULIB_ALLOCA_H +# define _GNULIB_ALLOCA_H + +/* alloca (N) returns a pointer to N bytes of memory + allocated on the stack, which will last until the function returns. + Use of alloca should be avoided: + - inside arguments of function calls - undefined behaviour, + - in inline functions - the allocation may actually last until the + calling function returns, + - for huge N (say, N >= 65536) - you never know how large (or small) + the stack is, and when the stack cannot fulfill the memory allocation + request, the program just crashes. + */ + +#ifndef alloca +# ifdef __GNUC__ +# define alloca __builtin_alloca +# elif defined _AIX +# define alloca __alloca +# elif defined _MSC_VER +# include +# define alloca _alloca +# else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +# endif +#endif + +#endif /* _GNULIB_ALLOCA_H */ diff --git a/src/bin/m4/lib/allocsa.c b/src/bin/m4/lib/allocsa.c new file mode 100644 index 0000000000..97652e6889 --- /dev/null +++ b/src/bin/m4/lib/allocsa.c @@ -0,0 +1,137 @@ +/* Safe automatic memory allocation. + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "allocsa.h" + +/* The speed critical point in this file is freesa() applied to an alloca() + result: it must be fast, to match the speed of alloca(). The speed of + mallocsa() and freesa() in the other case are not critical, because they + are only invoked for big memory sizes. */ + +#if HAVE_ALLOCA + +/* Store the mallocsa() results in a hash table. This is needed to reliably + distinguish a mallocsa() result and an alloca() result. + + Although it is possible that the same pointer is returned by alloca() and + by mallocsa() at different times in the same application, it does not lead + to a bug in freesa(), because: + - Before a pointer returned by alloca() can point into malloc()ed memory, + the function must return, and once this has happened the programmer must + not call freesa() on it anyway. + - Before a pointer returned by mallocsa() can point into the stack, it + must be freed. The only function that can free it is freesa(), and + when freesa() frees it, it also removes it from the hash table. */ + +#define MAGIC_NUMBER 0x1415fb4a +#define MAGIC_SIZE sizeof (int) +/* This is how the header info would look like without any alignment + considerations. */ +struct preliminary_header { void *next; char room[MAGIC_SIZE]; }; +/* But the header's size must be a multiple of sa_alignment_max. */ +#define HEADER_SIZE \ + (((sizeof (struct preliminary_header) + sa_alignment_max - 1) / sa_alignment_max) * sa_alignment_max) +struct header { void *next; char room[HEADER_SIZE - sizeof (struct preliminary_header) + MAGIC_SIZE]; }; +/* Verify that HEADER_SIZE == sizeof (struct header). */ +typedef int verify1[2 * (HEADER_SIZE == sizeof (struct header)) - 1]; +/* We make the hash table quite big, so that during lookups the probability + of empty hash buckets is quite high. There is no need to make the hash + table resizable, because when the hash table gets filled so much that the + lookup becomes slow, it means that the application has memory leaks. */ +#define HASH_TABLE_SIZE 257 +static void * mallocsa_results[HASH_TABLE_SIZE]; + +#endif + +void * +mallocsa (size_t n) +{ +#if HAVE_ALLOCA + /* Allocate one more word, that serves as an indicator for malloc()ed + memory, so that freesa() of an alloca() result is fast. */ + size_t nplus = n + HEADER_SIZE; + + if (nplus >= n) + { + char *p = (char *) malloc (nplus); + + if (p != NULL) + { + size_t slot; + + p += HEADER_SIZE; + + /* Put a magic number into the indicator word. */ + ((int *) p)[-1] = MAGIC_NUMBER; + + /* Enter p into the hash table. */ + slot = (unsigned long) p % HASH_TABLE_SIZE; + ((struct header *) (p - HEADER_SIZE))->next = mallocsa_results[slot]; + mallocsa_results[slot] = p; + + return p; + } + } + /* Out of memory. */ + return NULL; +#else +# if !MALLOC_0_IS_NONNULL + if (n == 0) + n = 1; +# endif + return malloc (n); +#endif +} + +#if HAVE_ALLOCA +void +freesa (void *p) +{ + /* mallocsa() may have returned NULL. */ + if (p != NULL) + { + /* Attempt to quickly distinguish the mallocsa() result - which has + a magic indicator word - and the alloca() result - which has an + uninitialized indicator word. It is for this test that sa_increment + additional bytes are allocated in the alloca() case. */ + if (((int *) p)[-1] == MAGIC_NUMBER) + { + /* Looks like a mallocsa() result. To see whether it really is one, + perform a lookup in the hash table. */ + size_t slot = (unsigned long) p % HASH_TABLE_SIZE; + void **chain = &mallocsa_results[slot]; + for (; *chain != NULL;) + { + if (*chain == p) + { + /* Found it. Remove it from the hash table and free it. */ + char *p_begin = (char *) p - HEADER_SIZE; + *chain = ((struct header *) p_begin)->next; + free (p_begin); + return; + } + chain = &((struct header *) ((char *) *chain - HEADER_SIZE))->next; + } + } + /* At this point, we know it was not a mallocsa() result. */ + } +} +#endif diff --git a/src/bin/m4/lib/allocsa.h b/src/bin/m4/lib/allocsa.h new file mode 100644 index 0000000000..0fd46a8e6a --- /dev/null +++ b/src/bin/m4/lib/allocsa.h @@ -0,0 +1,128 @@ +/* Safe automatic memory allocation. + Copyright (C) 2003-2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _ALLOCSA_H +#define _ALLOCSA_H + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* safe_alloca(N) is equivalent to alloca(N) when it is safe to call + alloca(N); otherwise it returns NULL. It either returns N bytes of + memory allocated on the stack, that lasts until the function returns, + or NULL. + Use of safe_alloca should be avoided: + - inside arguments of function calls - undefined behaviour, + - in inline functions - the allocation may actually last until the + calling function returns. +*/ +#if HAVE_ALLOCA +/* The OS usually guarantees only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + allocate anything larger than 4096 bytes. Also care for the possibility + of a few compiler-allocated temporary stack slots. + This must be a macro, not an inline function. */ +# define safe_alloca(N) ((N) < 4032 ? alloca (N) : NULL) +#else +# define safe_alloca(N) ((N), NULL) +#endif + +/* allocsa(N) is a safe variant of alloca(N). It allocates N bytes of + memory allocated on the stack, that must be freed using freesa() before + the function returns. Upon failure, it returns NULL. */ +#if HAVE_ALLOCA +# define allocsa(N) \ + ((N) < 4032 - sa_increment \ + ? (void *) ((char *) alloca ((N) + sa_increment) + sa_increment) \ + : mallocsa (N)) +#else +# define allocsa(N) \ + mallocsa (N) +#endif +extern void * mallocsa (size_t n); + +/* Free a block of memory allocated through allocsa(). */ +#if HAVE_ALLOCA +extern void freesa (void *p); +#else +# define freesa free +#endif + +/* Maybe we should also define a variant + nallocsa (size_t n, size_t s) - behaves like allocsa (n * s) + If this would be useful in your application. please speak up. */ + + +#ifdef __cplusplus +} +#endif + + +/* ------------------- Auxiliary, non-public definitions ------------------- */ + +/* Determine the alignment of a type at compile time. */ +#if defined __GNUC__ +# define sa_alignof __alignof__ +#elif defined __cplusplus + template struct sa_alignof_helper { char __slot1; type __slot2; }; +# define sa_alignof(type) offsetof (sa_alignof_helper, __slot2) +#elif defined __hpux + /* Work around a HP-UX 10.20 cc bug with enums constants defined as offsetof + values. */ +# define sa_alignof(type) (sizeof (type) <= 4 ? 4 : 8) +#elif defined _AIX + /* Work around an AIX 3.2.5 xlc bug with enums constants defined as offsetof + values. */ +# define sa_alignof(type) (sizeof (type) <= 4 ? 4 : 8) +#else +# define sa_alignof(type) offsetof (struct { char __slot1; type __slot2; }, __slot2) +#endif + +enum +{ +/* The desired alignment of memory allocations is the maximum alignment + among all elementary types. */ + sa_alignment_long = sa_alignof (long), + sa_alignment_double = sa_alignof (double), +#ifdef HAVE_LONG_LONG_INT + sa_alignment_longlong = sa_alignof (long long), +#endif +#ifdef HAVE_LONG_DOUBLE + sa_alignment_longdouble = sa_alignof (long double), +#endif + sa_alignment_max = ((sa_alignment_long - 1) | (sa_alignment_double - 1) +#ifdef HAVE_LONG_LONG_INT + | (sa_alignment_longlong - 1) +#endif +#ifdef HAVE_LONG_DOUBLE + | (sa_alignment_longdouble - 1) +#endif + ) + 1, +/* The increment that guarantees room for a magic word must be >= sizeof (int) + and a multiple of sa_alignment_max. */ + sa_increment = ((sizeof (int) + sa_alignment_max - 1) / sa_alignment_max) * sa_alignment_max +}; + +#endif /* _ALLOCSA_H */ diff --git a/src/bin/m4/lib/allocsa.valgrind b/src/bin/m4/lib/allocsa.valgrind new file mode 100644 index 0000000000..f4c77d6861 --- /dev/null +++ b/src/bin/m4/lib/allocsa.valgrind @@ -0,0 +1,7 @@ +# Suppress a valgrind message about use of uninitialized memory in freesa(). +# This use is OK because it provides only a speedup. +{ + freesa + Memcheck:Cond + fun:freesa +} diff --git a/src/bin/m4/lib/asnprintf.c b/src/bin/m4/lib/asnprintf.c new file mode 100644 index 0000000000..26c3988b01 --- /dev/null +++ b/src/bin/m4/lib/asnprintf.c @@ -0,0 +1,35 @@ +/* Formatted output to strings. + Copyright (C) 1999, 2002, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "vasnprintf.h" + +#include + +char * +asnprintf (char *resultbuf, size_t *lengthp, const char *format, ...) +{ + va_list args; + char *result; + + va_start (args, format); + result = vasnprintf (resultbuf, lengthp, format, args); + va_end (args); + return result; +} diff --git a/src/bin/m4/lib/asprintf.c b/src/bin/m4/lib/asprintf.c new file mode 100644 index 0000000000..29ac6cf2d1 --- /dev/null +++ b/src/bin/m4/lib/asprintf.c @@ -0,0 +1,35 @@ +/* Formatted output to strings. + Copyright (C) 1999, 2002, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "vasprintf.h" + +#include + +int +asprintf (char **resultp, const char *format, ...) +{ + va_list args; + int result; + + va_start (args, format); + result = vasprintf (resultp, format, args); + va_end (args); + return result; +} diff --git a/src/bin/m4/lib/binary-io.h b/src/bin/m4/lib/binary-io.h new file mode 100644 index 0000000000..d9748ee252 --- /dev/null +++ b/src/bin/m4/lib/binary-io.h @@ -0,0 +1,64 @@ +/* Binary mode I/O. + Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _BINARY_H +#define _BINARY_H + +/* For systems that distinguish between text and binary I/O. + O_BINARY is usually declared in . */ +#include + +/* The MSVC7 doesn't like to be included after '#define fileno ...', + so we include it here first. */ +#include + +#if !defined O_BINARY && defined _O_BINARY + /* For MSC-compatible compilers. */ +# define O_BINARY _O_BINARY +# define O_TEXT _O_TEXT +#endif +#ifdef __BEOS__ + /* BeOS 5 has O_BINARY and O_TEXT, but they have no effect. */ +# undef O_BINARY +# undef O_TEXT +#endif +#if O_BINARY +# if !(defined __EMX__ || defined __DJGPP__ || defined __CYGWIN__) +# define setmode _setmode +# undef fileno +# define fileno _fileno +# endif +# if defined __DJGPP__ || defined __CYGWIN__ +# include /* declares setmode() */ +# endif +# ifdef __DJGPP__ +# include /* declares isatty() */ +# /* Avoid putting stdin/stdout in binary mode if it is connected to the +# console, because that would make it impossible for the user to +# interrupt the program through Ctrl-C or Ctrl-Break. */ +# define SET_BINARY(fd) (!isatty (fd) ? (setmode (fd, O_BINARY), 0) : 0) +# else +# define SET_BINARY(fd) setmode (fd, O_BINARY) +# endif +#else + /* On reasonable systems, binary I/O is the default. */ +# undef O_BINARY +# define O_BINARY 0 +# define SET_BINARY(fd) /* nothing */ +#endif + +#endif /* _BINARY_H */ diff --git a/src/bin/m4/lib/clean-temp.c b/src/bin/m4/lib/clean-temp.c new file mode 100644 index 0000000000..aa82974a1e --- /dev/null +++ b/src/bin/m4/lib/clean-temp.c @@ -0,0 +1,715 @@ +/* Temporary directories and temporary files with automatic cleanup. + Copyright (C) 2001, 2003, 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + + +#include + +/* Specification. */ +#include "clean-temp.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "fatal-signal.h" +#include "pathmax.h" +#include "tmpdir.h" +#include "mkdtemp.h" +#include "xalloc.h" +#include "xallocsa.h" +#include "gl_linkedhash_list.h" +#include "gettext.h" +#if GNULIB_FWRITEERROR +# include "fwriteerror.h" +#endif +#if GNULIB_CLOSE_STREAM +# include "close-stream.h" +#endif +#if GNULIB_FCNTL_SAFER +# include "fcntl--.h" +#endif +#if GNULIB_FOPEN_SAFER +# include "stdio--.h" +#endif + +#define _(str) gettext (str) + +/* GNU Hurd doesn't have PATH_MAX. */ +#ifndef PATH_MAX +# ifdef MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# else +# define PATH_MAX 1024 +# endif +#endif + +#ifndef uintptr_t +# define uintptr_t unsigned long +#endif + + +/* The use of 'volatile' in the types below (and ISO C 99 section 5.1.2.3.(5)) + ensure that while constructing or modifying the data structures, the field + values are written to memory in the order of the C statements. So the + signal handler can rely on these field values to be up to date. */ + + +/* Registry for a single temporary directory. + 'struct temp_dir' from the public header file overlaps with this. */ +struct tempdir +{ + /* The absolute pathname of the directory. */ + char * volatile dirname; + /* Whether errors during explicit cleanup are reported to standard error. */ + bool cleanup_verbose; + /* Absolute pathnames of subdirectories. */ + gl_list_t /* */ volatile subdirs; + /* Absolute pathnames of files. */ + gl_list_t /* */ volatile files; +}; + +/* List of all temporary directories. */ +static struct +{ + struct tempdir * volatile * volatile tempdir_list; + size_t volatile tempdir_count; + size_t tempdir_allocated; +} cleanup_list /* = { NULL, 0, 0 } */; + +/* List of all open file descriptors to temporary files. */ +static gl_list_t /* */ volatile descriptors; + + +/* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH. + Why? We need a data structure that + + 1) Can contain an arbitrary number of 'char *' values. The strings + are compared via strcmp, not pointer comparison. + 2) Has insertion and deletion operations that are fast: ideally O(1), + or possibly O(log n). This is important for GNU sort, which may + create a large number of temporary files. + 3) Allows iteration through all elements from within a signal handler. + 4) May or may not allow duplicates. It doesn't matter here, since + any file or subdir can only be removed once. + + Criterion 1) would allow any gl_list_t or gl_oset_t implementation. + + Criterion 2) leaves only GL_LINKEDHASH_LIST, GL_TREEHASH_LIST, or + GL_TREE_OSET. + + Criterion 3) puts at disadvantage GL_TREEHASH_LIST and GL_TREE_OSET. + Namely, iteration through the elements of a binary tree requires access + to many ->left, ->right, ->parent pointers. However, the rebalancing + code for insertion and deletion in an AVL or red-black tree is so + complicated that we cannot assume that >left, ->right, ->parent pointers + are in a consistent state throughout these operations. Therefore, to + avoid a crash in the signal handler, all destructive operations to the + lists would have to be protected by a + block_fatal_signals (); + ... + unblock_fatal_signals (); + pair. Which causes extra system calls. + + Criterion 3) would also discourage GL_ARRAY_LIST and GL_CARRAY_LIST, + if they were not already excluded. Namely, these implementations use + xrealloc(), leaving a time window in which in the list->elements pointer + points to already deallocated memory. To avoid a crash in the signal + handler at such a moment, all destructive operations would have to + protected by block/unblock_fatal_signals (), in this case too. + + A list of type GL_LINKEDHASH_LIST without duplicates fulfills all + requirements: + 2) Insertion and deletion are O(1) on average. + 3) The gl_list_iterator, gl_list_iterator_next implementations do + not trigger memory allocations, nor other system calls, and are + therefore safe to be called from a signal handler. + Furthermore, since SIGNAL_SAFE_LIST is defined, the implementation + of the destructive functions ensures that the list structure is + safe to be traversed at any moment, even when interrupted by an + asynchronous signal. + */ + +/* String equality and hash code functions used by the lists. */ + +static bool +string_equals (const void *x1, const void *x2) +{ + const char *s1 = (const char *) x1; + const char *s2 = (const char *) x2; + return strcmp (s1, s2) == 0; +} + +#define SIZE_BITS (sizeof (size_t) * CHAR_BIT) + +/* A hash function for NUL-terminated char* strings using + the method described by Bruno Haible. + See http://www.haible.de/bruno/hashfunc.html. */ +static size_t +string_hash (const void *x) +{ + const char *s = (const char *) x; + size_t h = 0; + + for (; *s; s++) + h = *s + ((h << 9) | (h >> (SIZE_BITS - 9))); + + return h; +} + + +/* The signal handler. It gets called asynchronously. */ +static void +cleanup () +{ + size_t i; + + /* First close all file descriptors to temporary files. */ + { + gl_list_t fds = descriptors; + + if (fds != NULL) + { + gl_list_iterator_t iter; + const void *element; + + iter = gl_list_iterator (fds); + while (gl_list_iterator_next (&iter, &element, NULL)) + { + int fd = (int) (uintptr_t) element; + close (fd); + } + gl_list_iterator_free (&iter); + } + } + + for (i = 0; i < cleanup_list.tempdir_count; i++) + { + struct tempdir *dir = cleanup_list.tempdir_list[i]; + + if (dir != NULL) + { + gl_list_iterator_t iter; + const void *element; + + /* First cleanup the files in the subdirectories. */ + iter = gl_list_iterator (dir->files); + while (gl_list_iterator_next (&iter, &element, NULL)) + { + const char *file = (const char *) element; + unlink (file); + } + gl_list_iterator_free (&iter); + + /* Then cleanup the subdirectories. */ + iter = gl_list_iterator (dir->subdirs); + while (gl_list_iterator_next (&iter, &element, NULL)) + { + const char *subdir = (const char *) element; + rmdir (subdir); + } + gl_list_iterator_free (&iter); + + /* Then cleanup the temporary directory itself. */ + rmdir (dir->dirname); + } + } +} + +/* Create a temporary directory. + PREFIX is used as a prefix for the name of the temporary directory. It + should be short and still give an indication about the program. + PARENTDIR can be used to specify the parent directory; if NULL, a default + parent directory is used (either $TMPDIR or /tmp or similar). + CLEANUP_VERBOSE determines whether errors during explicit cleanup are + reported to standard error. + Return a fresh 'struct temp_dir' on success. Upon error, an error message + is shown and NULL is returned. */ +struct temp_dir * +create_temp_dir (const char *prefix, const char *parentdir, + bool cleanup_verbose) +{ + struct tempdir * volatile *tmpdirp = NULL; + struct tempdir *tmpdir; + size_t i; + char *xtemplate; + char *tmpdirname; + + /* See whether it can take the slot of an earlier temporary directory + already cleaned up. */ + for (i = 0; i < cleanup_list.tempdir_count; i++) + if (cleanup_list.tempdir_list[i] == NULL) + { + tmpdirp = &cleanup_list.tempdir_list[i]; + break; + } + if (tmpdirp == NULL) + { + /* See whether the array needs to be extended. */ + if (cleanup_list.tempdir_count == cleanup_list.tempdir_allocated) + { + /* Note that we cannot use xrealloc(), because then the cleanup() + function could access an already deallocated array. */ + struct tempdir * volatile *old_array = cleanup_list.tempdir_list; + size_t old_allocated = cleanup_list.tempdir_allocated; + size_t new_allocated = 2 * cleanup_list.tempdir_allocated + 1; + struct tempdir * volatile *new_array = + XNMALLOC (new_allocated, struct tempdir * volatile); + + if (old_allocated == 0) + /* First use of this facility. Register the cleanup handler. */ + at_fatal_signal (&cleanup); + else + { + /* Don't use memcpy() here, because memcpy takes non-volatile + arguments and is therefore not guaranteed to complete all + memory stores before the next statement. */ + size_t k; + + for (k = 0; k < old_allocated; k++) + new_array[k] = old_array[k]; + } + + cleanup_list.tempdir_list = new_array; + cleanup_list.tempdir_allocated = new_allocated; + + /* Now we can free the old array. */ + if (old_array != NULL) + free ((struct tempdir **) old_array); + } + + tmpdirp = &cleanup_list.tempdir_list[cleanup_list.tempdir_count]; + /* Initialize *tmpdirp before incrementing tempdir_count, so that + cleanup() will skip this entry before it is fully initialized. */ + *tmpdirp = NULL; + cleanup_list.tempdir_count++; + } + + /* Initialize a 'struct tempdir'. */ + tmpdir = XMALLOC (struct tempdir); + tmpdir->dirname = NULL; + tmpdir->cleanup_verbose = cleanup_verbose; + tmpdir->subdirs = gl_list_create_empty (GL_LINKEDHASH_LIST, + string_equals, string_hash, false); + tmpdir->files = gl_list_create_empty (GL_LINKEDHASH_LIST, + string_equals, string_hash, false); + + /* Create the temporary directory. */ + xtemplate = (char *) xallocsa (PATH_MAX); + if (path_search (xtemplate, PATH_MAX, parentdir, prefix, parentdir == NULL)) + { + error (0, errno, + _("cannot find a temporary directory, try setting $TMPDIR")); + goto quit; + } + block_fatal_signals (); + tmpdirname = mkdtemp (xtemplate); + if (tmpdirname != NULL) + { + tmpdir->dirname = tmpdirname; + *tmpdirp = tmpdir; + } + unblock_fatal_signals (); + if (tmpdirname == NULL) + { + error (0, errno, + _("cannot create a temporary directory using template \"%s\""), + xtemplate); + goto quit; + } + /* Replace tmpdir->dirname with a copy that has indefinite extent. + We cannot do this inside the block_fatal_signals/unblock_fatal_signals + block because then the cleanup handler would not remove the directory + if xstrdup fails. */ + tmpdir->dirname = xstrdup (tmpdirname); + freesa (xtemplate); + return (struct temp_dir *) tmpdir; + + quit: + freesa (xtemplate); + return NULL; +} + +/* Register the given ABSOLUTE_FILE_NAME as being a file inside DIR, that + needs to be removed before DIR can be removed. + Should be called before the file ABSOLUTE_FILE_NAME is created. */ +void +register_temp_file (struct temp_dir *dir, + const char *absolute_file_name) +{ + struct tempdir *tmpdir = (struct tempdir *)dir; + + /* Add absolute_file_name to tmpdir->files, without duplicates. */ + if (gl_list_search (tmpdir->files, absolute_file_name) == NULL) + gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name)); +} + +/* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that + needs to be removed before DIR can be removed. + Should be called when the file ABSOLUTE_FILE_NAME could not be created. */ +void +unregister_temp_file (struct temp_dir *dir, + const char *absolute_file_name) +{ + struct tempdir *tmpdir = (struct tempdir *)dir; + gl_list_t list = tmpdir->files; + gl_list_node_t node; + + node = gl_list_search (list, absolute_file_name); + if (node != NULL) + { + char *old_string = (char *) gl_list_node_value (list, node); + + gl_list_remove_node (list, node); + free (old_string); + } +} + +/* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR, + that needs to be removed before DIR can be removed. + Should be called before the subdirectory ABSOLUTE_DIR_NAME is created. */ +void +register_temp_subdir (struct temp_dir *dir, + const char *absolute_dir_name) +{ + struct tempdir *tmpdir = (struct tempdir *)dir; + + /* Add absolute_dir_name to tmpdir->subdirs, without duplicates. */ + if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL) + gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name)); +} + +/* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR, + that needs to be removed before DIR can be removed. + Should be called when the subdirectory ABSOLUTE_DIR_NAME could not be + created. */ +void +unregister_temp_subdir (struct temp_dir *dir, + const char *absolute_dir_name) +{ + struct tempdir *tmpdir = (struct tempdir *)dir; + gl_list_t list = tmpdir->subdirs; + gl_list_node_t node; + + node = gl_list_search (list, absolute_dir_name); + if (node != NULL) + { + char *old_string = (char *) gl_list_node_value (list, node); + + gl_list_remove_node (list, node); + free (old_string); + } +} + +/* Remove a file, with optional error message. + Return 0 upon success, or -1 if there was some problem. */ +static int +do_unlink (struct temp_dir *dir, const char *absolute_file_name) +{ + if (unlink (absolute_file_name) < 0 && dir->cleanup_verbose + && errno != ENOENT) + { + error (0, errno, _("cannot remove temporary file %s"), absolute_file_name); + return -1; + } + return 0; +} + +/* Remove a directory, with optional error message. + Return 0 upon success, or -1 if there was some problem. */ +static int +do_rmdir (struct temp_dir *dir, const char *absolute_dir_name) +{ + if (rmdir (absolute_dir_name) < 0 && dir->cleanup_verbose + && errno != ENOENT) + { + error (0, errno, + _("cannot remove temporary directory %s"), absolute_dir_name); + return -1; + } + return 0; +} + +/* Remove the given ABSOLUTE_FILE_NAME and unregister it. + Return 0 upon success, or -1 if there was some problem. */ +int +cleanup_temp_file (struct temp_dir *dir, + const char *absolute_file_name) +{ + int err; + + err = do_unlink (dir, absolute_file_name); + unregister_temp_file (dir, absolute_file_name); + + return err; +} + +/* Remove the given ABSOLUTE_DIR_NAME and unregister it. + Return 0 upon success, or -1 if there was some problem. */ +int +cleanup_temp_subdir (struct temp_dir *dir, + const char *absolute_dir_name) +{ + int err; + + err = do_rmdir (dir, absolute_dir_name); + unregister_temp_subdir (dir, absolute_dir_name); + + return err; +} + +/* Remove all registered files and subdirectories inside DIR. + Return 0 upon success, or -1 if there was some problem. */ +int +cleanup_temp_dir_contents (struct temp_dir *dir) +{ + struct tempdir *tmpdir = (struct tempdir *)dir; + int err = 0; + gl_list_t list; + gl_list_iterator_t iter; + const void *element; + gl_list_node_t node; + + /* First cleanup the files in the subdirectories. */ + list = tmpdir->files; + iter = gl_list_iterator (list); + while (gl_list_iterator_next (&iter, &element, &node)) + { + char *file = (char *) element; + + err |= do_unlink (dir, file); + gl_list_remove_node (list, node); + /* Now only we can free file. */ + free (file); + } + gl_list_iterator_free (&iter); + + /* Then cleanup the subdirectories. */ + list = tmpdir->subdirs; + iter = gl_list_iterator (list); + while (gl_list_iterator_next (&iter, &element, &node)) + { + char *subdir = (char *) element; + + err |= do_rmdir (dir, subdir); + gl_list_remove_node (list, node); + /* Now only we can free subdir. */ + free (subdir); + } + gl_list_iterator_free (&iter); + + return err; +} + +/* Remove all registered files and subdirectories inside DIR and DIR itself. + DIR cannot be used any more after this call. + Return 0 upon success, or -1 if there was some problem. */ +int +cleanup_temp_dir (struct temp_dir *dir) +{ + struct tempdir *tmpdir = (struct tempdir *)dir; + int err = 0; + size_t i; + + err |= cleanup_temp_dir_contents (dir); + err |= do_rmdir (dir, tmpdir->dirname); + + for (i = 0; i < cleanup_list.tempdir_count; i++) + if (cleanup_list.tempdir_list[i] == tmpdir) + { + /* Remove cleanup_list.tempdir_list[i]. */ + if (i + 1 == cleanup_list.tempdir_count) + { + while (i > 0 && cleanup_list.tempdir_list[i - 1] == NULL) + i--; + cleanup_list.tempdir_count = i; + } + else + cleanup_list.tempdir_list[i] = NULL; + /* Now only we can free the tmpdir->dirname and tmpdir itself. */ + free (tmpdir->dirname); + free (tmpdir); + return err; + } + + /* The user passed an invalid DIR argument. */ + abort (); +} + + +/* Register a file descriptor to be closed. */ +static void +register_fd (int fd) +{ + if (descriptors == NULL) + descriptors = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, false); + gl_list_add_first (descriptors, (void *) (uintptr_t) fd); +} + +/* Unregister a file descriptor to be closed. */ +static void +unregister_fd (int fd) +{ + gl_list_t fds = descriptors; + gl_list_node_t node; + + if (fds == NULL) + /* descriptors should already contain fd. */ + abort (); + node = gl_list_search (fds, (void *) (uintptr_t) fd); + if (node == NULL) + /* descriptors should already contain fd. */ + abort (); + gl_list_remove_node (fds, node); +} + +/* Open a temporary file in a temporary directory. + Registers the resulting file descriptor to be closed. */ +int +open_temp (const char *file_name, int flags, mode_t mode) +{ + int fd; + int saved_errno; + + block_fatal_signals (); + fd = open (file_name, flags, mode); /* actually open or open_safer */ + saved_errno = errno; + if (fd >= 0) + register_fd (fd); + unblock_fatal_signals (); + errno = saved_errno; + return fd; +} + +/* Open a temporary file in a temporary directory. + Registers the resulting file descriptor to be closed. */ +FILE * +fopen_temp (const char *file_name, const char *mode) +{ + FILE *fp; + int saved_errno; + + block_fatal_signals (); + fp = fopen (file_name, mode); /* actually fopen or fopen_safer */ + saved_errno = errno; + if (fp != NULL) + { + /* It is sufficient to register fileno (fp) instead of the entire fp, + because at cleanup time there is no need to do an fflush (fp); a + close (fileno (fp)) will be enough. */ + int fd = fileno (fp); + if (!(fd >= 0)) + abort (); + register_fd (fd); + } + unblock_fatal_signals (); + errno = saved_errno; + return fp; +} + +/* Close a temporary file in a temporary directory. + Unregisters the previously registered file descriptor. */ +int +close_temp (int fd) +{ + if (fd >= 0) + { + /* No blocking of signals is needed here, since a double close of a + file descriptor is harmless. */ + int result = close (fd); + int saved_errno = errno; + + /* No race condition here: we assume a single-threaded program, hence + fd cannot be re-opened here. */ + + unregister_fd (fd); + + errno = saved_errno; + return result; + } + else + return close (fd); +} + +/* Close a temporary file in a temporary directory. + Unregisters the previously registered file descriptor. */ +int +fclose_temp (FILE *fp) +{ + int fd = fileno (fp); + /* No blocking of signals is needed here, since a double close of a + file descriptor is harmless. */ + int result = fclose (fp); + int saved_errno = errno; + + /* No race condition here: we assume a single-threaded program, hence + fd cannot be re-opened here. */ + + unregister_fd (fd); + + errno = saved_errno; + return result; +} + +#if GNULIB_FWRITEERROR +/* Like fwriteerror. + Unregisters the previously registered file descriptor. */ +int +fwriteerror_temp (FILE *fp) +{ + int fd = fileno (fp); + /* No blocking of signals is needed here, since a double close of a + file descriptor is harmless. */ + int result = fwriteerror (fp); + int saved_errno = errno; + + /* No race condition here: we assume a single-threaded program, hence + fd cannot be re-opened here. */ + + unregister_fd (fd); + + errno = saved_errno; + return result; +} +#endif + +#if GNULIB_CLOSE_STREAM +/* Like close_stream. + Unregisters the previously registered file descriptor. */ +int +close_stream_temp (FILE *fp) +{ + int fd = fileno (fp); + /* No blocking of signals is needed here, since a double close of a + file descriptor is harmless. */ + int result = close_stream (fp); + int saved_errno = errno; + + /* No race condition here: we assume a single-threaded program, hence + fd cannot be re-opened here. */ + + unregister_fd (fd); + + errno = saved_errno; + return result; +} +#endif diff --git a/src/bin/m4/lib/clean-temp.h b/src/bin/m4/lib/clean-temp.h new file mode 100644 index 0000000000..6e7c3e6c91 --- /dev/null +++ b/src/bin/m4/lib/clean-temp.h @@ -0,0 +1,134 @@ +/* Temporary directories and temporary files with automatic cleanup. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _CLEAN_TEMP_H +#define _CLEAN_TEMP_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Temporary directories and temporary files should be automatically removed + when the program exits either normally or through a fatal signal. We can't + rely on the "unlink before close" idiom, because it works only on Unix and + also - if no signal blocking is used - leaves a time window where a fatal + signal would not clean up the temporary file. + + Also, open file descriptors need to be closed before the temporary files + and the temporary directories can be removed, because only on Unix + (excluding Cygwin) can one remove directories containing open files. + + This module provides support for temporary directories and temporary files + inside these temporary directories. Temporary files without temporary + directories are not supported here. */ + +struct temp_dir +{ + /* The absolute pathname of the directory. */ + const char * const dir_name; + /* Whether errors during explicit cleanup are reported to standard error. */ + bool cleanup_verbose; + /* More fields are present here, but not public. */ +}; + +/* Create a temporary directory. + PREFIX is used as a prefix for the name of the temporary directory. It + should be short and still give an indication about the program. + PARENTDIR can be used to specify the parent directory; if NULL, a default + parent directory is used (either $TMPDIR or /tmp or similar). + CLEANUP_VERBOSE determines whether errors during explicit cleanup are + reported to standard error. + Return a fresh 'struct temp_dir' on success. Upon error, an error message + is shown and NULL is returned. */ +extern struct temp_dir * create_temp_dir (const char *prefix, + const char *parentdir, + bool cleanup_verbose); + +/* Register the given ABSOLUTE_FILE_NAME as being a file inside DIR, that + needs to be removed before DIR can be removed. + Should be called before the file ABSOLUTE_FILE_NAME is created. */ +extern void register_temp_file (struct temp_dir *dir, + const char *absolute_file_name); + +/* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that + needs to be removed before DIR can be removed. + Should be called when the file ABSOLUTE_FILE_NAME could not be created. */ +extern void unregister_temp_file (struct temp_dir *dir, + const char *absolute_file_name); + +/* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR, + that needs to be removed before DIR can be removed. + Should be called before the subdirectory ABSOLUTE_DIR_NAME is created. */ +extern void register_temp_subdir (struct temp_dir *dir, + const char *absolute_dir_name); + +/* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR, + that needs to be removed before DIR can be removed. + Should be called when the subdirectory ABSOLUTE_DIR_NAME could not be + created. */ +extern void unregister_temp_subdir (struct temp_dir *dir, + const char *absolute_dir_name); + +/* Remove the given ABSOLUTE_FILE_NAME and unregister it. + Return 0 upon success, or -1 if there was some problem. */ +extern int cleanup_temp_file (struct temp_dir *dir, + const char *absolute_file_name); + +/* Remove the given ABSOLUTE_DIR_NAME and unregister it. + Return 0 upon success, or -1 if there was some problem. */ +extern int cleanup_temp_subdir (struct temp_dir *dir, + const char *absolute_dir_name); + +/* Remove all registered files and subdirectories inside DIR. + Return 0 upon success, or -1 if there was some problem. */ +extern int cleanup_temp_dir_contents (struct temp_dir *dir); + +/* Remove all registered files and subdirectories inside DIR and DIR itself. + DIR cannot be used any more after this call. + Return 0 upon success, or -1 if there was some problem. */ +extern int cleanup_temp_dir (struct temp_dir *dir); + +/* Open a temporary file in a temporary directory. + Registers the resulting file descriptor to be closed. */ +extern int open_temp (const char *file_name, int flags, mode_t mode); +extern FILE * fopen_temp (const char *file_name, const char *mode); + +/* Close a temporary file in a temporary directory. + Unregisters the previously registered file descriptor. */ +extern int close_temp (int fd); +extern int fclose_temp (FILE *fp); + +/* Like fwriteerror. + Unregisters the previously registered file descriptor. */ +extern int fwriteerror_temp (FILE *fp); + +/* Like close_stream. + Unregisters the previously registered file descriptor. */ +extern int close_stream_temp (FILE *fp); + + +#ifdef __cplusplus +} +#endif + +#endif /* _CLEAN_TEMP_H */ diff --git a/src/bin/m4/lib/cloexec.c b/src/bin/m4/lib/cloexec.c new file mode 100644 index 0000000000..6480006a5b --- /dev/null +++ b/src/bin/m4/lib/cloexec.c @@ -0,0 +1,59 @@ +/* closexec.c - set or clear the close-on-exec descriptor flag + + Copyright (C) 1991, 2004, 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + The code is taken from glibc/manual/llio.texi */ + +#include + +#include "cloexec.h" + +#include +#include + +#ifndef FD_CLOEXEC +# define FD_CLOEXEC 1 +#endif + +/* Set the `FD_CLOEXEC' flag of DESC if VALUE is true, + or clear the flag if VALUE is false. + Return 0 on success, or -1 on error with `errno' set. */ + +int +set_cloexec_flag (int desc, bool value) +{ +#if defined F_GETFD && defined F_SETFD + + int flags = fcntl (desc, F_GETFD, 0); + + if (0 <= flags) + { + int newflags = (value ? flags | FD_CLOEXEC : flags & ~FD_CLOEXEC); + + if (flags == newflags + || fcntl (desc, F_SETFD, newflags) != -1) + return 0; + } + + return -1; + +#else + + return 0; + +#endif +} diff --git a/src/bin/m4/lib/cloexec.h b/src/bin/m4/lib/cloexec.h new file mode 100644 index 0000000000..c25921d6b3 --- /dev/null +++ b/src/bin/m4/lib/cloexec.h @@ -0,0 +1,2 @@ +#include +int set_cloexec_flag (int desc, bool value); diff --git a/src/bin/m4/lib/close-stream.c b/src/bin/m4/lib/close-stream.c new file mode 100644 index 0000000000..72d0d6816c --- /dev/null +++ b/src/bin/m4/lib/close-stream.c @@ -0,0 +1,76 @@ +/* Close a stream, with nicer error checking than fclose's. + + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2006 Free + Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +#include "close-stream.h" + +#include +#include + +#include "__fpending.h" + +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +/* Close STREAM. Return 0 if successful, EOF (setting errno) + otherwise. A failure might set errno to 0 if the error number + cannot be determined. + + If a program writes *anything* to STREAM, that program should close + STREAM and make sure that it succeeds before exiting. Otherwise, + suppose that you go to the extreme of checking the return status + of every function that does an explicit write to STREAM. The last + printf can succeed in writing to the internal stream buffer, and yet + the fclose(STREAM) could still fail (due e.g., to a disk full error) + when it tries to write out that buffered data. Thus, you would be + left with an incomplete output file and the offending program would + exit successfully. Even calling fflush is not always sufficient, + since some file systems (NFS and CODA) buffer written/flushed data + until an actual close call. + + Besides, it's wasteful to check the return value from every call + that writes to STREAM -- just let the internal stream state record + the failure. That's what the ferror test is checking below. */ + +int +close_stream (FILE *stream) +{ + bool some_pending = (__fpending (stream) != 0); + bool prev_fail = (ferror (stream) != 0); + bool fclose_fail = (fclose (stream) != 0); + + /* Return an error indication if there was a previous failure or if + fclose failed, with one exception: ignore an fclose failure if + there was no previous error, no data remains to be flushed, and + fclose failed with EBADF. That can happen when a program like cp + is invoked like this `cp a b >&-' (i.e., with standard output + closed) and doesn't generate any output (hence no previous error + and nothing to be flushed). */ + + if (prev_fail || (fclose_fail && (some_pending || errno != EBADF))) + { + if (! fclose_fail) + errno = 0; + return EOF; + } + + return 0; +} diff --git a/src/bin/m4/lib/close-stream.h b/src/bin/m4/lib/close-stream.h new file mode 100644 index 0000000000..be3d4196b0 --- /dev/null +++ b/src/bin/m4/lib/close-stream.h @@ -0,0 +1,2 @@ +#include +int close_stream (FILE *stream); diff --git a/src/bin/m4/lib/closeout.c b/src/bin/m4/lib/closeout.c new file mode 100644 index 0000000000..830f16f8ae --- /dev/null +++ b/src/bin/m4/lib/closeout.c @@ -0,0 +1,86 @@ +/* Close standard output and standard error, exiting with a diagnostic on error. + + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2006 Free + Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +#include "closeout.h" + +#include +#include +#include + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +#include "close-stream.h" +#include "error.h" +#include "exitfail.h" +#include "quotearg.h" + +static const char *file_name; + +/* Set the file name to be reported in the event an error is detected + by close_stdout. */ +void +close_stdout_set_file_name (const char *file) +{ + file_name = file; +} + +/* Close standard output. On error, issue a diagnostic and _exit + with status 'exit_failure'. + + Also close standard error. On error, _exit with status 'exit_failure'. + + Since close_stdout is commonly registered via 'atexit', POSIX + and the C standard both say that it should not call 'exit', + because the behavior is undefined if 'exit' is called more than + once. So it calls '_exit' instead of 'exit'. If close_stdout + is registered via atexit before other functions are registered, + the other functions can act before this _exit is invoked. + + Applications that use close_stdout should flush any streams + other than stdout and stderr before exiting, since the call to + _exit will bypass other buffer flushing. Applications should + be flushing and closing other streams anyway, to check for I/O + errors. Also, applications should not use tmpfile, since _exit + can bypass the removal of these files. + + It's important to detect such failures and exit nonzero because many + tools (most notably `make' and other build-management systems) depend + on being able to detect failure in other tools via their exit status. */ + +void +close_stdout (void) +{ + if (close_stream (stdout) != 0) + { + char const *write_error = _("write error"); + if (file_name) + error (0, errno, "%s: %s", quotearg_colon (file_name), + write_error); + else + error (0, errno, "%s", write_error); + + _exit (exit_failure); + } + + if (close_stream (stderr) != 0) + _exit (exit_failure); +} diff --git a/src/bin/m4/lib/closeout.h b/src/bin/m4/lib/closeout.h new file mode 100644 index 0000000000..8bed23b5f1 --- /dev/null +++ b/src/bin/m4/lib/closeout.h @@ -0,0 +1,33 @@ +/* Close standard output and standard error. + + Copyright (C) 1998, 2000, 2003, 2004, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef CLOSEOUT_H +# define CLOSEOUT_H 1 + +# ifdef __cplusplus +extern "C" { +# endif + +void close_stdout_set_file_name (const char *file); +void close_stdout (void); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/src/bin/m4/lib/config.h b/src/bin/m4/lib/config.h new file mode 100644 index 0000000000..0c7b207796 --- /dev/null +++ b/src/bin/m4/lib/config.h @@ -0,0 +1,607 @@ +/* lib/config.h. Generated from config.hin by configure. */ +/* lib/config.hin. Generated from configure.ac by autoheader. */ + +/* Define this to an absolute name of . */ +#define ABSOLUTE_STDINT_H "///usr/include/stdint.h" + +/* Define this to an absolute name of . */ +#define ABSOLUTE_SYS_STAT_H "///usr/include/sys/stat.h" + +/* Define to the number of bits in type 'ptrdiff_t'. */ +#define BITSIZEOF_PTRDIFF_T 32 + +/* Define to the number of bits in type 'sig_atomic_t'. */ +#define BITSIZEOF_SIG_ATOMIC_T 32 + +/* Define to the number of bits in type 'size_t'. */ +#define BITSIZEOF_SIZE_T 32 + +/* Define to the number of bits in type 'wchar_t'. */ +#define BITSIZEOF_WCHAR_T 32 + +/* Define to the number of bits in type 'wint_t'. */ +#define BITSIZEOF_WINT_T 32 + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +/* #undef CRAY_STACKSEG_END */ + +/* Define to 1 if using `alloca.c'. */ +/* #undef C_ALLOCA */ + +/* Define to 1 if the changeword(REGEXP) functionality is wanted */ +/* #undef ENABLE_CHANGEWORD */ + +/* Define as good substitute value for EOVERFLOW. */ +/* #undef EOVERFLOW */ + +/* Define to 1 if the return value of system() disagrees with pclose(). */ +/* #undef FUNC_SYSTEM_BROKEN */ + +/* Define to 1 when using the gnulib close-stream module. */ +#define GNULIB_CLOSE_STREAM 1 + +/* Define to 1 when using the gnulib fopen-safer module. */ +#define GNULIB_FOPEN_SAFER 1 + +/* Define to 1 if you have 'alloca' after including , a header that + may be supplied by this distribution. */ +#define HAVE_ALLOCA 1 + +/* Define HAVE_ALLOCA_H for backward compatibility with older code that + includes only if HAVE_ALLOCA_H is defined. */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BP_SYM_H */ + +/* Define to 1 if you have the declaration of `clearerr_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_CLEARERR_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `feof_unlocked', and to 0 if you + don't. */ +#define HAVE_DECL_FEOF_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `ferror_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_FERROR_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `fflush_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_FFLUSH_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_FGETS_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `fputc_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_FPUTC_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `fputs_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_FPUTS_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `fread_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_FREAD_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `fwrite_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_FWRITE_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `getchar_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_GETCHAR_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you + don't. */ +#define HAVE_DECL_GETC_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `getenv', and to 0 if you don't. + */ +#define HAVE_DECL_GETENV 1 + +/* Define to 1 if you have the declaration of `isblank', and to 0 if you + don't. */ +/* #undef HAVE_DECL_ISBLANK */ + +/* Define to 1 if you have the declaration of `mkdir', and to 0 if you don't. + */ +#define HAVE_DECL_MKDIR 1 + +/* Define to 1 if you have the declaration of `putchar_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_PUTCHAR_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `putc_unlocked', and to 0 if you + don't. */ +#define HAVE_DECL_PUTC_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you + don't. */ +#define HAVE_DECL_STRERROR_R 1 + +/* Define to 1 if you have the declaration of `strncasecmp', and to 0 if you + don't. */ +#define HAVE_DECL_STRNCASECMP 1 + +/* Define to 1 if you have the declaration of `wcwidth', and to 0 if you + don't. */ +#define HAVE_DECL_WCWIDTH 1 + +/* Define to 1 if you have the declaration of `__fpending', and to 0 if you + don't. */ +#define HAVE_DECL___FPENDING 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long_only' function. */ +#define HAVE_GETOPT_LONG_ONLY 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if the compiler supports one of the keywords 'inline', + '__inline__', '__inline' and effectively inlines functions marked as such. + */ +#define HAVE_INLINE 1 + +/* Define if you have the 'intmax_t' type in or . */ +#define HAVE_INTMAX_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if exists, doesn't clash with , and + declares uintmax_t. */ +#define HAVE_INTTYPES_H_WITH_UINTMAX 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IO_H */ + +/* Define to 1 if you have the `iswcntrl' function. */ +#define HAVE_ISWCNTRL 1 + +/* Define to 1 if you have the `iswprint' function. */ +#define HAVE_ISWPRINT 1 + +/* Define if you have and nl_langinfo(CODESET). */ +#define HAVE_LANGINFO_CODESET 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define if you have the 'long double' type. */ +#define HAVE_LONG_DOUBLE 1 + +/* Define if you have the 'long long' type. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if the system has the type `long long int'. */ +#define HAVE_LONG_LONG_INT 1 + +/* Define to 1 if you have the `lstat' function. */ +#define HAVE_LSTAT 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the `mbrtowc' function. */ +#define HAVE_MBRTOWC 1 + +/* Define to 1 if you have the `mbsinit' function. */ +#define HAVE_MBSINIT 1 + +/* Define to 1 if declares mbstate_t. */ +#define HAVE_MBSTATE_T 1 + +/* Define to 1 if you have the `memchr' function. */ +#define HAVE_MEMCHR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mempcpy' function. */ +#define HAVE_MEMPCPY 1 + +/* Define to 1 if you have the `mkdtemp' function. */ +#define HAVE_MKDTEMP 1 + +/* Define to 1 if libc includes obstacks. */ +#define HAVE_OBSTACK 1 + +/* Define to 1 if you have the `pipe' function. */ +#define HAVE_PIPE 1 + +/* Define to 1 if you have the sigset_t type and the sigprocmask function. */ +#define HAVE_POSIX_SIGNALBLOCKING 1 + +/* Define to 1 if the system has the type `ptrdiff_t'. */ +#define HAVE_PTRDIFF_T 1 + +/* Define to 1 if you have the `raise' function. */ +#define HAVE_RAISE 1 + +/* Define to 1 if the system has the type `rlim_t'. */ +#define HAVE_RLIM_T 1 + +/* Define to 1 if you have the `sigaction' function. */ +#define HAVE_SIGACTION 1 + +/* Define to 1 if you have the `sigaltstack' function. */ +// #define HAVE_SIGALTSTACK 1 + +/* Define to 1 if the system has the type `sigcontext'. */ +/* #undef HAVE_SIGCONTEXT */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SIGINFO_H */ + +/* Define to 1 if the system has the type `siginfo_t'. */ +#define HAVE_SIGINFO_T 1 + +/* Define to 1 if 'sig_atomic_t' is a signed integer type. */ +#define HAVE_SIGNED_SIG_ATOMIC_T 1 + +/* Define to 1 if 'wchar_t' is a signed integer type. */ +#define HAVE_SIGNED_WCHAR_T 1 + +/* Define to 1 if 'wint_t' is a signed integer type. */ +/* #undef HAVE_SIGNED_WINT_T */ + +/* Define to 1 if you lack the sigprocmask function but have the sigset_t + type. */ +/* #undef HAVE_SIGSET_T */ + +/* Define to 1 if you have the `sigstack' function. */ +#define HAVE_SIGSTACK 1 + +/* Define to 1 if you have the `sigvec' function. */ +#define HAVE_SIGVEC 1 + +/* Define to 1 if the system has the type `sig_atomic_t'. */ +#define HAVE_SIG_ATOMIC_T 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if the system has the type `stack_t'. */ +#define HAVE_STACK_T 1 + +/* Define to 1 if stdbool.h conforms to C99. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define if exists, doesn't clash with , and declares + uintmax_t. */ +#define HAVE_STDINT_H_WITH_UINTMAX 1 + +/* Define to 1 if you have the header file. */ +// #define HAVE_STDIO_EXT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strerror_r' function. */ +#define HAVE_STRERROR_R 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strncasecmp' function. */ +#define HAVE_STRNCASECMP 1 + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Define to 1 if `sa_sigaction' is member of `struct sigaction'. */ +// #define HAVE_STRUCT_SIGACTION_SA_SIGACTION 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_BITYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_INTTYPES_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if the system has the type `unsigned long long int'. */ +#define HAVE_UNSIGNED_LONG_LONG_INT 1 + +/* Define to 1 if you have the `vasnprintf' function. */ +/* #undef HAVE_VASNPRINTF */ + +/* Define to 1 if you have the `vasprintf' function. */ +#define HAVE_VASPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCHAR_H 1 + +/* Define if you have the 'wchar_t' type. */ +#define HAVE_WCHAR_T 1 + +/* Define to 1 if you have the `wcrtomb' function. */ +#define HAVE_WCRTOMB 1 + +/* Define to 1 if you have the `wcscoll' function. */ +#define HAVE_WCSCOLL 1 + +/* Define to 1 if you have the `wcslen' function. */ +#define HAVE_WCSLEN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCTYPE_H 1 + +/* Define to 1 if you have the `wcwidth' function. */ +#define HAVE_WCWIDTH 1 + +/* Define if you have the 'wint_t' type. */ +#define HAVE_WINT_T 1 + +/* Define to 1 if the system has the type `_Bool'. */ +#define HAVE__BOOL 1 + +/* Define to 1 if you have the `__fpending' function. */ +#define HAVE___FPENDING 1 + +/* Define to 1 if you have the `__secure_getenv' function. */ +// #define HAVE___SECURE_GETENV 1 + +/* If malloc(0) is != NULL, define this to 1. Otherwise define this to 0. */ +#define MALLOC_0_IS_NONNULL 1 + +/* Name of package */ +#define PACKAGE "m4" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "bug-m4@gnu.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "GNU M4" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "GNU M4 1.4.8" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "m4" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.4.8" + +/* the number of pending output bytes on stream `fp' */ +/* #undef PENDING_OUTPUT_N_BYTES */ +#define PENDING_OUTPUT_N_BYTES fp->_IO_write_ptr - fp->_IO_write_base + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'ptrdiff_t'. */ +#define PTRDIFF_T_SUFFIX + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define if lists must be signal-safe. */ +#define SIGNAL_SAFE_LIST 1 + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'sig_atomic_t'. */ +#define SIG_ATOMIC_T_SUFFIX + +/* Define as the maximum value of type 'size_t', if the system doesn't define + it. */ +/* #undef SIZE_MAX */ +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'size_t'. */ +#define SIZE_T_SUFFIX u + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if the `S_IS*' macros in do not work properly. */ +/* #undef STAT_MACROS_BROKEN */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if strerror_r returns char *. */ +#define STRERROR_R_CHAR_P 1 + +/* Define to 1 if using stack overflow detection */ +// #define USE_STACKOVF 1 + +/* Define to 1 if you want getc etc. to use unlocked I/O if available. + Unlocked I/O can improve performance in unithreaded apps, but it is not + safe for multithreaded apps. */ +#define USE_UNLOCKED_IO 1 + +/* Version number of package */ +#define VERSION "1.4.8" + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'wchar_t'. */ +#define WCHAR_T_SUFFIX + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'wint_t'. */ +#define WINT_T_SUFFIX u + +/* Define to 1 if malloc debugging is enabled */ +/* #undef WITH_DMALLOC */ + +/* Define to 1 if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* # undef _ALL_SOURCE */ +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#define _FILE_OFFSET_BITS 64 + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define if you want regoff_t to be at least as wide POSIX requires. */ +/* #undef _REGEX_LARGE_OFFSETS */ + +/* Enable extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif + +/* Define to rpl_ if the getopt replacement functions and variables should be + used. */ +/* #undef __GETOPT_PREFIX */ + +/* Define to rpl_ if the mkstemp replacement function should be used. */ +/* #undef __MKSTEMP_PREFIX */ + +/* Define to rpl_free if the replacement function should be used. */ +/* #undef free */ + +/* A replacement for va_copy, if needed. */ +#define gl_va_copy(a,b) ((a) = (b)) + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to long or long long if and don't define. */ +/* #undef intmax_t */ + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to a type if does not define. */ +/* #undef mbstate_t */ + +/* Define to rpl_re_comp if the replacement should be used. */ +/* #undef re_comp */ + +/* Define to rpl_re_compile_fastmap if the replacement should be used. */ +/* #undef re_compile_fastmap */ + +/* Define to rpl_re_compile_pattern if the replacement should be used. */ +/* #undef re_compile_pattern */ + +/* Define to rpl_re_exec if the replacement should be used. */ +/* #undef re_exec */ + +/* Define to rpl_re_match if the replacement should be used. */ +/* #undef re_match */ + +/* Define to rpl_re_match_2 if the replacement should be used. */ +/* #undef re_match_2 */ + +/* Define to rpl_re_search if the replacement should be used. */ +/* #undef re_search */ + +/* Define to rpl_re_search_2 if the replacement should be used. */ +/* #undef re_search_2 */ + +/* Define to rpl_re_set_registers if the replacement should be used. */ +/* #undef re_set_registers */ + +/* Define to rpl_re_set_syntax if the replacement should be used. */ +/* #undef re_set_syntax */ + +/* Define to rpl_re_syntax_options if the replacement should be used. */ +/* #undef re_syntax_options */ + +/* Define to rpl_regcomp if the replacement should be used. */ +/* #undef regcomp */ + +/* Define to rpl_regerror if the replacement should be used. */ +/* #undef regerror */ + +/* Define to rpl_regexec if the replacement should be used. */ +/* #undef regexec */ + +/* Define to rpl_regfree if the replacement should be used. */ +/* #undef regfree */ + +/* Define to equivalent of C99 restrict keyword, or to nothing if this is not + supported. Do not define if restrict is supported directly. */ +#define restrict __restrict + +/* Define to int if rlim_t is not defined in sys/resource.h */ +/* #undef rlim_t */ + +/* Define as an integer type suitable for memory locations that can be + accessed atomically even in the presence of asynchnonous signals. */ +/* #undef sig_atomic_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define as a signed type of the same size as size_t. */ +/* #undef ssize_t */ + +/* Define to struct sigaltstack if stack_t is not in signal.h */ +/* #undef stack_t */ + +/* Define as a macro for copying va_list variables. */ +/* #undef va_copy */ diff --git a/src/bin/m4/lib/config.hin b/src/bin/m4/lib/config.hin new file mode 100644 index 0000000000..fef9579162 --- /dev/null +++ b/src/bin/m4/lib/config.hin @@ -0,0 +1,602 @@ +/* lib/config.hin. Generated from configure.ac by autoheader. */ + +/* Define this to an absolute name of . */ +#undef ABSOLUTE_STDINT_H + +/* Define this to an absolute name of . */ +#undef ABSOLUTE_SYS_STAT_H + +/* Define to the number of bits in type 'ptrdiff_t'. */ +#undef BITSIZEOF_PTRDIFF_T + +/* Define to the number of bits in type 'sig_atomic_t'. */ +#undef BITSIZEOF_SIG_ATOMIC_T + +/* Define to the number of bits in type 'size_t'. */ +#undef BITSIZEOF_SIZE_T + +/* Define to the number of bits in type 'wchar_t'. */ +#undef BITSIZEOF_WCHAR_T + +/* Define to the number of bits in type 'wint_t'. */ +#undef BITSIZEOF_WINT_T + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +#undef CRAY_STACKSEG_END + +/* Define to 1 if using `alloca.c'. */ +#undef C_ALLOCA + +/* Define to 1 if the changeword(REGEXP) functionality is wanted */ +#undef ENABLE_CHANGEWORD + +/* Define as good substitute value for EOVERFLOW. */ +#undef EOVERFLOW + +/* Define to 1 if the return value of system() disagrees with pclose(). */ +#undef FUNC_SYSTEM_BROKEN + +/* Define to 1 when using the gnulib close-stream module. */ +#undef GNULIB_CLOSE_STREAM + +/* Define to 1 when using the gnulib fopen-safer module. */ +#undef GNULIB_FOPEN_SAFER + +/* Define to 1 if you have 'alloca' after including , a header that + may be supplied by this distribution. */ +#undef HAVE_ALLOCA + +/* Define HAVE_ALLOCA_H for backward compatibility with older code that + includes only if HAVE_ALLOCA_H is defined. */ +#undef HAVE_ALLOCA_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_BP_SYM_H + +/* Define to 1 if you have the declaration of `clearerr_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_CLEARERR_UNLOCKED + +/* Define to 1 if you have the declaration of `feof_unlocked', and to 0 if you + don't. */ +#undef HAVE_DECL_FEOF_UNLOCKED + +/* Define to 1 if you have the declaration of `ferror_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_FERROR_UNLOCKED + +/* Define to 1 if you have the declaration of `fflush_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_FFLUSH_UNLOCKED + +/* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_FGETS_UNLOCKED + +/* Define to 1 if you have the declaration of `fputc_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_FPUTC_UNLOCKED + +/* Define to 1 if you have the declaration of `fputs_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_FPUTS_UNLOCKED + +/* Define to 1 if you have the declaration of `fread_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_FREAD_UNLOCKED + +/* Define to 1 if you have the declaration of `fwrite_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_FWRITE_UNLOCKED + +/* Define to 1 if you have the declaration of `getchar_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_GETCHAR_UNLOCKED + +/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you + don't. */ +#undef HAVE_DECL_GETC_UNLOCKED + +/* Define to 1 if you have the declaration of `getenv', and to 0 if you don't. + */ +#undef HAVE_DECL_GETENV + +/* Define to 1 if you have the declaration of `isblank', and to 0 if you + don't. */ +#undef HAVE_DECL_ISBLANK + +/* Define to 1 if you have the declaration of `mkdir', and to 0 if you don't. + */ +#undef HAVE_DECL_MKDIR + +/* Define to 1 if you have the declaration of `putchar_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_PUTCHAR_UNLOCKED + +/* Define to 1 if you have the declaration of `putc_unlocked', and to 0 if you + don't. */ +#undef HAVE_DECL_PUTC_UNLOCKED + +/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you + don't. */ +#undef HAVE_DECL_STRERROR_R + +/* Define to 1 if you have the declaration of `strncasecmp', and to 0 if you + don't. */ +#undef HAVE_DECL_STRNCASECMP + +/* Define to 1 if you have the declaration of `wcwidth', and to 0 if you + don't. */ +#undef HAVE_DECL_WCWIDTH + +/* Define to 1 if you have the declaration of `__fpending', and to 0 if you + don't. */ +#undef HAVE_DECL___FPENDING + +/* Define to 1 if you have the header file. */ +#undef HAVE_GETOPT_H + +/* Define to 1 if you have the `getopt_long_only' function. */ +#undef HAVE_GETOPT_LONG_ONLY + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if the compiler supports one of the keywords 'inline', + '__inline__', '__inline' and effectively inlines functions marked as such. + */ +#undef HAVE_INLINE + +/* Define if you have the 'intmax_t' type in or . */ +#undef HAVE_INTMAX_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define if exists, doesn't clash with , and + declares uintmax_t. */ +#undef HAVE_INTTYPES_H_WITH_UINTMAX + +/* Define to 1 if you have the header file. */ +#undef HAVE_IO_H + +/* Define to 1 if you have the `iswcntrl' function. */ +#undef HAVE_ISWCNTRL + +/* Define to 1 if you have the `iswprint' function. */ +#undef HAVE_ISWPRINT + +/* Define if you have and nl_langinfo(CODESET). */ +#undef HAVE_LANGINFO_CODESET + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define if you have the 'long double' type. */ +#undef HAVE_LONG_DOUBLE + +/* Define if you have the 'long long' type. */ +#undef HAVE_LONG_LONG + +/* Define to 1 if the system has the type `long long int'. */ +#undef HAVE_LONG_LONG_INT + +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the `mbrtowc' function. */ +#undef HAVE_MBRTOWC + +/* Define to 1 if you have the `mbsinit' function. */ +#undef HAVE_MBSINIT + +/* Define to 1 if declares mbstate_t. */ +#undef HAVE_MBSTATE_T + +/* Define to 1 if you have the `memchr' function. */ +#undef HAVE_MEMCHR + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mempcpy' function. */ +#undef HAVE_MEMPCPY + +/* Define to 1 if you have the `mkdtemp' function. */ +#undef HAVE_MKDTEMP + +/* Define to 1 if libc includes obstacks. */ +#undef HAVE_OBSTACK + +/* Define to 1 if you have the `pipe' function. */ +#undef HAVE_PIPE + +/* Define to 1 if you have the sigset_t type and the sigprocmask function. */ +#undef HAVE_POSIX_SIGNALBLOCKING + +/* Define to 1 if the system has the type `ptrdiff_t'. */ +#undef HAVE_PTRDIFF_T + +/* Define to 1 if you have the `raise' function. */ +#undef HAVE_RAISE + +/* Define to 1 if the system has the type `rlim_t'. */ +#undef HAVE_RLIM_T + +/* Define to 1 if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the `sigaltstack' function. */ +#undef HAVE_SIGALTSTACK + +/* Define to 1 if the system has the type `sigcontext'. */ +#undef HAVE_SIGCONTEXT + +/* Define to 1 if you have the header file. */ +#undef HAVE_SIGINFO_H + +/* Define to 1 if the system has the type `siginfo_t'. */ +#undef HAVE_SIGINFO_T + +/* Define to 1 if 'sig_atomic_t' is a signed integer type. */ +#undef HAVE_SIGNED_SIG_ATOMIC_T + +/* Define to 1 if 'wchar_t' is a signed integer type. */ +#undef HAVE_SIGNED_WCHAR_T + +/* Define to 1 if 'wint_t' is a signed integer type. */ +#undef HAVE_SIGNED_WINT_T + +/* Define to 1 if you lack the sigprocmask function but have the sigset_t + type. */ +#undef HAVE_SIGSET_T + +/* Define to 1 if you have the `sigstack' function. */ +#undef HAVE_SIGSTACK + +/* Define to 1 if you have the `sigvec' function. */ +#undef HAVE_SIGVEC + +/* Define to 1 if the system has the type `sig_atomic_t'. */ +#undef HAVE_SIG_ATOMIC_T + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if the system has the type `stack_t'. */ +#undef HAVE_STACK_T + +/* Define to 1 if stdbool.h conforms to C99. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define if exists, doesn't clash with , and declares + uintmax_t. */ +#undef HAVE_STDINT_H_WITH_UINTMAX + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_EXT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strerror_r' function. */ +#undef HAVE_STRERROR_R + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strncasecmp' function. */ +#undef HAVE_STRNCASECMP + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* Define to 1 if `sa_sigaction' is member of `struct sigaction'. */ +#undef HAVE_STRUCT_SIGACTION_SA_SIGACTION + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_BITYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if the system has the type `unsigned long long int'. */ +#undef HAVE_UNSIGNED_LONG_LONG_INT + +/* Define to 1 if you have the `vasnprintf' function. */ +#undef HAVE_VASNPRINTF + +/* Define to 1 if you have the `vasprintf' function. */ +#undef HAVE_VASPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_WCHAR_H + +/* Define if you have the 'wchar_t' type. */ +#undef HAVE_WCHAR_T + +/* Define to 1 if you have the `wcrtomb' function. */ +#undef HAVE_WCRTOMB + +/* Define to 1 if you have the `wcscoll' function. */ +#undef HAVE_WCSCOLL + +/* Define to 1 if you have the `wcslen' function. */ +#undef HAVE_WCSLEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_WCTYPE_H + +/* Define to 1 if you have the `wcwidth' function. */ +#undef HAVE_WCWIDTH + +/* Define if you have the 'wint_t' type. */ +#undef HAVE_WINT_T + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Define to 1 if you have the `__fpending' function. */ +#undef HAVE___FPENDING + +/* Define to 1 if you have the `__secure_getenv' function. */ +#undef HAVE___SECURE_GETENV + +/* If malloc(0) is != NULL, define this to 1. Otherwise define this to 0. */ +#undef MALLOC_0_IS_NONNULL + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* the number of pending output bytes on stream `fp' */ +#undef PENDING_OUTPUT_N_BYTES + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'ptrdiff_t'. */ +#undef PTRDIFF_T_SUFFIX + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define if lists must be signal-safe. */ +#undef SIGNAL_SAFE_LIST + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'sig_atomic_t'. */ +#undef SIG_ATOMIC_T_SUFFIX + +/* Define as the maximum value of type 'size_t', if the system doesn't define + it. */ +#undef SIZE_MAX + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'size_t'. */ +#undef SIZE_T_SUFFIX + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +#undef STACK_DIRECTION + +/* Define to 1 if the `S_IS*' macros in do not work properly. */ +#undef STAT_MACROS_BROKEN + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if strerror_r returns char *. */ +#undef STRERROR_R_CHAR_P + +/* Define to 1 if using stack overflow detection */ +#undef USE_STACKOVF + +/* Define to 1 if you want getc etc. to use unlocked I/O if available. + Unlocked I/O can improve performance in unithreaded apps, but it is not + safe for multithreaded apps. */ +#undef USE_UNLOCKED_IO + +/* Version number of package */ +#undef VERSION + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'wchar_t'. */ +#undef WCHAR_T_SUFFIX + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'wint_t'. */ +#undef WINT_T_SUFFIX + +/* Define to 1 if malloc debugging is enabled */ +#undef WITH_DMALLOC + +/* Define to 1 if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Define if you want regoff_t to be at least as wide POSIX requires. */ +#undef _REGEX_LARGE_OFFSETS + +/* Enable extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif + +/* Define to rpl_ if the getopt replacement functions and variables should be + used. */ +#undef __GETOPT_PREFIX + +/* Define to rpl_ if the mkstemp replacement function should be used. */ +#undef __MKSTEMP_PREFIX + +/* Define to rpl_free if the replacement function should be used. */ +#undef free + +/* A replacement for va_copy, if needed. */ +#define gl_va_copy(a,b) ((a) = (b)) + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to long or long long if and don't define. */ +#undef intmax_t + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to a type if does not define. */ +#undef mbstate_t + +/* Define to rpl_re_comp if the replacement should be used. */ +#undef re_comp + +/* Define to rpl_re_compile_fastmap if the replacement should be used. */ +#undef re_compile_fastmap + +/* Define to rpl_re_compile_pattern if the replacement should be used. */ +#undef re_compile_pattern + +/* Define to rpl_re_exec if the replacement should be used. */ +#undef re_exec + +/* Define to rpl_re_match if the replacement should be used. */ +#undef re_match + +/* Define to rpl_re_match_2 if the replacement should be used. */ +#undef re_match_2 + +/* Define to rpl_re_search if the replacement should be used. */ +#undef re_search + +/* Define to rpl_re_search_2 if the replacement should be used. */ +#undef re_search_2 + +/* Define to rpl_re_set_registers if the replacement should be used. */ +#undef re_set_registers + +/* Define to rpl_re_set_syntax if the replacement should be used. */ +#undef re_set_syntax + +/* Define to rpl_re_syntax_options if the replacement should be used. */ +#undef re_syntax_options + +/* Define to rpl_regcomp if the replacement should be used. */ +#undef regcomp + +/* Define to rpl_regerror if the replacement should be used. */ +#undef regerror + +/* Define to rpl_regexec if the replacement should be used. */ +#undef regexec + +/* Define to rpl_regfree if the replacement should be used. */ +#undef regfree + +/* Define to equivalent of C99 restrict keyword, or to nothing if this is not + supported. Do not define if restrict is supported directly. */ +#undef restrict + +/* Define to int if rlim_t is not defined in sys/resource.h */ +#undef rlim_t + +/* Define as an integer type suitable for memory locations that can be + accessed atomically even in the presence of asynchnonous signals. */ +#undef sig_atomic_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define as a signed type of the same size as size_t. */ +#undef ssize_t + +/* Define to struct sigaltstack if stack_t is not in signal.h */ +#undef stack_t + +/* Define as a macro for copying va_list variables. */ +#undef va_copy diff --git a/src/bin/m4/lib/dup-safer.c b/src/bin/m4/lib/dup-safer.c new file mode 100644 index 0000000000..d0606eeb4c --- /dev/null +++ b/src/bin/m4/lib/dup-safer.c @@ -0,0 +1,45 @@ +/* Invoke dup, but avoid some glitches. + + Copyright (C) 2001, 2004, 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#include + +#include "unistd-safer.h" + +#include + +#include +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +/* Like dup, but do not return STDIN_FILENO, STDOUT_FILENO, or + STDERR_FILENO. */ + +int +dup_safer (int fd) +{ +#ifdef F_DUPFD + return fcntl (fd, F_DUPFD, STDERR_FILENO + 1); +#else + /* fd_safer calls us back, but eventually the recursion unwinds and + does the right thing. */ + return fd_safer (dup (fd)); +#endif +} diff --git a/src/bin/m4/lib/error.c b/src/bin/m4/lib/error.c new file mode 100644 index 0000000000..cf86343320 --- /dev/null +++ b/src/bin/m4/lib/error.c @@ -0,0 +1,338 @@ +/* Error handler for noninteractive utilities + Copyright (C) 1990-1998, 2000-2005, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by David MacKenzie . */ + +#if !_LIBC +# include +#endif + +#include "error.h" + +#include +#include +#include +#include + +#if !_LIBC && ENABLE_NLS +# include "gettext.h" +#endif + +#ifdef _LIBC +# include +# include +# include +# include +# define mbsrtowcs __mbsrtowcs +#endif + +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +#ifndef _ +# define _(String) String +#endif + +/* If NULL, error will flush stdout, then print on stderr the program + name, a colon and a space. Otherwise, error will call this + function without parameters instead. */ +void (*error_print_progname) (void); + +/* This variable is incremented each time `error' is called. */ +unsigned int error_message_count; + +#ifdef _LIBC +/* In the GNU C library, there is a predefined variable for this. */ + +# define program_name program_invocation_name +# include +# include +# include + +/* In GNU libc we want do not want to use the common name `error' directly. + Instead make it a weak alias. */ +extern void __error (int status, int errnum, const char *message, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); +extern void __error_at_line (int status, int errnum, const char *file_name, + unsigned int line_number, const char *message, + ...) + __attribute__ ((__format__ (__printf__, 5, 6)));; +# define error __error +# define error_at_line __error_at_line + +# include +# define fflush(s) INTUSE(_IO_fflush) (s) +# undef putc +# define putc(c, fp) INTUSE(_IO_putc) (c, fp) + +# include + +#else /* not _LIBC */ + +# if !HAVE_DECL_STRERROR_R && STRERROR_R_CHAR_P +# ifndef HAVE_DECL_STRERROR_R +"this configure-time declaration test was not run" +# endif +char *strerror_r (); +# endif + +/* The calling program should define program_name and set it to the + name of the executing program. */ +extern char *program_name; + +# if HAVE_STRERROR_R || defined strerror_r +# define __strerror_r strerror_r +# endif /* HAVE_STRERROR_R || defined strerror_r */ +#endif /* not _LIBC */ + +static void +print_errno_message (int errnum) +{ + char const *s; + +#if defined HAVE_STRERROR_R || _LIBC + char errbuf[1024]; +# if STRERROR_R_CHAR_P || _LIBC + s = __strerror_r (errnum, errbuf, sizeof errbuf); +# else + if (__strerror_r (errnum, errbuf, sizeof errbuf) == 0) + s = errbuf; + else + s = 0; +# endif +#else + s = strerror (errnum); +#endif + +#if !_LIBC + if (! s) + s = _("Unknown system error"); +#endif + +#if _LIBC + __fxprintf (NULL, ": %s", s); +#else + fprintf (stderr, ": %s", s); +#endif +} + +static void +error_tail (int status, int errnum, const char *message, va_list args) +{ +#if _LIBC + if (_IO_fwide (stderr, 0) > 0) + { +# define ALLOCA_LIMIT 2000 + size_t len = strlen (message) + 1; + wchar_t *wmessage = NULL; + mbstate_t st; + size_t res; + const char *tmp; + bool use_malloc = false; + + while (1) + { + if (__libc_use_alloca (len * sizeof (wchar_t))) + wmessage = (wchar_t *) alloca (len * sizeof (wchar_t)); + else + { + if (!use_malloc) + wmessage = NULL; + + wchar_t *p = (wchar_t *) realloc (wmessage, + len * sizeof (wchar_t)); + if (p == NULL) + { + free (wmessage); + fputws_unlocked (L"out of memory\n", stderr); + return; + } + wmessage = p; + use_malloc = true; + } + + memset (&st, '\0', sizeof (st)); + tmp = message; + + res = mbsrtowcs (wmessage, &tmp, len, &st); + if (res != len) + break; + + if (__builtin_expect (len >= SIZE_MAX / 2, 0)) + { + /* This really should not happen if everything is fine. */ + res = (size_t) -1; + break; + } + + len *= 2; + } + + if (res == (size_t) -1) + { + /* The string cannot be converted. */ + if (use_malloc) + { + free (wmessage); + use_malloc = false; + } + wmessage = (wchar_t *) L"???"; + } + + __vfwprintf (stderr, wmessage, args); + + if (use_malloc) + free (wmessage); + } + else +#endif + vfprintf (stderr, message, args); + va_end (args); + + ++error_message_count; + if (errnum) + print_errno_message (errnum); +#if _LIBC + __fxprintf (NULL, "\n"); +#else + putc ('\n', stderr); +#endif + fflush (stderr); + if (status) + exit (status); +} + + +/* Print the program name and error message MESSAGE, which is a printf-style + format string with optional args. + If ERRNUM is nonzero, print its corresponding system error message. + Exit with status STATUS if it is nonzero. */ +void +error (int status, int errnum, const char *message, ...) +{ + va_list args; + +#if defined _LIBC && defined __libc_ptf_call + /* We do not want this call to be cut short by a thread + cancellation. Therefore disable cancellation for now. */ + int state = PTHREAD_CANCEL_ENABLE; + __libc_ptf_call (pthread_setcancelstate, (PTHREAD_CANCEL_DISABLE, &state), + 0); +#endif + + fflush (stdout); +#ifdef _LIBC + _IO_flockfile (stderr); +#endif + if (error_print_progname) + (*error_print_progname) (); + else + { +#if _LIBC + __fxprintf (NULL, "%s: ", program_name); +#else + fprintf (stderr, "%s: ", program_name); +#endif + } + + va_start (args, message); + error_tail (status, errnum, message, args); + +#ifdef _LIBC + _IO_funlockfile (stderr); +# ifdef __libc_ptf_call + __libc_ptf_call (pthread_setcancelstate, (state, NULL), 0); +# endif +#endif +} + +/* Sometimes we want to have at most one error per line. This + variable controls whether this mode is selected or not. */ +int error_one_per_line; + +void +error_at_line (int status, int errnum, const char *file_name, + unsigned int line_number, const char *message, ...) +{ + va_list args; + + if (error_one_per_line) + { + static const char *old_file_name; + static unsigned int old_line_number; + + if (old_line_number == line_number + && (file_name == old_file_name + || strcmp (old_file_name, file_name) == 0)) + /* Simply return and print nothing. */ + return; + + old_file_name = file_name; + old_line_number = line_number; + } + +#if defined _LIBC && defined __libc_ptf_call + /* We do not want this call to be cut short by a thread + cancellation. Therefore disable cancellation for now. */ + int state = PTHREAD_CANCEL_ENABLE; + __libc_ptf_call (pthread_setcancelstate, (PTHREAD_CANCEL_DISABLE, &state), + 0); +#endif + + fflush (stdout); +#ifdef _LIBC + _IO_flockfile (stderr); +#endif + if (error_print_progname) + (*error_print_progname) (); + else + { +#if _LIBC + __fxprintf (NULL, "%s:", program_name); +#else + fprintf (stderr, "%s:", program_name); +#endif + } + +#if _LIBC + __fxprintf (NULL, file_name != NULL ? "%s:%d: " : " ", + file_name, line_number); +#else + fprintf (stderr, file_name != NULL ? "%s:%d: " : " ", + file_name, line_number); +#endif + + va_start (args, message); + error_tail (status, errnum, message, args); + +#ifdef _LIBC + _IO_funlockfile (stderr); +# ifdef __libc_ptf_call + __libc_ptf_call (pthread_setcancelstate, (state, NULL), 0); +# endif +#endif +} + +#ifdef _LIBC +/* Make the weak alias. */ +# undef error +# undef error_at_line +weak_alias (__error, error) +weak_alias (__error_at_line, error_at_line) +#endif diff --git a/src/bin/m4/lib/error.h b/src/bin/m4/lib/error.h new file mode 100644 index 0000000000..5a5f247658 --- /dev/null +++ b/src/bin/m4/lib/error.h @@ -0,0 +1,66 @@ +/* Declaration for error-reporting function + Copyright (C) 1995, 1996, 1997, 2003, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _ERROR_H +#define _ERROR_H 1 + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ +# define __attribute__(Spec) /* empty */ +# endif +/* The __-protected variants of `format' and `printf' attributes + are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __format__ format +# define __printf__ printf +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Print a message with `fprintf (stderr, FORMAT, ...)'; + if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM). + If STATUS is nonzero, terminate the program with `exit (STATUS)'. */ + +extern void error (int __status, int __errnum, const char *__format, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); + +extern void error_at_line (int __status, int __errnum, const char *__fname, + unsigned int __lineno, const char *__format, ...) + __attribute__ ((__format__ (__printf__, 5, 6))); + +/* If NULL, error will flush stdout, then print on stderr the program + name, a colon and a space. Otherwise, error will call this + function without parameters instead. */ +extern void (*error_print_progname) (void); + +/* This variable is incremented each time `error' is called. */ +extern unsigned int error_message_count; + +/* Sometimes we want to have at most one error per line. This + variable controls whether this mode is selected or not. */ +extern int error_one_per_line; + +#ifdef __cplusplus +} +#endif + +#endif /* error.h */ diff --git a/src/bin/m4/lib/exit.h b/src/bin/m4/lib/exit.h new file mode 100644 index 0000000000..e8f7738838 --- /dev/null +++ b/src/bin/m4/lib/exit.h @@ -0,0 +1,32 @@ +/* exit() function. + Copyright (C) 1995, 2001 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _EXIT_H +#define _EXIT_H + +/* Get exit() declaration. */ +#include + +/* Some systems do not define EXIT_*, despite otherwise supporting C89. */ +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif +#ifndef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif + +#endif /* _EXIT_H */ diff --git a/src/bin/m4/lib/exitfail.c b/src/bin/m4/lib/exitfail.c new file mode 100644 index 0000000000..97abc674ec --- /dev/null +++ b/src/bin/m4/lib/exitfail.c @@ -0,0 +1,25 @@ +/* Failure exit status + + Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc. + + 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, 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; see the file COPYING. + If not, write to the Free Software Foundation, + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +#include "exitfail.h" +#include "exit.h" + +int volatile exit_failure = EXIT_FAILURE; diff --git a/src/bin/m4/lib/exitfail.h b/src/bin/m4/lib/exitfail.h new file mode 100644 index 0000000000..e46cf9c166 --- /dev/null +++ b/src/bin/m4/lib/exitfail.h @@ -0,0 +1,20 @@ +/* Failure exit status + + Copyright (C) 2002 Free Software Foundation, Inc. + + 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, 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; see the file COPYING. + If not, write to the Free Software Foundation, + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +extern int volatile exit_failure; diff --git a/src/bin/m4/lib/fatal-signal.c b/src/bin/m4/lib/fatal-signal.c new file mode 100644 index 0000000000..e9c9d04850 --- /dev/null +++ b/src/bin/m4/lib/fatal-signal.c @@ -0,0 +1,270 @@ +/* Emergency actions in case of a fatal signal. + Copyright (C) 2003-2004, 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + + +#include + +/* Specification. */ +#include "fatal-signal.h" + +#include +#include +#include +#include + +#include "sigprocmask.h" +#include "xalloc.h" + +#define SIZEOF(a) (sizeof(a) / sizeof(a[0])) + + +/* ========================================================================= */ + + +/* The list of fatal signals. + These are those signals whose default action is to terminate the process + without a core dump, except + SIGKILL - because it cannot be caught, + SIGALRM SIGUSR1 SIGUSR2 SIGPOLL SIGIO SIGLOST - because applications + often use them for their own purpose, + SIGPROF SIGVTALRM - because they are used for profiling, + SIGSTKFLT - because it is more similar to SIGFPE, SIGSEGV, SIGBUS, + SIGSYS - because it is more similar to SIGABRT, SIGSEGV, + SIGPWR - because it of too special use, + SIGRTMIN...SIGRTMAX - because they are reserved for application use. + plus + SIGXCPU, SIGXFSZ - because they are quite similar to SIGTERM. */ + +static int fatal_signals[] = + { + /* ISO C 99 signals. */ +#ifdef SIGINT + SIGINT, +#endif +#ifdef SIGTERM + SIGTERM, +#endif + /* POSIX:2001 signals. */ +#ifdef SIGHUP + SIGHUP, +#endif +#ifdef SIGPIPE + SIGPIPE, +#endif + /* BSD signals. */ +#ifdef SIGXCPU + SIGXCPU, +#endif +#ifdef SIGXFSZ + SIGXFSZ, +#endif + /* Woe32 signals. */ +#ifdef SIGBREAK + SIGBREAK, +#endif + 0 + }; + +#define num_fatal_signals (SIZEOF (fatal_signals) - 1) + +/* Eliminate signals whose signal handler is SIG_IGN. */ + +static void +init_fatal_signals (void) +{ + static bool fatal_signals_initialized = false; + if (!fatal_signals_initialized) + { +#if HAVE_SIGACTION + size_t i; + + for (i = 0; i < num_fatal_signals; i++) + { + struct sigaction action; + + if (sigaction (fatal_signals[i], NULL, &action) >= 0 + && action.sa_handler == SIG_IGN) + fatal_signals[i] = -1; + } +#endif + + fatal_signals_initialized = true; + } +} + + +/* ========================================================================= */ + + +typedef void (*action_t) (void); + +/* Type of an entry in the actions array. + The 'action' field is accessed from within the fatal_signal_handler(), + therefore we mark it as 'volatile'. */ +typedef struct +{ + volatile action_t action; +} +actions_entry_t; + +/* The registered cleanup actions. */ +static actions_entry_t static_actions[32]; +static actions_entry_t * volatile actions = static_actions; +static sig_atomic_t volatile actions_count = 0; +static size_t actions_allocated = SIZEOF (static_actions); + + +/* Uninstall the handlers. */ +static inline void +uninstall_handlers () +{ + size_t i; + + for (i = 0; i < num_fatal_signals; i++) + if (fatal_signals[i] >= 0) + signal (fatal_signals[i], SIG_DFL); +} + + +/* The signal handler. It gets called asynchronously. */ +static void +fatal_signal_handler (int sig) +{ + for (;;) + { + /* Get the last registered cleanup action, in a reentrant way. */ + action_t action; + size_t n = actions_count; + if (n == 0) + break; + n--; + actions_count = n; + action = actions[n].action; + /* Execute the action. */ + action (); + } + + /* Now execute the signal's default action. + If signal() blocks the signal being delivered for the duration of the + signal handler's execution, the re-raised signal is delivered when this + handler returns; otherwise it is delivered already during raise(). */ + uninstall_handlers (); +#if HAVE_RAISE + raise (sig); +#else + kill (getpid (), sig); +#endif +} + + +/* Install the handlers. */ +static inline void +install_handlers () +{ + size_t i; + + for (i = 0; i < num_fatal_signals; i++) + if (fatal_signals[i] >= 0) + signal (fatal_signals[i], &fatal_signal_handler); +} + + +/* Register a cleanup function to be executed when a catchable fatal signal + occurs. */ +void +at_fatal_signal (action_t action) +{ + static bool cleanup_initialized = false; + if (!cleanup_initialized) + { + init_fatal_signals (); + install_handlers (); + cleanup_initialized = true; + } + + if (actions_count == actions_allocated) + { + /* Extend the actions array. Note that we cannot use xrealloc(), + because then the cleanup() function could access an already + deallocated array. */ + actions_entry_t *old_actions = actions; + size_t old_actions_allocated = actions_allocated; + size_t new_actions_allocated = 2 * actions_allocated; + actions_entry_t *new_actions = + XNMALLOC (new_actions_allocated, actions_entry_t); + size_t k; + + /* Don't use memcpy() here, because memcpy takes non-volatile arguments + and is therefore not guaranteed to complete all memory stores before + the next statement. */ + for (k = 0; k < old_actions_allocated; k++) + new_actions[k] = old_actions[k]; + actions = new_actions; + actions_allocated = new_actions_allocated; + /* Now we can free the old actions array. */ + if (old_actions != static_actions) + free (old_actions); + } + /* The two uses of 'volatile' in the types above (and ISO C 99 section + 5.1.2.3.(5)) ensure that we increment the actions_count only after + the new action has been written to the memory location + actions[actions_count]. */ + actions[actions_count].action = action; + actions_count++; +} + + +/* ========================================================================= */ + + +static sigset_t fatal_signal_set; + +static void +init_fatal_signal_set () +{ + static bool fatal_signal_set_initialized = false; + if (!fatal_signal_set_initialized) + { + size_t i; + + init_fatal_signals (); + + sigemptyset (&fatal_signal_set); + for (i = 0; i < num_fatal_signals; i++) + if (fatal_signals[i] >= 0) + sigaddset (&fatal_signal_set, fatal_signals[i]); + + fatal_signal_set_initialized = true; + } +} + +/* Temporarily delay the catchable fatal signals. */ +void +block_fatal_signals () +{ + init_fatal_signal_set (); + sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL); +} + +/* Stop delaying the catchable fatal signals. */ +void +unblock_fatal_signals () +{ + init_fatal_signal_set (); + sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL); +} diff --git a/src/bin/m4/lib/fatal-signal.h b/src/bin/m4/lib/fatal-signal.h new file mode 100644 index 0000000000..3e6f677a87 --- /dev/null +++ b/src/bin/m4/lib/fatal-signal.h @@ -0,0 +1,77 @@ +/* Emergency actions in case of a fatal signal. + Copyright (C) 2003-2004 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* It is often useful to do some cleanup action when a usually fatal signal + terminates the process, like removing a temporary file or killing a + subprocess that may be stuck waiting for a device, pipe or network input. + Such signals are SIGHUP, SIGINT, SIGPIPE, SIGTERM, and possibly others. + The limitation of this facility is that it cannot work for SIGKILL. + + Signals with a SIG_IGN handler are considered to be non-fatal. The + functions in this file assume that when a SIG_IGN handler is installed + for a signal, it was installed before any functions in this file were + called and it stays so for the whole lifetime of the process. */ + +/* Register a cleanup function to be executed when a catchable fatal signal + occurs. + + Restrictions for the cleanup function: + - The cleanup function can do all kinds of system calls. + - It can also access application dependent memory locations and data + structures provided they are in a consistent state. One way to ensure + this is through block_fatal_signals()/unblock_fatal_signals(), see + below. Another - more tricky - way to ensure this is the careful use + of 'volatile'. + However, + - malloc() and similarly complex facilities are not safe to be called + because they are not guaranteed to be in a consistent state. + - Also, the cleanup function must not block the catchable fatal signals + and leave them blocked upon return. + + The cleanup function is executed asynchronously. It is unspecified + whether during its execution the catchable fatal signals are blocked + or not. */ +extern void at_fatal_signal (void (*function) (void)); + + +/* Sometimes it is necessary to block the usually fatal signals while the + data structures being accessed by the cleanup action are being built or + reorganized. This is the case, for example, when a temporary file or + directory is created through mkstemp() or mkdtemp(), because these + functions create the temporary file or directory _before_ returning its + name to the application. */ + +/* Temporarily delay the catchable fatal signals. + The signals will be blocked (= delayed) until the next call to + unblock_fatal_signals(). If the signals are already blocked, a further + call to block_fatal_signals() has no effect. */ +extern void block_fatal_signals (void); + +/* Stop delaying the catchable fatal signals. */ +extern void unblock_fatal_signals (void); + + +#ifdef __cplusplus +} +#endif diff --git a/src/bin/m4/lib/fd-safer.c b/src/bin/m4/lib/fd-safer.c new file mode 100644 index 0000000000..256bfa4a0e --- /dev/null +++ b/src/bin/m4/lib/fd-safer.c @@ -0,0 +1,57 @@ +/* Return a safer copy of a file descriptor. + + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#include + +#include "unistd-safer.h" + +#include + +#include +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +/* Return FD, unless FD would be a copy of standard input, output, or + error; in that case, return a duplicate of FD, closing FD. On + failure to duplicate, close FD, set errno, and return -1. Preserve + errno if FD is negative, so that the caller can always inspect + errno when the returned value is negative. + + This function is usefully wrapped around functions that return file + descriptors, e.g., fd_safer (open ("file", O_RDONLY)). */ + +int +fd_safer (int fd) +{ + if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) + { + int f = dup_safer (fd); + int e = errno; + close (fd); + errno = e; + fd = f; + } + + return fd; +} diff --git a/src/bin/m4/lib/fopen-safer.c b/src/bin/m4/lib/fopen-safer.c new file mode 100644 index 0000000000..3502dbaa8c --- /dev/null +++ b/src/bin/m4/lib/fopen-safer.c @@ -0,0 +1,68 @@ +/* Invoke fopen, but avoid some glitches. + + Copyright (C) 2001, 2004, 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#include + +#include "stdio-safer.h" + +#include +#include +#include "unistd-safer.h" + +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +/* Like fopen, but do not return stdin, stdout, or stderr. */ + +FILE * +fopen_safer (char const *file, char const *mode) +{ + FILE *fp = fopen (file, mode); + + if (fp) + { + int fd = fileno (fp); + + if (0 <= fd && fd <= STDERR_FILENO) + { + int f = dup_safer (fd); + + if (f < 0) + { + int e = errno; + fclose (fp); + errno = e; + return NULL; + } + + if (fclose (fp) != 0 + || ! (fp = fdopen (f, mode))) + { + int e = errno; + close (f); + errno = e; + return NULL; + } + } + } + + return fp; +} diff --git a/src/bin/m4/lib/free.c b/src/bin/m4/lib/free.c new file mode 100644 index 0000000000..73a125a75f --- /dev/null +++ b/src/bin/m4/lib/free.c @@ -0,0 +1,31 @@ +/* Work around incompatibility on older systems where free (NULL) fails. + + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* written by Paul Eggert */ + +#include +#undef free + +#include + +void +rpl_free (void *p) +{ + if (p) + free (p); +} diff --git a/src/bin/m4/lib/getopt.c b/src/bin/m4/lib/getopt.c new file mode 100644 index 0000000000..3580ad825c --- /dev/null +++ b/src/bin/m4/lib/getopt.c @@ -0,0 +1,1191 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002,2003,2004,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _LIBC +# include +#endif + +#include "getopt.h" + +#include +#include +#include +#include + +#ifdef __VMS +# include +#endif + +#ifdef _LIBC +# include +#else +# include "gettext.h" +# define _(msgid) gettext (msgid) +#endif + +#if defined _LIBC && defined USE_IN_LIBIO +# include +#endif + +#ifndef attribute_hidden +# define attribute_hidden +#endif + +/* Unlike standard Unix `getopt', functions like `getopt_long' + let the user intersperse the options with the other arguments. + + As `getopt_long' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Using `getopt' or setting the environment variable POSIXLY_CORRECT + disables permutation. + Then the application's behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt_int.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Keep a global copy of all internal members of getopt_data. */ + +static struct _getopt_data getopt_data; + + +#if defined HAVE_DECL_GETENV && !HAVE_DECL_GETENV +extern char *getenv (); +#endif + +#ifdef _LIBC +/* Stored original parameters. + XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ +extern int __libc_argc; +extern char **__libc_argv; + +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +# ifdef USE_NONOPTION_FLAGS +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; +# endif + +# ifdef USE_NONOPTION_FLAGS +# define SWAP_FLAGS(ch1, ch2) \ + if (d->__nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (char **argv, struct _getopt_data *d) +{ + int bottom = d->__first_nonopt; + int middle = d->__last_nonopt; + int top = d->optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (d->__nonoption_flags_len > 0 && top >= d->__nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + d->__nonoption_flags_max_len), + '\0', top + 1 - d->__nonoption_flags_max_len); + d->__nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + d->__first_nonopt += (d->optind - d->__last_nonopt); + d->__last_nonopt = d->optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (int argc, char **argv, const char *optstring, + int posixly_correct, struct _getopt_data *d) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + d->__first_nonopt = d->__last_nonopt = d->optind; + + d->__nextchar = NULL; + + d->__posixly_correct = posixly_correct || !!getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + d->__ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + d->__ordering = REQUIRE_ORDER; + ++optstring; + } + else if (d->__posixly_correct) + d->__ordering = REQUIRE_ORDER; + else + d->__ordering = PERMUTE; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + if (!d->__posixly_correct + && argc == __libc_argc && argv == __libc_argv) + { + if (d->__nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + d->__nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = d->__nonoption_flags_max_len = strlen (orig_str); + if (d->__nonoption_flags_max_len < argc) + d->__nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (d->__nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + d->__nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', d->__nonoption_flags_max_len - len); + } + } + d->__nonoption_flags_len = d->__nonoption_flags_max_len; + } + else + d->__nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. + + If POSIXLY_CORRECT is nonzero, behave as if the POSIXLY_CORRECT + environment variable were set. */ + +int +_getopt_internal_r (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, + int long_only, int posixly_correct, struct _getopt_data *d) +{ + int print_errors = d->opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + d->optarg = NULL; + + if (d->optind == 0 || !d->__initialized) + { + if (d->optind == 0) + d->optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring, + posixly_correct, d); + d->__initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#if defined _LIBC && defined USE_NONOPTION_FLAGS +# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0' \ + || (d->optind < d->__nonoption_flags_len \ + && __getopt_nonoption_flags[d->optind] == '1')) +#else +# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') +#endif + + if (d->__nextchar == NULL || *d->__nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (d->__last_nonopt > d->optind) + d->__last_nonopt = d->optind; + if (d->__first_nonopt > d->optind) + d->__first_nonopt = d->optind; + + if (d->__ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (d->__first_nonopt != d->__last_nonopt + && d->__last_nonopt != d->optind) + exchange ((char **) argv, d); + else if (d->__last_nonopt != d->optind) + d->__first_nonopt = d->optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (d->optind < argc && NONOPTION_P) + d->optind++; + d->__last_nonopt = d->optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (d->optind != argc && !strcmp (argv[d->optind], "--")) + { + d->optind++; + + if (d->__first_nonopt != d->__last_nonopt + && d->__last_nonopt != d->optind) + exchange ((char **) argv, d); + else if (d->__first_nonopt == d->__last_nonopt) + d->__first_nonopt = d->optind; + d->__last_nonopt = argc; + + d->optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (d->optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (d->__first_nonopt != d->__last_nonopt) + d->optind = d->__first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (d->__ordering == REQUIRE_ORDER) + return -1; + d->optarg = argv[d->optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + d->__nextchar = (argv[d->optind] + 1 + + (longopts != NULL && argv[d->optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[d->optind][1] == '-' + || (long_only && (argv[d->optind][2] + || !strchr (optstring, argv[d->optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) + { + if ((unsigned int) (nameend - d->__nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[d->optind]) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[d->optind]); +#endif + } + d->__nextchar += strlen (d->__nextchar); + d->optind++; + d->optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + d->optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + d->optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + if (argv[d->optind - 1][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("\ +%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#else + fprintf (stderr, _("\ +%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("\ +%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[d->optind - 1][0], + pfound->name); +#else + fprintf (stderr, _("\ +%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[d->optind - 1][0], + pfound->name); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (n >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#endif + } + + d->__nextchar += strlen (d->__nextchar); + + d->optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (d->optind < argc) + d->optarg = argv[d->optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option `%s' requires an argument\n"), + argv[0], argv[d->optind - 1]) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[d->optind - 1]); +#endif + } + d->__nextchar += strlen (d->__nextchar); + d->optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + d->__nextchar += strlen (d->__nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[d->optind][1] == '-' + || strchr (optstring, *d->__nextchar) == NULL) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + if (argv[d->optind][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"), + argv[0], d->__nextchar); +#else + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], d->__nextchar); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[d->optind][0], d->__nextchar); +#else + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[d->optind][0], d->__nextchar); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (n >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#endif + } + d->__nextchar = (char *) ""; + d->optind++; + d->optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *d->__nextchar++; + char *temp = strchr (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*d->__nextchar == '\0') + ++d->optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + if (d->__posixly_correct) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: illegal option -- %c\n"), + argv[0], c); +#else + fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); +#endif + } + else + { +#if defined _LIBC && defined USE_IN_LIBIO + n = __asprintf (&buf, _("%s: invalid option -- %c\n"), + argv[0], c); +#else + fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (n >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#endif + } + d->optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + d->optind++; + } + else if (d->optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, + _("%s: option requires an argument -- %c\n"), + argv[0], c) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + d->optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `d->optind' once; + increment it again when taking next ARGV-elt as argument. */ + d->optarg = argv[d->optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; + nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) + { + if ((unsigned int) (nameend - d->__nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[d->optind]) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[d->optind]); +#endif + } + d->__nextchar += strlen (d->__nextchar); + d->optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + d->optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + + d->__nextchar += strlen (d->__nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (d->optind < argc) + d->optarg = argv[d->optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option `%s' requires an argument\n"), + argv[0], argv[d->optind - 1]) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[d->optind - 1]); +#endif + } + d->__nextchar += strlen (d->__nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + d->__nextchar += strlen (d->__nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + d->__nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + d->optind++; + } + else + d->optarg = NULL; + d->__nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + d->optind++; + } + else if (d->optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + if (__asprintf (&buf, _("\ +%s: option requires an argument -- %c\n"), + argv[0], c) >= 0) + { + _IO_flockfile (stderr); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + d->optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + d->optarg = argv[d->optind++]; + d->__nextchar = NULL; + } + } + return c; + } +} + +int +_getopt_internal (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, + int long_only, int posixly_correct) +{ + int result; + + getopt_data.optind = optind; + getopt_data.opterr = opterr; + + result = _getopt_internal_r (argc, argv, optstring, longopts, longind, + long_only, posixly_correct, &getopt_data); + + optind = getopt_data.optind; + optarg = getopt_data.optarg; + optopt = getopt_data.optopt; + + return result; +} + +/* glibc gets a LSB-compliant getopt. + Standalone applications get a POSIX-compliant getopt. */ +#if _LIBC +enum { POSIXLY_CORRECT = 0 }; +#else +enum { POSIXLY_CORRECT = 1 }; +#endif + +int +getopt (int argc, char *const *argv, const char *optstring) +{ + return _getopt_internal (argc, (char **) argv, optstring, NULL, NULL, 0, + POSIXLY_CORRECT); +} + + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/bin/m4/lib/getopt1.c b/src/bin/m4/lib/getopt1.c new file mode 100644 index 0000000000..cc0746ea43 --- /dev/null +++ b/src/bin/m4/lib/getopt1.c @@ -0,0 +1,171 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98,2004,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifdef _LIBC +# include +#else +# include +# include "getopt.h" +#endif +#include "getopt_int.h" + +#include + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, (char **) argv, options, long_options, + opt_index, 0, 0); +} + +int +_getopt_long_r (int argc, char **argv, const char *options, + const struct option *long_options, int *opt_index, + struct _getopt_data *d) +{ + return _getopt_internal_r (argc, argv, options, long_options, opt_index, + 0, 0, d); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (int argc, char *__getopt_argv_const *argv, + const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, (char **) argv, options, long_options, + opt_index, 1, 0); +} + +int +_getopt_long_only_r (int argc, char **argv, const char *options, + const struct option *long_options, int *opt_index, + struct _getopt_data *d) +{ + return _getopt_internal_r (argc, argv, options, long_options, opt_index, + 1, 0, d); +} + + +#ifdef TEST + +#include + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/bin/m4/lib/getopt_.h b/src/bin/m4/lib/getopt_.h new file mode 100644 index 0000000000..27fce3dc79 --- /dev/null +++ b/src/bin/m4/lib/getopt_.h @@ -0,0 +1,226 @@ +/* Declarations for getopt. + Copyright (C) 1989-1994,1996-1999,2001,2003,2004,2005,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +/* Standalone applications should #define __GETOPT_PREFIX to an + identifier that prefixes the external functions and variables + defined in this header. When this happens, include the + headers that might declare getopt so that they will not cause + confusion if included after this file. Then systematically rename + identifiers so that they do not collide with the system functions + and variables. Renaming avoids problems with some compilers and + linkers. */ +#if defined __GETOPT_PREFIX && !defined __need_getopt +# include +# include +# include +# undef __need_getopt +# undef getopt +# undef getopt_long +# undef getopt_long_only +# undef optarg +# undef opterr +# undef optind +# undef optopt +# define __GETOPT_CONCAT(x, y) x ## y +# define __GETOPT_XCONCAT(x, y) __GETOPT_CONCAT (x, y) +# define __GETOPT_ID(y) __GETOPT_XCONCAT (__GETOPT_PREFIX, y) +# define getopt __GETOPT_ID (getopt) +# define getopt_long __GETOPT_ID (getopt_long) +# define getopt_long_only __GETOPT_ID (getopt_long_only) +# define optarg __GETOPT_ID (optarg) +# define opterr __GETOPT_ID (opterr) +# define optind __GETOPT_ID (optind) +# define optopt __GETOPT_ID (optopt) +#endif + +/* Standalone applications get correct prototypes for getopt_long and + getopt_long_only; they declare "char **argv". libc uses prototypes + with "char *const *argv" that are incorrect because getopt_long and + getopt_long_only can permute argv; this is required for backward + compatibility (e.g., for LSB 2.0.1). + + This used to be `#if defined __GETOPT_PREFIX && !defined __need_getopt', + but it caused redefinition warnings if both unistd.h and getopt.h were + included, since unistd.h includes getopt.h having previously defined + __need_getopt. + + The only place where __getopt_argv_const is used is in definitions + of getopt_long and getopt_long_only below, but these are visible + only if __need_getopt is not defined, so it is quite safe to rewrite + the conditional as follows: +*/ +#if !defined __need_getopt +# if defined __GETOPT_PREFIX +# define __getopt_argv_const /* empty */ +# else +# define __getopt_argv_const const +# endif +#endif + +/* If __GNU_LIBRARY__ is not already defined, either we are being used + standalone, or this is the first header included in the source file. + If we are being used with glibc, we need to include , but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include , which will pull in for us + if it's from glibc. (Why ctype.h? It's guaranteed to exist and it + doesn't flood the namespace with stuff the way some other headers do.) */ +#if !defined __GNU_LIBRARY__ +# include +#endif + +#ifndef __THROW +# ifndef __GNUC_PREREQ +# define __GNUC_PREREQ(maj, min) (0) +# endif +# if defined __cplusplus && __GNUC_PREREQ (2,8) +# define __THROW throw () +# else +# define __THROW +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `-', then non-option arguments are treated as + arguments to the option '\1'. This behavior is specific to the GNU + `getopt'. If OPTS begins with `+', or POSIXLY_CORRECT is set in + the environment, then do not permute arguments. */ + +extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) + __THROW; + +#ifndef __need_getopt +extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW; +extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW; + +#endif + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/src/bin/m4/lib/getopt_int.h b/src/bin/m4/lib/getopt_int.h new file mode 100644 index 0000000000..401579fd28 --- /dev/null +++ b/src/bin/m4/lib/getopt_int.h @@ -0,0 +1,131 @@ +/* Internal declarations for getopt. + Copyright (C) 1989-1994,1996-1999,2001,2003,2004 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GETOPT_INT_H +#define _GETOPT_INT_H 1 + +extern int _getopt_internal (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, int __posixly_correct); + + +/* Reentrant versions which can handle parsing multiple argument + vectors at the same time. */ + +/* Data type for reentrant functions. */ +struct _getopt_data +{ + /* These have exactly the same meaning as the corresponding global + variables, except that they are used for the reentrant + versions of getopt. */ + int optind; + int opterr; + int optopt; + char *optarg; + + /* Internal members. */ + + /* True if the internal members have been initialized. */ + int __initialized; + + /* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + char *__nextchar; + + /* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters, or by calling getopt. + + PERMUTE is the default. We permute the contents of ARGV as we + scan, so that eventually all the non-options are at the end. + This allows options to be given in any order, even with programs + that were not written to expect this. + + RETURN_IN_ORDER is an option available to programs that were + written to expect options and other ARGV-elements in any order + and that care about the ordering of the two. We describe each + non-option ARGV-element as if it were the argument of an option + with character code 1. Using `-' as the first character of the + list of option characters selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + + enum + { + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER + } __ordering; + + /* If the POSIXLY_CORRECT environment variable is set + or getopt was called. */ + int __posixly_correct; + + + /* Handle permutation of arguments. */ + + /* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first + of them; `last_nonopt' is the index after the last of them. */ + + int __first_nonopt; + int __last_nonopt; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + int __nonoption_flags_max_len; + int __nonoption_flags_len; +# endif +}; + +/* The initializer is necessary to set OPTIND and OPTERR to their + default values and to clear the initialization flag. */ +#define _GETOPT_DATA_INITIALIZER { 1, 1 } + +extern int _getopt_internal_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, int __posixly_correct, + struct _getopt_data *__data); + +extern int _getopt_long_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + struct _getopt_data *__data); + +extern int _getopt_long_only_r (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, + int *__longind, + struct _getopt_data *__data); + +#endif /* getopt_int.h */ diff --git a/src/bin/m4/lib/gettext.h b/src/bin/m4/lib/gettext.h new file mode 100644 index 0000000000..18c62a4067 --- /dev/null +++ b/src/bin/m4/lib/gettext.h @@ -0,0 +1,269 @@ +/* Convenience header for conditional use of GNU . + Copyright (C) 1995-1998, 2000-2002, 2004-2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include + +/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by + the gettext() and ngettext() macros. This is an alternative to calling + textdomain(), and is useful for libraries. */ +# ifdef DEFAULT_TEXT_DOMAIN +# undef gettext +# define gettext(Msgid) \ + dgettext (DEFAULT_TEXT_DOMAIN, Msgid) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N) +# endif + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is OK. */ +#if defined(__sun) +# include +#endif + +/* Many header files from the libstdc++ coming with g++ 3.3 or newer include + , which chokes if dcgettext is defined as a macro. So include + it now, to make later inclusions of a NOP. */ +#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3) +# include +# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H +# include +# endif +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# define dcgettext(Domainname, Msgid, Category) \ + ((void) (Category), dgettext (Domainname, Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 \ + ? ((void) (Msgid2), (const char *) (Msgid1)) \ + : ((void) (Msgid1), (const char *) (Msgid2))) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) \ + ((void) (Domainname), (const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) \ + ((void) (Domainname), (const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +/* The separator between msgctxt and msgid in a .mo file. */ +#define GETTEXT_CONTEXT_GLUE "\004" + +/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a + MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be + short and rarely need to change. + The letter 'p' stands for 'particular' or 'special'. */ +#ifdef DEFAULT_TEXT_DOMAIN +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#else +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#endif +#define dpgettext(Domainname, Msgctxt, Msgid) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category) +#ifdef DEFAULT_TEXT_DOMAIN +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#else +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#endif +#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +pgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + int category) +{ + const char *translation = dcgettext (domain, msg_ctxt_id, category); + if (translation == msg_ctxt_id) + return msgid; + else + return translation; +} + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +npgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + const char *translation = + dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); + if (translation == msg_ctxt_id || translation == msgid_plural) + return (n == 1 ? msgid : msgid_plural); + else + return translation; +} + +/* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID + can be arbitrary expressions. But for string literals these macros are + less efficient than those above. */ + +#include + +#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \ + (__GNUC__ >= 3 || __GNUG__ >= 2 /* || __STDC_VERSION__ >= 199901L */ ) + +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS +#include +#endif + +#define pgettext_expr(Msgctxt, Msgid) \ + dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES) +#define dpgettext_expr(Domainname, Msgctxt, Msgid) \ + dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcgettext (domain, msg_ctxt_id, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (translation != msg_ctxt_id) + return translation; + } + return msgid; +} + +#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcnpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (!(translation == msg_ctxt_id || translation == msgid_plural)) + return translation; + } + return (n == 1 ? msgid : msgid_plural); +} + +#endif /* _LIBGETTEXT_H */ diff --git a/src/bin/m4/lib/gl_anyhash_list1.h b/src/bin/m4/lib/gl_anyhash_list1.h new file mode 100644 index 0000000000..1f1351e7ac --- /dev/null +++ b/src/bin/m4/lib/gl_anyhash_list1.h @@ -0,0 +1,28 @@ +/* Sequential list data type implemented by a hash table with another list. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Common code of + gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c. */ + +/* Hash table entry. */ +struct gl_hash_entry +{ + struct gl_hash_entry *hash_next; /* chain of entries in same bucket */ + size_t hashcode; /* cache of values' common hash code */ +}; +typedef struct gl_hash_entry * gl_hash_entry_t; diff --git a/src/bin/m4/lib/gl_anyhash_list2.h b/src/bin/m4/lib/gl_anyhash_list2.h new file mode 100644 index 0000000000..d719116866 --- /dev/null +++ b/src/bin/m4/lib/gl_anyhash_list2.h @@ -0,0 +1,127 @@ +/* Sequential list data type implemented by a hash table with another list. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Common code of + gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c. */ + +/* Array of primes, approximately in steps of factor 1.2. + This table was computed by executing the Common Lisp expression + (dotimes (i 244) (format t "nextprime(~D)~%" (ceiling (expt 1.2d0 i)))) + and feeding the result to PARI/gp. */ +static const size_t primes[] = + { + 11, 13, 17, 19, 23, 29, 37, 41, 47, 59, 67, 83, 97, 127, 139, 167, 199, + 239, 293, 347, 419, 499, 593, 709, 853, 1021, 1229, 1471, 1777, 2129, 2543, + 3049, 3659, 4391, 5273, 6323, 7589, 9103, 10937, 13109, 15727, 18899, + 22651, 27179, 32609, 39133, 46957, 56359, 67619, 81157, 97369, 116849, + 140221, 168253, 201907, 242309, 290761, 348889, 418667, 502409, 602887, + 723467, 868151, 1041779, 1250141, 1500181, 1800191, 2160233, 2592277, + 3110741, 3732887, 4479463, 5375371, 6450413, 7740517, 9288589, 11146307, + 13375573, 16050689, 19260817, 23112977, 27735583, 33282701, 39939233, + 47927081, 57512503, 69014987, 82818011, 99381577, 119257891, 143109469, + 171731387, 206077643, 247293161, 296751781, 356102141, 427322587, + 512787097, 615344489, 738413383, 886096061, 1063315271, 1275978331, + 1531174013, 1837408799, 2204890543UL, 2645868653UL, 3175042391UL, + 3810050851UL, +#if SIZE_MAX > 4294967295UL + 4572061027UL, 5486473229UL, 6583767889UL, 7900521449UL, 9480625733UL, + 11376750877UL, 13652101063UL, 16382521261UL, 19659025513UL, 23590830631UL, + 28308996763UL, 33970796089UL, 40764955463UL, 48917946377UL, 58701535657UL, + 70441842749UL, 84530211301UL, 101436253561UL, 121723504277UL, + 146068205131UL, 175281846149UL, 210338215379UL, 252405858521UL, + 302887030151UL, 363464436191UL, 436157323417UL, 523388788231UL, + 628066545713UL, 753679854847UL, 904415825857UL, 1085298991109UL, + 1302358789181UL, 1562830547009UL, 1875396656429UL, 2250475987709UL, + 2700571185239UL, 3240685422287UL, 3888822506759UL, 4666587008147UL, + 5599904409713UL, 6719885291641UL, 8063862349969UL, 9676634819959UL, + 11611961783951UL, 13934354140769UL, 16721224968907UL, 20065469962669UL, + 24078563955191UL, 28894276746229UL, 34673132095507UL, 41607758514593UL, + 49929310217531UL, 59915172260971UL, 71898206713183UL, 86277848055823UL, + 103533417666967UL, 124240101200359UL, 149088121440451UL, 178905745728529UL, + 214686894874223UL, 257624273849081UL, 309149128618903UL, 370978954342639UL, + 445174745211143UL, 534209694253381UL, 641051633104063UL, 769261959724877UL, + 923114351670013UL, 1107737222003791UL, 1329284666404567UL, + 1595141599685509UL, 1914169919622551UL, 2297003903547091UL, + 2756404684256459UL, 3307685621107757UL, 3969222745329323UL, + 4763067294395177UL, 5715680753274209UL, 6858816903929113UL, + 8230580284714831UL, 9876696341657791UL, 11852035609989371UL, + 14222442731987227UL, 17066931278384657UL, 20480317534061597UL, + 24576381040873903UL, 29491657249048679UL, 35389988698858471UL, + 42467986438630267UL, 50961583726356109UL, 61153900471627387UL, + 73384680565952851UL, 88061616679143347UL, 105673940014972061UL, + 126808728017966413UL, 152170473621559703UL, 182604568345871671UL, + 219125482015045997UL, 262950578418055169UL, 315540694101666193UL, + 378648832921999397UL, 454378599506399233UL, 545254319407679131UL, + 654305183289214771UL, 785166219947057701UL, 942199463936469157UL, + 1130639356723763129UL, 1356767228068515623UL, 1628120673682218619UL, + 1953744808418662409UL, 2344493770102394881UL, 2813392524122873857UL, + 3376071028947448339UL, 4051285234736937517UL, 4861542281684325481UL, + 5833850738021191727UL, 7000620885625427969UL, 8400745062750513217UL, + 10080894075300616261UL, 12097072890360739951UL, 14516487468432885797UL, + 17419784962119465179UL, +#endif + SIZE_MAX /* sentinel, to ensure the search terminates */ + }; + +/* Return a suitable prime >= ESTIMATE. */ +static size_t +next_prime (size_t estimate) +{ + size_t i; + + for (i = 0; i < sizeof (primes) / sizeof (primes[0]); i++) + if (primes[i] >= estimate) + return primes[i]; + return SIZE_MAX; /* not a prime, but better than nothing */ +} + +/* Resize the hash table with a new estimated size. */ +static void +hash_resize (gl_list_t list, size_t estimate) +{ + size_t new_size = next_prime (estimate); + + if (new_size > list->table_size) + { + gl_hash_entry_t *old_table = list->table; + /* Allocate the new table. */ + gl_hash_entry_t *new_table = XCALLOC (new_size, gl_hash_entry_t); + size_t i; + + /* Iterate through the entries of the old table. */ + for (i = list->table_size; i > 0; ) + { + gl_hash_entry_t node = old_table[--i]; + + while (node != NULL) + { + gl_hash_entry_t next = node->hash_next; + /* Add the entry to the new table. */ + size_t bucket = node->hashcode % new_size; + node->hash_next = new_table[bucket]; + new_table[bucket] = node; + + node = next; + } + } + + list->table = new_table; + list->table_size = new_size; + free (old_table); + } +} diff --git a/src/bin/m4/lib/gl_anylinked_list1.h b/src/bin/m4/lib/gl_anylinked_list1.h new file mode 100644 index 0000000000..988657e5c4 --- /dev/null +++ b/src/bin/m4/lib/gl_anylinked_list1.h @@ -0,0 +1,49 @@ +/* Sequential list data type implemented by a linked list. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Common code of gl_linked_list.c and gl_linkedhash_list.c. */ + +/* -------------------------- gl_list_t Data Type -------------------------- */ + +/* Concrete list node implementation, valid for this file only. */ +struct gl_list_node_impl +{ +#if WITH_HASHTABLE + struct gl_hash_entry h; /* hash table entry fields; must be first */ +#endif + struct gl_list_node_impl *next; + struct gl_list_node_impl *prev; + const void *value; +}; + +/* Concrete gl_list_impl type, valid for this file only. */ +struct gl_list_impl +{ + struct gl_list_impl_base base; +#if WITH_HASHTABLE + /* A hash table: managed as an array of collision lists. */ + struct gl_hash_entry **table; + size_t table_size; +#endif + /* A circular list anchored at root. + The first node is = root.next, the last node is = root.prev. + The root's value is unused. */ + struct gl_list_node_impl root; + /* Number of list nodes, excluding the root. */ + size_t count; +}; diff --git a/src/bin/m4/lib/gl_anylinked_list2.h b/src/bin/m4/lib/gl_anylinked_list2.h new file mode 100644 index 0000000000..718dfb713a --- /dev/null +++ b/src/bin/m4/lib/gl_anylinked_list2.h @@ -0,0 +1,1045 @@ +/* Sequential list data type implemented by a linked list. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Common code of gl_linked_list.c and gl_linkedhash_list.c. */ + +/* If the symbol SIGNAL_SAFE_LIST is defined, the code is compiled in such + a way that a gl_list_t data structure may be used from within a signal + handler. The operations allowed in the signal handler are: + gl_list_iterator, gl_list_iterator_next, gl_list_iterator_free. + The list and node fields that are therefore accessed from the signal handler + are: + list->root, node->next, node->value. + We are careful to make modifications to these fields only in an order + that maintains the consistency of the list data structure at any moment, + and we use 'volatile' assignments to prevent the compiler from reordering + such assignments. */ +#ifdef SIGNAL_SAFE_LIST +# define ASYNCSAFE(type) *(volatile type *)& +#else +# define ASYNCSAFE(type) +#endif + +/* -------------------------- gl_list_t Data Type -------------------------- */ + +static gl_list_t +gl_linked_create_empty (gl_list_implementation_t implementation, + gl_listelement_equals_fn equals_fn, + gl_listelement_hashcode_fn hashcode_fn, + bool allow_duplicates) +{ + struct gl_list_impl *list = XMALLOC (struct gl_list_impl); + + list->base.vtable = implementation; + list->base.equals_fn = equals_fn; + list->base.hashcode_fn = hashcode_fn; + list->base.allow_duplicates = allow_duplicates; +#if WITH_HASHTABLE + list->table_size = 11; + list->table = XCALLOC (list->table_size, gl_hash_entry_t); +#endif + list->root.next = &list->root; + list->root.prev = &list->root; + list->count = 0; + + return list; +} + +static gl_list_t +gl_linked_create (gl_list_implementation_t implementation, + gl_listelement_equals_fn equals_fn, + gl_listelement_hashcode_fn hashcode_fn, + bool allow_duplicates, + size_t count, const void **contents) +{ + struct gl_list_impl *list = XMALLOC (struct gl_list_impl); + gl_list_node_t tail; + + list->base.vtable = implementation; + list->base.equals_fn = equals_fn; + list->base.hashcode_fn = hashcode_fn; + list->base.allow_duplicates = allow_duplicates; +#if WITH_HASHTABLE + { + size_t estimate = xsum (count, count / 2); /* 1.5 * count */ + if (estimate < 10) + estimate = 10; + list->table_size = next_prime (estimate); + list->table = XCALLOC (list->table_size, gl_hash_entry_t); + } +#endif + list->count = count; + tail = &list->root; + for (; count > 0; contents++, count--) + { + gl_list_node_t node = XMALLOC (struct gl_list_node_impl); + + node->value = *contents; +#if WITH_HASHTABLE + node->h.hashcode = + (list->base.hashcode_fn != NULL + ? list->base.hashcode_fn (node->value) + : (size_t)(uintptr_t) node->value); + + /* Add node to the hash table. */ + add_to_bucket (list, node); +#endif + + /* Add node to the list. */ + node->prev = tail; + tail->next = node; + tail = node; + } + tail->next = &list->root; + list->root.prev = tail; + + return list; +} + +static size_t +gl_linked_size (gl_list_t list) +{ + return list->count; +} + +static const void * +gl_linked_node_value (gl_list_t list, gl_list_node_t node) +{ + return node->value; +} + +static gl_list_node_t +gl_linked_next_node (gl_list_t list, gl_list_node_t node) +{ + return (node->next != &list->root ? node->next : NULL); +} + +static gl_list_node_t +gl_linked_previous_node (gl_list_t list, gl_list_node_t node) +{ + return (node->prev != &list->root ? node->prev : NULL); +} + +static const void * +gl_linked_get_at (gl_list_t list, size_t position) +{ + size_t count = list->count; + gl_list_node_t node; + + if (!(position < count)) + /* Invalid argument. */ + abort (); + /* Here we know count > 0. */ + if (position <= ((count - 1) / 2)) + { + node = list->root.next; + for (; position > 0; position--) + node = node->next; + } + else + { + position = count - 1 - position; + node = list->root.prev; + for (; position > 0; position--) + node = node->prev; + } + return node->value; +} + +static gl_list_node_t +gl_linked_set_at (gl_list_t list, size_t position, const void *elt) +{ + size_t count = list->count; + gl_list_node_t node; + + if (!(position < count)) + /* Invalid argument. */ + abort (); + /* Here we know count > 0. */ + if (position <= ((count - 1) / 2)) + { + node = list->root.next; + for (; position > 0; position--) + node = node->next; + } + else + { + position = count - 1 - position; + node = list->root.prev; + for (; position > 0; position--) + node = node->prev; + } +#if WITH_HASHTABLE + if (elt != node->value) + { + size_t new_hashcode = + (list->base.hashcode_fn != NULL + ? list->base.hashcode_fn (elt) + : (size_t)(uintptr_t) elt); + + if (new_hashcode != node->h.hashcode) + { + remove_from_bucket (list, node); + node->value = elt; + node->h.hashcode = new_hashcode; + add_to_bucket (list, node); + } + else + node->value = elt; + } +#else + node->value = elt; +#endif + return node; +} + +static gl_list_node_t +gl_linked_search_from_to (gl_list_t list, size_t start_index, size_t end_index, + const void *elt) +{ + size_t count = list->count; + + if (!(start_index <= end_index && end_index <= count)) + /* Invalid arguments. */ + abort (); + { +#if WITH_HASHTABLE + size_t hashcode = + (list->base.hashcode_fn != NULL + ? list->base.hashcode_fn (elt) + : (size_t)(uintptr_t) elt); + size_t bucket = hashcode % list->table_size; + gl_listelement_equals_fn equals = list->base.equals_fn; + + if (!list->base.allow_duplicates) + { + /* Look for the first match in the hash bucket. */ + gl_list_node_t found = NULL; + gl_list_node_t node; + + for (node = (gl_list_node_t) list->table[bucket]; + node != NULL; + node = (gl_list_node_t) node->h.hash_next) + if (node->h.hashcode == hashcode + && (equals != NULL + ? equals (elt, node->value) + : elt == node->value)) + { + found = node; + break; + } + if (start_index > 0) + /* Look whether found's index is < start_index. */ + for (node = list->root.next; ; node = node->next) + { + if (node == found) + return NULL; + if (--start_index == 0) + break; + } + if (end_index < count) + /* Look whether found's index is >= end_index. */ + { + end_index = count - end_index; + for (node = list->root.prev; ; node = node->prev) + { + if (node == found) + return NULL; + if (--end_index == 0) + break; + } + } + return found; + } + else + { + /* Look whether there is more than one match in the hash bucket. */ + bool multiple_matches = false; + gl_list_node_t first_match = NULL; + gl_list_node_t node; + + for (node = (gl_list_node_t) list->table[bucket]; + node != NULL; + node = (gl_list_node_t) node->h.hash_next) + if (node->h.hashcode == hashcode + && (equals != NULL + ? equals (elt, node->value) + : elt == node->value)) + { + if (first_match == NULL) + first_match = node; + else + { + multiple_matches = true; + break; + } + } + if (multiple_matches) + { + /* We need the match with the smallest index. But we don't have + a fast mapping node -> index. So we have to walk the list. */ + end_index -= start_index; + node = list->root.next; + for (; start_index > 0; start_index--) + node = node->next; + + for (; + end_index > 0; + node = node->next, end_index--) + if (node->h.hashcode == hashcode + && (equals != NULL + ? equals (elt, node->value) + : elt == node->value)) + return node; + /* The matches must have all been at indices < start_index or + >= end_index. */ + return NULL; + } + else + { + if (start_index > 0) + /* Look whether first_match's index is < start_index. */ + for (node = list->root.next; node != &list->root; node = node->next) + { + if (node == first_match) + return NULL; + if (--start_index == 0) + break; + } + if (end_index < list->count) + /* Look whether first_match's index is >= end_index. */ + { + end_index = list->count - end_index; + for (node = list->root.prev; ; node = node->prev) + { + if (node == first_match) + return NULL; + if (--end_index == 0) + break; + } + } + return first_match; + } + } +#else + gl_listelement_equals_fn equals = list->base.equals_fn; + gl_list_node_t node = list->root.next; + + end_index -= start_index; + for (; start_index > 0; start_index--) + node = node->next; + + if (equals != NULL) + { + for (; end_index > 0; node = node->next, end_index--) + if (equals (elt, node->value)) + return node; + } + else + { + for (; end_index > 0; node = node->next, end_index--) + if (elt == node->value) + return node; + } + return NULL; +#endif + } +} + +static size_t +gl_linked_indexof_from_to (gl_list_t list, size_t start_index, size_t end_index, + const void *elt) +{ + size_t count = list->count; + + if (!(start_index <= end_index && end_index <= count)) + /* Invalid arguments. */ + abort (); + { +#if WITH_HASHTABLE + /* Here the hash table doesn't help much. It only allows us to minimize + the number of equals() calls, by looking up first the node and then + its index. */ + size_t hashcode = + (list->base.hashcode_fn != NULL + ? list->base.hashcode_fn (elt) + : (size_t)(uintptr_t) elt); + size_t bucket = hashcode % list->table_size; + gl_listelement_equals_fn equals = list->base.equals_fn; + gl_list_node_t node; + + /* First step: Look up the node. */ + if (!list->base.allow_duplicates) + { + /* Look for the first match in the hash bucket. */ + for (node = (gl_list_node_t) list->table[bucket]; + node != NULL; + node = (gl_list_node_t) node->h.hash_next) + if (node->h.hashcode == hashcode + && (equals != NULL + ? equals (elt, node->value) + : elt == node->value)) + break; + } + else + { + /* Look whether there is more than one match in the hash bucket. */ + bool multiple_matches = false; + gl_list_node_t first_match = NULL; + + for (node = (gl_list_node_t) list->table[bucket]; + node != NULL; + node = (gl_list_node_t) node->h.hash_next) + if (node->h.hashcode == hashcode + && (equals != NULL + ? equals (elt, node->value) + : elt == node->value)) + { + if (first_match == NULL) + first_match = node; + else + { + multiple_matches = true; + break; + } + } + if (multiple_matches) + { + /* We need the match with the smallest index. But we don't have + a fast mapping node -> index. So we have to walk the list. */ + size_t index; + + index = start_index; + node = list->root.next; + for (; start_index > 0; start_index--) + node = node->next; + + for (; + index < end_index; + node = node->next, index++) + if (node->h.hashcode == hashcode + && (equals != NULL + ? equals (elt, node->value) + : elt == node->value)) + return index; + /* The matches must have all been at indices < start_index or + >= end_index. */ + return (size_t)(-1); + } + node = first_match; + } + + /* Second step: Look up the index of the node. */ + if (node == NULL) + return (size_t)(-1); + else + { + size_t index = 0; + + for (; node->prev != &list->root; node = node->prev) + index++; + + if (index >= start_index && index < end_index) + return index; + else + return (size_t)(-1); + } +#else + gl_listelement_equals_fn equals = list->base.equals_fn; + size_t index = start_index; + gl_list_node_t node = list->root.next; + + for (; start_index > 0; start_index--) + node = node->next; + + if (equals != NULL) + { + for (; + index < end_index; + node = node->next, index++) + if (equals (elt, node->value)) + return index; + } + else + { + for (; + index < end_index; + node = node->next, index++) + if (elt == node->value) + return index; + } + return (size_t)(-1); +#endif + } +} + +static gl_list_node_t +gl_linked_add_first (gl_list_t list, const void *elt) +{ + gl_list_node_t node = XMALLOC (struct gl_list_node_impl); + + ASYNCSAFE(const void *) node->value = elt; +#if WITH_HASHTABLE + node->h.hashcode = + (list->base.hashcode_fn != NULL + ? list->base.hashcode_fn (node->value) + : (size_t)(uintptr_t) node->value); + + /* Add node to the hash table. */ + add_to_bucket (list, node); +#endif + + /* Add node to the list. */ + node->prev = &list->root; + ASYNCSAFE(gl_list_node_t) node->next = list->root.next; + node->next->prev = node; + ASYNCSAFE(gl_list_node_t) list->root.next = node; + list->count++; + +#if WITH_HASHTABLE + hash_resize_after_add (list); +#endif + + return node; +} + +static gl_list_node_t +gl_linked_add_last (gl_list_t list, const void *elt) +{ + gl_list_node_t node = XMALLOC (struct gl_list_node_impl); + + ASYNCSAFE(const void *) node->value = elt; +#if WITH_HASHTABLE + node->h.hashcode = + (list->base.hashcode_fn != NULL + ? list->base.hashcode_fn (node->value) + : (size_t)(uintptr_t) node->value); + + /* Add node to the hash table. */ + add_to_bucket (list, node); +#endif + + /* Add node to the list. */ + ASYNCSAFE(gl_list_node_t) node->next = &list->root; + node->prev = list->root.prev; + ASYNCSAFE(gl_list_node_t) node->prev->next = node; + list->root.prev = node; + list->count++; + +#if WITH_HASHTABLE + hash_resize_after_add (list); +#endif + + return node; +} + +static gl_list_node_t +gl_linked_add_before (gl_list_t list, gl_list_node_t node, const void *elt) +{ + gl_list_node_t new_node = XMALLOC (struct gl_list_node_impl); + + ASYNCSAFE(const void *) new_node->value = elt; +#if WITH_HASHTABLE + new_node->h.hashcode = + (list->base.hashcode_fn != NULL + ? list->base.hashcode_fn (new_node->value) + : (size_t)(uintptr_t) new_node->value); + + /* Add new_node to the hash table. */ + add_to_bucket (list, new_node); +#endif + + /* Add new_node to the list. */ + ASYNCSAFE(gl_list_node_t) new_node->next = node; + new_node->prev = node->prev; + ASYNCSAFE(gl_list_node_t) new_node->prev->next = new_node; + node->prev = new_node; + list->count++; + +#if WITH_HASHTABLE + hash_resize_after_add (list); +#endif + + return new_node; +} + +static gl_list_node_t +gl_linked_add_after (gl_list_t list, gl_list_node_t node, const void *elt) +{ + gl_list_node_t new_node = XMALLOC (struct gl_list_node_impl); + + ASYNCSAFE(const void *) new_node->value = elt; +#if WITH_HASHTABLE + new_node->h.hashcode = + (list->base.hashcode_fn != NULL + ? list->base.hashcode_fn (new_node->value) + : (size_t)(uintptr_t) new_node->value); + + /* Add new_node to the hash table. */ + add_to_bucket (list, new_node); +#endif + + /* Add new_node to the list. */ + new_node->prev = node; + ASYNCSAFE(gl_list_node_t) new_node->next = node->next; + new_node->next->prev = new_node; + ASYNCSAFE(gl_list_node_t) node->next = new_node; + list->count++; + +#if WITH_HASHTABLE + hash_resize_after_add (list); +#endif + + return new_node; +} + +static gl_list_node_t +gl_linked_add_at (gl_list_t list, size_t position, const void *elt) +{ + size_t count = list->count; + gl_list_node_t new_node; + + if (!(position <= count)) + /* Invalid argument. */ + abort (); + + new_node = XMALLOC (struct gl_list_node_impl); + ASYNCSAFE(const void *) new_node->value = elt; +#if WITH_HASHTABLE + new_node->h.hashcode = + (list->base.hashcode_fn != NULL + ? list->base.hashcode_fn (new_node->value) + : (size_t)(uintptr_t) new_node->value); + + /* Add new_node to the hash table. */ + add_to_bucket (list, new_node); +#endif + + /* Add new_node to the list. */ + if (position <= (count / 2)) + { + gl_list_node_t node; + + node = &list->root; + for (; position > 0; position--) + node = node->next; + new_node->prev = node; + ASYNCSAFE(gl_list_node_t) new_node->next = node->next; + new_node->next->prev = new_node; + ASYNCSAFE(gl_list_node_t) node->next = new_node; + } + else + { + gl_list_node_t node; + + position = count - position; + node = &list->root; + for (; position > 0; position--) + node = node->prev; + ASYNCSAFE(gl_list_node_t) new_node->next = node; + new_node->prev = node->prev; + ASYNCSAFE(gl_list_node_t) new_node->prev->next = new_node; + node->prev = new_node; + } + list->count++; + +#if WITH_HASHTABLE + hash_resize_after_add (list); +#endif + + return new_node; +} + +static bool +gl_linked_remove_node (gl_list_t list, gl_list_node_t node) +{ + gl_list_node_t prev; + gl_list_node_t next; + +#if WITH_HASHTABLE + /* Remove node from the hash table. */ + remove_from_bucket (list, node); +#endif + + /* Remove node from the list. */ + prev = node->prev; + next = node->next; + + ASYNCSAFE(gl_list_node_t) prev->next = next; + next->prev = prev; + list->count--; + + free (node); + return true; +} + +static bool +gl_linked_remove_at (gl_list_t list, size_t position) +{ + size_t count = list->count; + gl_list_node_t removed_node; + + if (!(position < count)) + /* Invalid argument. */ + abort (); + /* Here we know count > 0. */ + if (position <= ((count - 1) / 2)) + { + gl_list_node_t node; + gl_list_node_t after_removed; + + node = &list->root; + for (; position > 0; position--) + node = node->next; + removed_node = node->next; + after_removed = node->next->next; + ASYNCSAFE(gl_list_node_t) node->next = after_removed; + after_removed->prev = node; + } + else + { + gl_list_node_t node; + gl_list_node_t before_removed; + + position = count - 1 - position; + node = &list->root; + for (; position > 0; position--) + node = node->prev; + removed_node = node->prev; + before_removed = node->prev->prev; + node->prev = before_removed; + ASYNCSAFE(gl_list_node_t) before_removed->next = node; + } +#if WITH_HASHTABLE + remove_from_bucket (list, removed_node); +#endif + list->count--; + + free (removed_node); + return true; +} + +static bool +gl_linked_remove (gl_list_t list, const void *elt) +{ + gl_list_node_t node = gl_linked_search_from_to (list, 0, list->count, elt); + + if (node != NULL) + return gl_linked_remove_node (list, node); + else + return false; +} + +static void +gl_linked_list_free (gl_list_t list) +{ + gl_list_node_t node; + + for (node = list->root.next; node != &list->root; ) + { + gl_list_node_t next = node->next; + free (node); + node = next; + } +#if WITH_HASHTABLE + free (list->table); +#endif + free (list); +} + +/* --------------------- gl_list_iterator_t Data Type --------------------- */ + +static gl_list_iterator_t +gl_linked_iterator (gl_list_t list) +{ + gl_list_iterator_t result; + + result.vtable = list->base.vtable; + result.list = list; + result.p = list->root.next; + result.q = &list->root; +#ifdef lint + result.i = 0; + result.j = 0; + result.count = 0; +#endif + + return result; +} + +static gl_list_iterator_t +gl_linked_iterator_from_to (gl_list_t list, + size_t start_index, size_t end_index) +{ + gl_list_iterator_t result; + size_t n1, n2, n3; + + if (!(start_index <= end_index && end_index <= list->count)) + /* Invalid arguments. */ + abort (); + result.vtable = list->base.vtable; + result.list = list; + n1 = start_index; + n2 = end_index - start_index; + n3 = list->count - end_index; + /* Find the maximum among n1, n2, n3, so as to reduce the number of + loop iterations to n1 + n2 + n3 - max(n1,n2,n3). */ + if (n1 > n2 && n1 > n3) + { + /* n1 is the maximum, use n2 and n3. */ + gl_list_node_t node; + size_t i; + + node = &list->root; + for (i = n3; i > 0; i--) + node = node->prev; + result.q = node; + for (i = n2; i > 0; i--) + node = node->prev; + result.p = node; + } + else if (n2 > n3) + { + /* n2 is the maximum, use n1 and n3. */ + gl_list_node_t node; + size_t i; + + node = list->root.next; + for (i = n1; i > 0; i--) + node = node->next; + result.p = node; + + node = &list->root; + for (i = n3; i > 0; i--) + node = node->prev; + result.q = node; + } + else + { + /* n3 is the maximum, use n1 and n2. */ + gl_list_node_t node; + size_t i; + + node = list->root.next; + for (i = n1; i > 0; i--) + node = node->next; + result.p = node; + for (i = n2; i > 0; i--) + node = node->next; + result.q = node; + } + +#ifdef lint + result.i = 0; + result.j = 0; + result.count = 0; +#endif + + return result; +} + +static bool +gl_linked_iterator_next (gl_list_iterator_t *iterator, + const void **eltp, gl_list_node_t *nodep) +{ + if (iterator->p != iterator->q) + { + gl_list_node_t node = (gl_list_node_t) iterator->p; + *eltp = node->value; + if (nodep != NULL) + *nodep = node; + iterator->p = node->next; + return true; + } + else + return false; +} + +static void +gl_linked_iterator_free (gl_list_iterator_t *iterator) +{ +} + +/* ---------------------- Sorted gl_list_t Data Type ---------------------- */ + +static gl_list_node_t +gl_linked_sortedlist_search (gl_list_t list, gl_listelement_compar_fn compar, + const void *elt) +{ + gl_list_node_t node; + + for (node = list->root.next; node != &list->root; node = node->next) + { + int cmp = compar (node->value, elt); + + if (cmp > 0) + break; + if (cmp == 0) + return node; + } + return NULL; +} + +static gl_list_node_t +gl_linked_sortedlist_search_from_to (gl_list_t list, + gl_listelement_compar_fn compar, + size_t low, size_t high, + const void *elt) +{ + size_t count = list->count; + + if (!(low <= high && high <= list->count)) + /* Invalid arguments. */ + abort (); + + high -= low; + if (high > 0) + { + /* Here we know low < count. */ + size_t position = low; + gl_list_node_t node; + + if (position <= ((count - 1) / 2)) + { + node = list->root.next; + for (; position > 0; position--) + node = node->next; + } + else + { + position = count - 1 - position; + node = list->root.prev; + for (; position > 0; position--) + node = node->prev; + } + + do + { + int cmp = compar (node->value, elt); + + if (cmp > 0) + break; + if (cmp == 0) + return node; + node = node->next; + } + while (--high > 0); + } + return NULL; +} + +static size_t +gl_linked_sortedlist_indexof (gl_list_t list, gl_listelement_compar_fn compar, + const void *elt) +{ + gl_list_node_t node; + size_t index; + + for (node = list->root.next, index = 0; + node != &list->root; + node = node->next, index++) + { + int cmp = compar (node->value, elt); + + if (cmp > 0) + break; + if (cmp == 0) + return index; + } + return (size_t)(-1); +} + +static size_t +gl_linked_sortedlist_indexof_from_to (gl_list_t list, + gl_listelement_compar_fn compar, + size_t low, size_t high, + const void *elt) +{ + size_t count = list->count; + + if (!(low <= high && high <= list->count)) + /* Invalid arguments. */ + abort (); + + high -= low; + if (high > 0) + { + /* Here we know low < count. */ + size_t index = low; + size_t position = low; + gl_list_node_t node; + + if (position <= ((count - 1) / 2)) + { + node = list->root.next; + for (; position > 0; position--) + node = node->next; + } + else + { + position = count - 1 - position; + node = list->root.prev; + for (; position > 0; position--) + node = node->prev; + } + + do + { + int cmp = compar (node->value, elt); + + if (cmp > 0) + break; + if (cmp == 0) + return index; + node = node->next; + index++; + } + while (--high > 0); + } + return (size_t)(-1); +} + +static gl_list_node_t +gl_linked_sortedlist_add (gl_list_t list, gl_listelement_compar_fn compar, + const void *elt) +{ + gl_list_node_t node; + + for (node = list->root.next; node != &list->root; node = node->next) + if (compar (node->value, elt) >= 0) + return gl_linked_add_before (list, node, elt); + return gl_linked_add_last (list, elt); +} + +static bool +gl_linked_sortedlist_remove (gl_list_t list, gl_listelement_compar_fn compar, + const void *elt) +{ + gl_list_node_t node; + + for (node = list->root.next; node != &list->root; node = node->next) + { + int cmp = compar (node->value, elt); + + if (cmp > 0) + break; + if (cmp == 0) + return gl_linked_remove_node (list, node); + } + return false; +} diff --git a/src/bin/m4/lib/gl_anytree_oset.h b/src/bin/m4/lib/gl_anytree_oset.h new file mode 100644 index 0000000000..b6b50b4bc9 --- /dev/null +++ b/src/bin/m4/lib/gl_anytree_oset.h @@ -0,0 +1,287 @@ +/* Ordered set data type implemented by a binary tree. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Common code of gl_avltree_oset.c and gl_rbtree_oset.c. */ + +/* An item on the stack used for iterating across the elements. */ +typedef struct +{ + gl_oset_node_t node; + bool rightp; +} iterstack_item_t; + +/* A stack used for iterating across the elements. */ +typedef iterstack_item_t iterstack_t[MAXHEIGHT]; + +static gl_oset_t +gl_tree_create_empty (gl_oset_implementation_t implementation, + gl_setelement_compar_fn compar_fn) +{ + struct gl_oset_impl *set = XMALLOC (struct gl_oset_impl); + + set->base.vtable = implementation; + set->base.compar_fn = compar_fn; + set->root = NULL; + set->count = 0; + + return set; +} + +static size_t +gl_tree_size (gl_oset_t set) +{ + return set->count; +} + +static bool +gl_tree_search (gl_oset_t set, const void *elt) +{ + gl_setelement_compar_fn compar = set->base.compar_fn; + gl_oset_node_t node; + + for (node = set->root; node != NULL; ) + { + int cmp = (compar != NULL + ? compar (node->value, elt) + : (node->value > elt ? 1 : + node->value < elt ? -1 : 0)); + + if (cmp < 0) + node = node->right; + else if (cmp > 0) + node = node->left; + else /* cmp == 0 */ + /* We have an element equal to ELT. */ + return true; + } + return false; +} + +static bool +gl_tree_search_atleast (gl_oset_t set, + gl_setelement_threshold_fn threshold_fn, + const void *threshold, + const void **eltp) +{ + gl_oset_node_t node; + + for (node = set->root; node != NULL; ) + { + if (! threshold_fn (node->value, threshold)) + node = node->right; + else + { + /* We have an element >= VALUE. But we need the leftmost such + element. */ + gl_oset_node_t found = node; + node = node->left; + for (; node != NULL; ) + { + if (! threshold_fn (node->value, threshold)) + node = node->right; + else + { + found = node; + node = node->left; + } + } + *eltp = found->value; + return true; + } + } + return false; +} + +static gl_oset_node_t +gl_tree_search_node (gl_oset_t set, const void *elt) +{ + gl_setelement_compar_fn compar = set->base.compar_fn; + gl_oset_node_t node; + + for (node = set->root; node != NULL; ) + { + int cmp = (compar != NULL + ? compar (node->value, elt) + : (node->value > elt ? 1 : + node->value < elt ? -1 : 0)); + + if (cmp < 0) + node = node->right; + else if (cmp > 0) + node = node->left; + else /* cmp == 0 */ + /* We have an element equal to ELT. */ + return node; + } + return NULL; +} + +static bool +gl_tree_add (gl_oset_t set, const void *elt) +{ + gl_setelement_compar_fn compar; + gl_oset_node_t node = set->root; + + if (node == NULL) + { + gl_tree_add_first (set, elt); + return true; + } + + compar = set->base.compar_fn; + + for (;;) + { + int cmp = (compar != NULL + ? compar (node->value, elt) + : (node->value > elt ? 1 : + node->value < elt ? -1 : 0)); + + if (cmp < 0) + { + if (node->right == NULL) + { + gl_tree_add_after (set, node, elt); + return true; + } + node = node->right; + } + else if (cmp > 0) + { + if (node->left == NULL) + { + gl_tree_add_before (set, node, elt); + return true; + } + node = node->left; + } + else /* cmp == 0 */ + return false; + } +} + +static bool +gl_tree_remove (gl_oset_t set, const void *elt) +{ + gl_oset_node_t node = gl_tree_search_node (set, elt); + + if (node != NULL) + return gl_tree_remove_node (set, node); + else + return false; +} + +static void +gl_tree_oset_free (gl_oset_t set) +{ + /* Iterate across all elements in post-order. */ + gl_oset_node_t node = set->root; + iterstack_t stack; + iterstack_item_t *stack_ptr = &stack[0]; + + for (;;) + { + /* Descend on left branch. */ + for (;;) + { + if (node == NULL) + break; + stack_ptr->node = node; + stack_ptr->rightp = false; + node = node->left; + stack_ptr++; + } + /* Climb up again. */ + for (;;) + { + if (stack_ptr == &stack[0]) + goto done_iterate; + stack_ptr--; + node = stack_ptr->node; + if (!stack_ptr->rightp) + break; + /* Free the current node. */ + free (node); + } + /* Descend on right branch. */ + stack_ptr->rightp = true; + node = node->right; + stack_ptr++; + } + done_iterate: + free (set); +} + +/* --------------------- gl_oset_iterator_t Data Type --------------------- */ + +static gl_oset_iterator_t +gl_tree_iterator (gl_oset_t set) +{ + gl_oset_iterator_t result; + gl_oset_node_t node; + + result.vtable = set->base.vtable; + result.set = set; + /* Start node is the leftmost node. */ + node = set->root; + if (node != NULL) + while (node->left != NULL) + node = node->left; + result.p = node; + /* End point is past the rightmost node. */ + result.q = NULL; +#ifdef lint + result.i = 0; + result.j = 0; + result.count = 0; +#endif + + return result; +} + +static bool +gl_tree_iterator_next (gl_oset_iterator_t *iterator, const void **eltp) +{ + if (iterator->p != iterator->q) + { + gl_oset_node_t node = (gl_oset_node_t) iterator->p; + *eltp = node->value; + /* Advance to the next node. */ + if (node->right != NULL) + { + node = node->right; + while (node->left != NULL) + node = node->left; + } + else + { + while (node->parent != NULL && node->parent->right == node) + node = node->parent; + node = node->parent; + } + iterator->p = node; + return true; + } + else + return false; +} + +static void +gl_tree_iterator_free (gl_oset_iterator_t *iterator) +{ +} diff --git a/src/bin/m4/lib/gl_avltree_oset.c b/src/bin/m4/lib/gl_avltree_oset.c new file mode 100644 index 0000000000..1e38b984f3 --- /dev/null +++ b/src/bin/m4/lib/gl_avltree_oset.c @@ -0,0 +1,572 @@ +/* Ordered set data type implemented by a binary tree. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "gl_avltree_oset.h" + +#include + +#include "xalloc.h" + +/* An AVL tree is a binary tree where + 1. The height of each node is calculated as + heightof(node) = 1 + max (heightof(node.left), heightof(node.right)). + 2. The heights of the subtrees of each node differ by at most 1: + | heightof(right) - heightof(left) | <= 1. + 3. The index of the elements in the node.left subtree are smaller than + the index of node. + The index of the elements in the node.right subtree are larger than + the index of node. + */ + +/* -------------------------- gl_oset_t Data Type -------------------------- */ + +/* Tree node implementation, valid for this file only. */ +struct gl_oset_node_impl +{ + struct gl_oset_node_impl *left; /* left branch, or NULL */ + struct gl_oset_node_impl *right; /* right branch, or NULL */ + /* Parent pointer, or NULL. The parent pointer is not needed for most + operations. It is needed so that a gl_oset_node_t can be returned + without memory allocation, on which the functions gl_oset_remove_node, + gl_oset_add_before, gl_oset_add_after can be implemented. */ + struct gl_oset_node_impl *parent; + int balance; /* heightof(right) - heightof(left), + always = -1 or 0 or 1 */ + const void *value; +}; +typedef struct gl_oset_node_impl * gl_oset_node_t; + +/* Concrete gl_oset_impl type, valid for this file only. */ +struct gl_oset_impl +{ + struct gl_oset_impl_base base; + struct gl_oset_node_impl *root; /* root node or NULL */ + size_t count; /* number of nodes */ +}; + +/* An AVL tree of height h has at least F_(h+2) [Fibonacci number] and at most + 2^h - 1 elements. So, h <= 84 (because a tree of height h >= 85 would have + at least F_87 elements, and because even on 64-bit machines, + sizeof (gl_oset_node_impl) * F_87 > 2^64 + this would exceed the address space of the machine. */ +#define MAXHEIGHT 83 + +/* Ensure the tree is balanced, after an insertion or deletion operation. + The height of NODE is incremented by HEIGHT_DIFF (1 or -1). + PARENT = NODE->parent. (NODE can also be NULL. But PARENT is non-NULL.) + Rotation operations are performed starting at PARENT (not NODE itself!). */ +static void +rebalance (gl_oset_t set, + gl_oset_node_t node, int height_diff, gl_oset_node_t parent) +{ + for (;;) + { + gl_oset_node_t child; + int previous_balance; + int balance_diff; + gl_oset_node_t nodeleft; + gl_oset_node_t noderight; + + child = node; + node = parent; + + previous_balance = node->balance; + + /* The balance of NODE is incremented by BALANCE_DIFF: +1 if the right + branch's height has increased by 1 or the left branch's height has + decreased by 1, -1 if the right branch's height has decreased by 1 or + the left branch's height has increased by 1, 0 if no height change. */ + if (node->left != NULL || node->right != NULL) + balance_diff = (child == node->right ? height_diff : -height_diff); + else + /* Special case where above formula doesn't work, because the caller + didn't tell whether node's left or right branch shrunk from height 1 + to NULL. */ + balance_diff = - previous_balance; + + node->balance += balance_diff; + if (balance_diff == previous_balance) + { + /* node->balance is outside the range [-1,1]. Must rotate. */ + gl_oset_node_t *nodep; + + if (node->parent == NULL) + /* node == set->root */ + nodep = &set->root; + else if (node->parent->left == node) + nodep = &node->parent->left; + else if (node->parent->right == node) + nodep = &node->parent->right; + else + abort (); + + nodeleft = node->left; + noderight = node->right; + + if (balance_diff < 0) + { + /* node->balance = -2. The subtree is heavier on the left side. + Rotate from left to right: + + * + / \ + h+2 h + */ + gl_oset_node_t nodeleftright = nodeleft->right; + if (nodeleft->balance <= 0) + { + /* + * h+2|h+3 + / \ / \ + h+2 h --> / h+1|h+2 + / \ | / \ + h+1 h|h+1 h+1 h|h+1 h + */ + node->left = nodeleftright; + nodeleft->right = node; + + nodeleft->parent = node->parent; + node->parent = nodeleft; + if (nodeleftright != NULL) + nodeleftright->parent = node; + + nodeleft->balance += 1; + node->balance = - nodeleft->balance; + + *nodep = nodeleft; + height_diff = (height_diff < 0 + ? /* noderight's height had been decremented from + h+1 to h. The subtree's height changes from + h+3 to h+2|h+3. */ + nodeleft->balance - 1 + : /* nodeleft's height had been incremented from + h+1 to h+2. The subtree's height changes from + h+2 to h+2|h+3. */ + nodeleft->balance); + } + else + { + /* + * h+2 + / \ / \ + h+2 h --> h+1 h+1 + / \ / \ / \ + h h+1 h L R h + / \ + L R + + */ + gl_oset_node_t L = nodeleft->right = nodeleftright->left; + gl_oset_node_t R = node->left = nodeleftright->right; + nodeleftright->left = nodeleft; + nodeleftright->right = node; + + nodeleftright->parent = node->parent; + if (L != NULL) + L->parent = nodeleft; + if (R != NULL) + R->parent = node; + nodeleft->parent = nodeleftright; + node->parent = nodeleftright; + + nodeleft->balance = (nodeleftright->balance > 0 ? -1 : 0); + node->balance = (nodeleftright->balance < 0 ? 1 : 0); + nodeleftright->balance = 0; + + *nodep = nodeleftright; + height_diff = (height_diff < 0 + ? /* noderight's height had been decremented from + h+1 to h. The subtree's height changes from + h+3 to h+2. */ + -1 + : /* nodeleft's height had been incremented from + h+1 to h+2. The subtree's height changes from + h+2 to h+2. */ + 0); + } + } + else + { + /* node->balance = 2. The subtree is heavier on the right side. + Rotate from right to left: + + * + / \ + h h+2 + */ + gl_oset_node_t noderightleft = noderight->left; + if (noderight->balance >= 0) + { + /* + * h+2|h+3 + / \ / \ + h h+2 --> h+1|h+2 \ + / \ / \ | + h|h+1 h+1 h h|h+1 h+1 + */ + node->right = noderightleft; + noderight->left = node; + + noderight->parent = node->parent; + node->parent = noderight; + if (noderightleft != NULL) + noderightleft->parent = node; + + noderight->balance -= 1; + node->balance = - noderight->balance; + + *nodep = noderight; + height_diff = (height_diff < 0 + ? /* nodeleft's height had been decremented from + h+1 to h. The subtree's height changes from + h+3 to h+2|h+3. */ + - noderight->balance - 1 + : /* noderight's height had been incremented from + h+1 to h+2. The subtree's height changes from + h+2 to h+2|h+3. */ + - noderight->balance); + } + else + { + /* + * h+2 + / \ / \ + h h+2 --> h+1 h+1 + / \ / \ / \ + h+1 h h L R h + / \ + L R + + */ + gl_oset_node_t L = node->right = noderightleft->left; + gl_oset_node_t R = noderight->left = noderightleft->right; + noderightleft->left = node; + noderightleft->right = noderight; + + noderightleft->parent = node->parent; + if (L != NULL) + L->parent = node; + if (R != NULL) + R->parent = noderight; + node->parent = noderightleft; + noderight->parent = noderightleft; + + node->balance = (noderightleft->balance > 0 ? -1 : 0); + noderight->balance = (noderightleft->balance < 0 ? 1 : 0); + noderightleft->balance = 0; + + *nodep = noderightleft; + height_diff = (height_diff < 0 + ? /* nodeleft's height had been decremented from + h+1 to h. The subtree's height changes from + h+3 to h+2. */ + -1 + : /* noderight's height had been incremented from + h+1 to h+2. The subtree's height changes from + h+2 to h+2. */ + 0); + } + } + node = *nodep; + } + else + { + /* No rotation needed. Only propagation of the height change to the + next higher level. */ + if (height_diff < 0) + height_diff = (previous_balance == 0 ? 0 : -1); + else + height_diff = (node->balance == 0 ? 0 : 1); + } + + if (height_diff == 0) + break; + + parent = node->parent; + if (parent == NULL) + break; + } +} + +static gl_oset_node_t +gl_tree_add_first (gl_oset_t set, const void *elt) +{ + /* Create new node. */ + gl_oset_node_t new_node = XMALLOC (struct gl_oset_node_impl); + + new_node->left = NULL; + new_node->right = NULL; + new_node->balance = 0; + new_node->value = elt; + + /* Add it to the tree. */ + if (set->root == NULL) + { + set->root = new_node; + new_node->parent = NULL; + } + else + { + gl_oset_node_t node; + + for (node = set->root; node->left != NULL; ) + node = node->left; + + node->left = new_node; + new_node->parent = node; + node->balance--; + + /* Rebalance. */ + if (node->right == NULL && node->parent != NULL) + rebalance (set, node, 1, node->parent); + } + + set->count++; + return new_node; +} + +static gl_oset_node_t +gl_tree_add_before (gl_oset_t set, gl_oset_node_t node, const void *elt) +{ + /* Create new node. */ + gl_oset_node_t new_node = XMALLOC (struct gl_oset_node_impl); + bool height_inc; + + new_node->left = NULL; + new_node->right = NULL; + new_node->balance = 0; + new_node->value = elt; + + /* Add it to the tree. */ + if (node->left == NULL) + { + node->left = new_node; + node->balance--; + height_inc = (node->right == NULL); + } + else + { + for (node = node->left; node->right != NULL; ) + node = node->right; + node->right = new_node; + node->balance++; + height_inc = (node->left == NULL); + } + new_node->parent = node; + + /* Rebalance. */ + if (height_inc && node->parent != NULL) + rebalance (set, node, 1, node->parent); + + set->count++; + return new_node; +} + +static gl_oset_node_t +gl_tree_add_after (gl_oset_t set, gl_oset_node_t node, const void *elt) +{ + /* Create new node. */ + gl_oset_node_t new_node = XMALLOC (struct gl_oset_node_impl); + bool height_inc; + + new_node->left = NULL; + new_node->right = NULL; + new_node->balance = 0; + new_node->value = elt; + + /* Add it to the tree. */ + if (node->right == NULL) + { + node->right = new_node; + node->balance++; + height_inc = (node->left == NULL); + } + else + { + for (node = node->right; node->left != NULL; ) + node = node->left; + node->left = new_node; + node->balance--; + height_inc = (node->right == NULL); + } + new_node->parent = node; + + /* Rebalance. */ + if (height_inc && node->parent != NULL) + rebalance (set, node, 1, node->parent); + + set->count++; + return new_node; +} + +static bool +gl_tree_remove_node (gl_oset_t set, gl_oset_node_t node) +{ + gl_oset_node_t parent = node->parent; + + if (node->left == NULL) + { + /* Replace node with node->right. */ + gl_oset_node_t child = node->right; + + if (child != NULL) + child->parent = parent; + if (parent == NULL) + set->root = child; + else + { + if (parent->left == node) + parent->left = child; + else /* parent->right == node */ + parent->right = child; + + rebalance (set, child, -1, parent); + } + } + else if (node->right == NULL) + { + /* It is not absolutely necessary to treat this case. But the more + general case below is more complicated, hence slower. */ + /* Replace node with node->left. */ + gl_oset_node_t child = node->left; + + child->parent = parent; + if (parent == NULL) + set->root = child; + else + { + if (parent->left == node) + parent->left = child; + else /* parent->right == node */ + parent->right = child; + + rebalance (set, child, -1, parent); + } + } + else + { + /* Replace node with the rightmost element of the node->left subtree. */ + gl_oset_node_t subst; + gl_oset_node_t subst_parent; + gl_oset_node_t child; + + for (subst = node->left; subst->right != NULL; ) + subst = subst->right; + + subst_parent = subst->parent; + + child = subst->left; + + /* The case subst_parent == node is special: If we do nothing special, + we get confusion about node->left, subst->left and child->parent. + subst_parent == node + <==> The 'for' loop above terminated immediately. + <==> subst == subst_parent->left + [otherwise subst == subst_parent->right] + In this case, we would need to first set + child->parent = node; node->left = child; + and later - when we copy subst into node's position - again + child->parent = subst; subst->left = child; + Altogether a no-op. */ + if (subst_parent != node) + { + if (child != NULL) + child->parent = subst_parent; + subst_parent->right = child; + } + + /* Copy subst into node's position. + (This is safer than to copy subst's value into node, keep node in + place, and free subst.) */ + if (subst_parent != node) + { + subst->left = node->left; + subst->left->parent = subst; + } + subst->right = node->right; + subst->right->parent = subst; + subst->balance = node->balance; + subst->parent = parent; + if (parent == NULL) + set->root = subst; + else if (parent->left == node) + parent->left = subst; + else /* parent->right == node */ + parent->right = subst; + + /* Rebalancing starts at child's parent, that is subst_parent - + except when subst_parent == node. In this case, we need to use + its replacement, subst. */ + rebalance (set, child, -1, subst_parent != node ? subst_parent : subst); + } + + set->count--; + free (node); + return true; +} + +/* Generic binary tree code. */ +#include "gl_anytree_oset.h" + +/* For debugging. */ +static unsigned int +check_invariants (gl_oset_node_t node, gl_oset_node_t parent, size_t *counterp) +{ + unsigned int left_height = + (node->left != NULL ? check_invariants (node->left, node, counterp) : 0); + unsigned int right_height = + (node->right != NULL ? check_invariants (node->right, node, counterp) : 0); + int balance = (int)right_height - (int)left_height; + + if (!(node->parent == parent)) + abort (); + if (!(balance >= -1 && balance <= 1)) + abort (); + if (!(node->balance == balance)) + abort (); + + (*counterp)++; + + return 1 + (left_height > right_height ? left_height : right_height); +} +void +gl_avltree_oset_check_invariants (gl_oset_t set) +{ + size_t counter = 0; + if (set->root != NULL) + check_invariants (set->root, NULL, &counter); + if (!(set->count == counter)) + abort (); +} + +const struct gl_oset_implementation gl_avltree_oset_implementation = + { + gl_tree_create_empty, + gl_tree_size, + gl_tree_search, + gl_tree_search_atleast, + gl_tree_add, + gl_tree_remove, + gl_tree_oset_free, + gl_tree_iterator, + gl_tree_iterator_next, + gl_tree_iterator_free + }; diff --git a/src/bin/m4/lib/gl_avltree_oset.h b/src/bin/m4/lib/gl_avltree_oset.h new file mode 100644 index 0000000000..27f75d22d0 --- /dev/null +++ b/src/bin/m4/lib/gl_avltree_oset.h @@ -0,0 +1,35 @@ +/* Ordered set data type implemented by a binary tree. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GL_AVLTREE_OSET_H +#define _GL_AVLTREE_OSET_H + +#include "gl_oset.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct gl_oset_implementation gl_avltree_oset_implementation; +#define GL_AVLTREE_OSET &gl_avltree_oset_implementation + +#ifdef __cplusplus +} +#endif + +#endif /* _GL_AVLTREE_OSET_H */ diff --git a/src/bin/m4/lib/gl_linkedhash_list.c b/src/bin/m4/lib/gl_linkedhash_list.c new file mode 100644 index 0000000000..89173f51bc --- /dev/null +++ b/src/bin/m4/lib/gl_linkedhash_list.c @@ -0,0 +1,123 @@ +/* Sequential list data type implemented by a hash table with a linked list. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "gl_linkedhash_list.h" + +#include /* for SIZE_MAX */ +#include + +#include "xalloc.h" +#include "xsize.h" + +#ifndef uintptr_t +# define uintptr_t unsigned long +#endif + +#define WITH_HASHTABLE 1 + +/* -------------------------- gl_list_t Data Type -------------------------- */ + +/* Generic hash-table code. */ +#include "gl_anyhash_list1.h" + +/* Generic linked list code. */ +#include "gl_anylinked_list1.h" + +/* Generic hash-table code. */ +#include "gl_anyhash_list2.h" + +/* Resize the hash table if needed, after list->count was incremented. */ +static inline void +hash_resize_after_add (gl_list_t list) +{ + size_t count = list->count; + size_t estimate = xsum (count, count / 2); /* 1.5 * count */ + if (estimate > list->table_size) + hash_resize (list, estimate); +} + +/* Add a node to the hash table structure. */ +static inline void +add_to_bucket (gl_list_t list, gl_list_node_t node) +{ + size_t bucket = node->h.hashcode % list->table_size; + + node->h.hash_next = list->table[bucket]; + list->table[bucket] = &node->h; +} + +/* Remove a node from the hash table structure. */ +static inline void +remove_from_bucket (gl_list_t list, gl_list_node_t node) +{ + size_t bucket = node->h.hashcode % list->table_size; + gl_hash_entry_t *p; + + for (p = &list->table[bucket]; ; p = &(*p)->hash_next) + { + if (*p == &node->h) + { + *p = node->h.hash_next; + break; + } + if (*p == NULL) + /* node is not in the right bucket. Did the hash codes + change inadvertently? */ + abort (); + } +} + +/* Generic linked list code. */ +#include "gl_anylinked_list2.h" + + +const struct gl_list_implementation gl_linkedhash_list_implementation = + { + gl_linked_create_empty, + gl_linked_create, + gl_linked_size, + gl_linked_node_value, + gl_linked_next_node, + gl_linked_previous_node, + gl_linked_get_at, + gl_linked_set_at, + gl_linked_search_from_to, + gl_linked_indexof_from_to, + gl_linked_add_first, + gl_linked_add_last, + gl_linked_add_before, + gl_linked_add_after, + gl_linked_add_at, + gl_linked_remove_node, + gl_linked_remove_at, + gl_linked_remove, + gl_linked_list_free, + gl_linked_iterator, + gl_linked_iterator_from_to, + gl_linked_iterator_next, + gl_linked_iterator_free, + gl_linked_sortedlist_search, + gl_linked_sortedlist_search_from_to, + gl_linked_sortedlist_indexof, + gl_linked_sortedlist_indexof_from_to, + gl_linked_sortedlist_add, + gl_linked_sortedlist_remove + }; diff --git a/src/bin/m4/lib/gl_linkedhash_list.h b/src/bin/m4/lib/gl_linkedhash_list.h new file mode 100644 index 0000000000..d3e71ed95d --- /dev/null +++ b/src/bin/m4/lib/gl_linkedhash_list.h @@ -0,0 +1,35 @@ +/* Sequential list data type implemented by a hash table with a linked list. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GL_LINKEDHASH_LIST_H +#define _GL_LINKEDHASH_LIST_H + +#include "gl_list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct gl_list_implementation gl_linkedhash_list_implementation; +#define GL_LINKEDHASH_LIST &gl_linkedhash_list_implementation + +#ifdef __cplusplus +} +#endif + +#endif /* _GL_LINKEDHASH_LIST_H */ diff --git a/src/bin/m4/lib/gl_list.c b/src/bin/m4/lib/gl_list.c new file mode 100644 index 0000000000..cbc765a944 --- /dev/null +++ b/src/bin/m4/lib/gl_list.c @@ -0,0 +1,272 @@ +/* Abstract sequential list data type. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "gl_list.h" + +#if !HAVE_INLINE + +/* Define all functions of this file as inline accesses to the + struct gl_list_implementation. + Use #define to avoid a warning because of extern vs. static. */ + +gl_list_t +gl_list_create_empty (gl_list_implementation_t implementation, + gl_listelement_equals_fn equals_fn, + gl_listelement_hashcode_fn hashcode_fn, + bool allow_duplicates) +{ + return implementation->create_empty (implementation, equals_fn, hashcode_fn, + allow_duplicates); +} + +gl_list_t +gl_list_create (gl_list_implementation_t implementation, + gl_listelement_equals_fn equals_fn, + gl_listelement_hashcode_fn hashcode_fn, + bool allow_duplicates, + size_t count, const void **contents) +{ + return implementation->create (implementation, equals_fn, hashcode_fn, + allow_duplicates, count, contents); +} + +size_t +gl_list_size (gl_list_t list) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->size (list); +} + +const void * +gl_list_node_value (gl_list_t list, gl_list_node_t node) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->node_value (list, node); +} + +gl_list_node_t +gl_list_next_node (gl_list_t list, gl_list_node_t node) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->next_node (list, node); +} + +gl_list_node_t +gl_list_previous_node (gl_list_t list, gl_list_node_t node) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->previous_node (list, node); +} + +const void * +gl_list_get_at (gl_list_t list, size_t position) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->get_at (list, position); +} + +gl_list_node_t +gl_list_set_at (gl_list_t list, size_t position, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->set_at (list, position, elt); +} + +gl_list_node_t +gl_list_search (gl_list_t list, const void *elt) +{ + size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); + return ((const struct gl_list_impl_base *) list)->vtable + ->search_from_to (list, 0, size, elt); +} + +gl_list_node_t +gl_list_search_from (gl_list_t list, size_t start_index, const void *elt) +{ + size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); + return ((const struct gl_list_impl_base *) list)->vtable + ->search_from_to (list, start_index, size, elt); +} + +gl_list_node_t +gl_list_search_from_to (gl_list_t list, size_t start_index, size_t end_index, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->search_from_to (list, start_index, end_index, elt); +} + +size_t +gl_list_indexof (gl_list_t list, const void *elt) +{ + size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); + return ((const struct gl_list_impl_base *) list)->vtable + ->indexof_from_to (list, 0, size, elt); +} + +size_t +gl_list_indexof_from (gl_list_t list, size_t start_index, const void *elt) +{ + size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); + return ((const struct gl_list_impl_base *) list)->vtable + ->indexof_from_to (list, start_index, size, elt); +} + +size_t +gl_list_indexof_from_to (gl_list_t list, size_t start_index, size_t end_index, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->indexof_from_to (list, start_index, end_index, elt); +} + +gl_list_node_t +gl_list_add_first (gl_list_t list, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->add_first (list, elt); +} + +gl_list_node_t +gl_list_add_last (gl_list_t list, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->add_last (list, elt); +} + +gl_list_node_t +gl_list_add_before (gl_list_t list, gl_list_node_t node, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->add_before (list, node, elt); +} + +gl_list_node_t +gl_list_add_after (gl_list_t list, gl_list_node_t node, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->add_after (list, node, elt); +} + +gl_list_node_t +gl_list_add_at (gl_list_t list, size_t position, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->add_at (list, position, elt); +} + +bool +gl_list_remove_node (gl_list_t list, gl_list_node_t node) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->remove_node (list, node); +} + +bool +gl_list_remove_at (gl_list_t list, size_t position) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->remove_at (list, position); +} + +bool +gl_list_remove (gl_list_t list, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->remove (list, elt); +} + +void +gl_list_free (gl_list_t list) +{ + ((const struct gl_list_impl_base *) list)->vtable->list_free (list); +} + +gl_list_iterator_t +gl_list_iterator (gl_list_t list) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->iterator (list); +} + +gl_list_iterator_t +gl_list_iterator_from_to (gl_list_t list, size_t start_index, size_t end_index) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->iterator_from_to (list, start_index, end_index); +} + +bool +gl_list_iterator_next (gl_list_iterator_t *iterator, + const void **eltp, gl_list_node_t *nodep) +{ + return iterator->vtable->iterator_next (iterator, eltp, nodep); +} + +void +gl_list_iterator_free (gl_list_iterator_t *iterator) +{ + iterator->vtable->iterator_free (iterator); +} + +gl_list_node_t +gl_sortedlist_search (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_search (list, compar, elt); +} + +gl_list_node_t +gl_sortedlist_search_from_to (gl_list_t list, gl_listelement_compar_fn compar, size_t start_index, size_t end_index, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_search_from_to (list, compar, start_index, end_index, + elt); +} + +size_t +gl_sortedlist_indexof (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_indexof (list, compar, elt); +} + +size_t +gl_sortedlist_indexof_from_to (gl_list_t list, gl_listelement_compar_fn compar, size_t start_index, size_t end_index, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_indexof_from_to (list, compar, start_index, end_index, + elt); +} + +gl_list_node_t +gl_sortedlist_add (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_add (list, compar, elt); +} + +bool +gl_sortedlist_remove (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_remove (list, compar, elt); +} + +#endif diff --git a/src/bin/m4/lib/gl_list.h b/src/bin/m4/lib/gl_list.h new file mode 100644 index 0000000000..b97a6a42a3 --- /dev/null +++ b/src/bin/m4/lib/gl_list.h @@ -0,0 +1,724 @@ +/* Abstract sequential list data type. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GL_LIST_H +#define _GL_LIST_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* gl_list is an abstract list data type. It can contain any number of + objects ('void *' or 'const void *' pointers) in any given order. + Duplicates are allowed, but can optionally be forbidden. + + There are several implementations of this list datatype, optimized for + different operations or for memory. You can start using the simplest list + implementation, GL_ARRAY_LIST, and switch to a different implementation + later, when you realize which operations are performed the most frequently. + The API of the different implementations is exactly the same; when + switching to a different implementation, you only have to change the + gl_list_create call. + + The implementations are: + GL_ARRAY_LIST a growable array + GL_CARRAY_LIST a growable circular array + GL_LINKED_LIST a linked list + GL_AVLTREE_LIST a binary tree (AVL tree) + GL_RBTREE_LIST a binary tree (red-black tree) + GL_LINKEDHASH_LIST a hash table with a linked list + GL_AVLTREEHASH_LIST a hash table with a binary tree (AVL tree) + GL_RBTREEHASH_LIST a hash table with a binary tree (red-black tree) + + The memory consumption is asymptotically the same: O(1) for every object + in the list. When looking more closely at the average memory consumed + for an object, GL_ARRAY_LIST is the most compact representation, and + GL_LINKEDHASH_LIST and GL_TREEHASH_LIST need more memory. + + The guaranteed average performance of the operations is, for a list of + n elements: + + Operation ARRAY LINKED TREE LINKEDHASH TREEHASH + CARRAY with|without with|without + duplicates duplicates + + gl_list_size O(1) O(1) O(1) O(1) O(1) + gl_list_node_value O(1) O(1) O(1) O(1) O(1) + gl_list_next_node O(1) O(1) O(log n) O(1) O(log n) + gl_list_previous_node O(1) O(1) O(log n) O(1) O(log n) + gl_list_get_at O(1) O(n) O(log n) O(n) O(log n) + gl_list_set_at O(1) O(n) O(log n) O(n) O((log n)²)/O(log n) + gl_list_search O(n) O(n) O(n) O(n)/O(1) O(log n)/O(1) + gl_list_search_from O(n) O(n) O(n) O(n)/O(1) O((log n)²)/O(log n) + gl_list_search_from_to O(n) O(n) O(n) O(n)/O(1) O((log n)²)/O(log n) + gl_list_indexof O(n) O(n) O(n) O(n) O(log n) + gl_list_indexof_from O(n) O(n) O(n) O(n) O((log n)²)/O(log n) + gl_list_indexof_from_to O(n) O(n) O(n) O(n) O((log n)²)/O(log n) + gl_list_add_first O(n)/O(1) O(1) O(log n) O(1) O((log n)²)/O(log n) + gl_list_add_last O(1) O(1) O(log n) O(1) O((log n)²)/O(log n) + gl_list_add_before O(n) O(1) O(log n) O(1) O((log n)²)/O(log n) + gl_list_add_after O(n) O(1) O(log n) O(1) O((log n)²)/O(log n) + gl_list_add_at O(n) O(n) O(log n) O(n) O((log n)²)/O(log n) + gl_list_remove_node O(n) O(1) O(log n) O(n)/O(1) O((log n)²)/O(log n) + gl_list_remove_at O(n) O(n) O(log n) O(n) O((log n)²)/O(log n) + gl_list_remove O(n) O(n) O(n) O(n)/O(1) O((log n)²)/O(log n) + gl_list_iterator O(1) O(1) O(log n) O(1) O(log n) + gl_list_iterator_from_to O(1) O(n) O(log n) O(n) O(log n) + gl_list_iterator_next O(1) O(1) O(log n) O(1) O(log n) + gl_sortedlist_search O(log n) O(n) O(log n) O(n) O(log n) + gl_sortedlist_search_from O(log n) O(n) O(log n) O(n) O(log n) + gl_sortedlist_indexof O(log n) O(n) O(log n) O(n) O(log n) + gl_sortedlist_indexof_fro O(log n) O(n) O(log n) O(n) O(log n) + gl_sortedlist_add O(n) O(n) O(log n) O(n) O((log n)²)/O(log n) + gl_sortedlist_remove O(n) O(n) O(log n) O(n) O((log n)²)/O(log n) + */ + +/* -------------------------- gl_list_t Data Type -------------------------- */ + +/* Type of function used to compare two elements. + NULL denotes pointer comparison. */ +typedef bool (*gl_listelement_equals_fn) (const void *elt1, const void *elt2); + +/* Type of function used to compute a hash code. + NULL denotes a function that depends only on the pointer itself. */ +typedef size_t (*gl_listelement_hashcode_fn) (const void *elt); + +struct gl_list_impl; +/* Type representing an entire list. */ +typedef struct gl_list_impl * gl_list_t; + +struct gl_list_node_impl; +/* Type representing the position of an element in the list, in a way that + is more adapted to the list implementation than a plain index. + Note: It is invalidated by insertions and removals! */ +typedef struct gl_list_node_impl * gl_list_node_t; + +struct gl_list_implementation; +/* Type representing a list datatype implementation. */ +typedef const struct gl_list_implementation * gl_list_implementation_t; + +/* Create an empty list. + IMPLEMENTATION is one of GL_ARRAY_LIST, GL_CARRAY_LIST, GL_LINKED_LIST, + GL_AVLTREE_LIST, GL_RBTREE_LIST, GL_LINKEDHASH_LIST, GL_AVLTREEHASH_LIST, + GL_RBTREEHASH_LIST. + EQUALS_FN is an element comparison function or NULL. + HASHCODE_FN is an element hash code function or NULL. + ALLOW_DUPLICATES is false if duplicate elements shall not be allowed in + the list. The implementation may verify this at runtime. */ +extern gl_list_t gl_list_create_empty (gl_list_implementation_t implementation, + gl_listelement_equals_fn equals_fn, + gl_listelement_hashcode_fn hashcode_fn, + bool allow_duplicates); + +/* Create a list with given contents. + IMPLEMENTATION is one of GL_ARRAY_LIST, GL_CARRAY_LIST, GL_LINKED_LIST, + GL_AVLTREE_LIST, GL_RBTREE_LIST, GL_LINKEDHASH_LIST, GL_AVLTREEHASH_LIST, + GL_RBTREEHASH_LIST. + EQUALS_FN is an element comparison function or NULL. + HASHCODE_FN is an element hash code function or NULL. + ALLOW_DUPLICATES is false if duplicate elements shall not be allowed in + the list. The implementation may verify this at runtime. + COUNT is the number of initial elements. + CONTENTS[0..COUNT-1] is the initial contents. */ +extern gl_list_t gl_list_create (gl_list_implementation_t implementation, + gl_listelement_equals_fn equals_fn, + gl_listelement_hashcode_fn hashcode_fn, + bool allow_duplicates, + size_t count, const void **contents); + +/* Return the current number of elements in a list. */ +extern size_t gl_list_size (gl_list_t list); + +/* Return the element value represented by a list node. */ +extern const void * gl_list_node_value (gl_list_t list, gl_list_node_t node); + +/* Return the node immediately after the given node in the list, or NULL + if the given node is the last (rightmost) one in the list. */ +extern gl_list_node_t gl_list_next_node (gl_list_t list, gl_list_node_t node); + +/* Return the node immediately before the given node in the list, or NULL + if the given node is the first (leftmost) one in the list. */ +extern gl_list_node_t gl_list_previous_node (gl_list_t list, gl_list_node_t node); + +/* Return the element at a given position in the list. + POSITION must be >= 0 and < gl_list_size (list). */ +extern const void * gl_list_get_at (gl_list_t list, size_t position); + +/* Replace the element at a given position in the list. + POSITION must be >= 0 and < gl_list_size (list). + Return its node. */ +extern gl_list_node_t gl_list_set_at (gl_list_t list, size_t position, + const void *elt); + +/* Search whether an element is already in the list. + Return its node if found, or NULL if not present in the list. */ +extern gl_list_node_t gl_list_search (gl_list_t list, const void *elt); + +/* Search whether an element is already in the list, + at a position >= START_INDEX. + Return its node if found, or NULL if not present in the list. */ +extern gl_list_node_t gl_list_search_from (gl_list_t list, size_t start_index, + const void *elt); + +/* Search whether an element is already in the list, + at a position >= START_INDEX and < END_INDEX. + Return its node if found, or NULL if not present in the list. */ +extern gl_list_node_t gl_list_search_from_to (gl_list_t list, + size_t start_index, + size_t end_index, + const void *elt); + +/* Search whether an element is already in the list. + Return its position if found, or (size_t)(-1) if not present in the list. */ +extern size_t gl_list_indexof (gl_list_t list, const void *elt); + +/* Search whether an element is already in the list, + at a position >= START_INDEX. + Return its position if found, or (size_t)(-1) if not present in the list. */ +extern size_t gl_list_indexof_from (gl_list_t list, size_t start_index, + const void *elt); + +/* Search whether an element is already in the list, + at a position >= START_INDEX and < END_INDEX. + Return its position if found, or (size_t)(-1) if not present in the list. */ +extern size_t gl_list_indexof_from_to (gl_list_t list, + size_t start_index, size_t end_index, + const void *elt); + +/* Add an element as the first element of the list. + Return its node. */ +extern gl_list_node_t gl_list_add_first (gl_list_t list, const void *elt); + +/* Add an element as the last element of the list. + Return its node. */ +extern gl_list_node_t gl_list_add_last (gl_list_t list, const void *elt); + +/* Add an element before a given element node of the list. + Return its node. */ +extern gl_list_node_t gl_list_add_before (gl_list_t list, gl_list_node_t node, + const void *elt); + +/* Add an element after a given element node of the list. + Return its node. */ +extern gl_list_node_t gl_list_add_after (gl_list_t list, gl_list_node_t node, + const void *elt); + +/* Add an element add a given position in the list. + POSITION must be >= 0 and <= gl_list_size (list). */ +extern gl_list_node_t gl_list_add_at (gl_list_t list, size_t position, + const void *elt); + +/* Remove an element from the list. + Return true. */ +extern bool gl_list_remove_node (gl_list_t list, gl_list_node_t node); + +/* Remove an element at a given position from the list. + POSITION must be >= 0 and < gl_list_size (list). + Return true. */ +extern bool gl_list_remove_at (gl_list_t list, size_t position); + +/* Search and remove an element from the list. + Return true if it was found and removed. */ +extern bool gl_list_remove (gl_list_t list, const void *elt); + +/* Free an entire list. + (But this call does not free the elements of the list.) */ +extern void gl_list_free (gl_list_t list); + +/* --------------------- gl_list_iterator_t Data Type --------------------- */ + +/* Functions for iterating through a list. */ + +/* Type of an iterator that traverses a list. + This is a fixed-size struct, so that creation of an iterator doesn't need + memory allocation on the heap. */ +typedef struct +{ + /* For fast dispatch of gl_list_iterator_next. */ + const struct gl_list_implementation *vtable; + /* For detecting whether the last returned element was removed. */ + gl_list_t list; + size_t count; + /* Other, implementation-private fields. */ + void *p; void *q; + size_t i; size_t j; +} gl_list_iterator_t; + +/* Create an iterator traversing a list. + The list contents must not be modified while the iterator is in use, + except for replacing or removing the last returned element. */ +extern gl_list_iterator_t gl_list_iterator (gl_list_t list); + +/* Create an iterator traversing the element with indices i, + start_index <= i < end_index, of a list. + The list contents must not be modified while the iterator is in use, + except for replacing or removing the last returned element. */ +extern gl_list_iterator_t gl_list_iterator_from_to (gl_list_t list, + size_t start_index, + size_t end_index); + +/* If there is a next element, store the next element in *ELTP, store its + node in *NODEP if NODEP is non-NULL, advance the iterator and return true. + Otherwise, return false. */ +extern bool gl_list_iterator_next (gl_list_iterator_t *iterator, + const void **eltp, gl_list_node_t *nodep); + +/* Free an iterator. */ +extern void gl_list_iterator_free (gl_list_iterator_t *iterator); + +/* ---------------------- Sorted gl_list_t Data Type ---------------------- */ + +/* The following functions are for lists without duplicates where the + order is given by a sort criterion. */ + +/* Type of function used to compare two elements. Same as for qsort(). + NULL denotes pointer comparison. */ +typedef int (*gl_listelement_compar_fn) (const void *elt1, const void *elt2); + +/* Search whether an element is already in the list. + The list is assumed to be sorted with COMPAR. + Return its node if found, or NULL if not present in the list. + If the list contains several copies of ELT, the node of the leftmost one is + returned. */ +extern gl_list_node_t gl_sortedlist_search (gl_list_t list, + gl_listelement_compar_fn compar, + const void *elt); + +/* Search whether an element is already in the list. + The list is assumed to be sorted with COMPAR. + Only list elements with indices >= START_INDEX and < END_INDEX are + considered; the implementation uses these bounds to minimize the number + of COMPAR invocations. + Return its node if found, or NULL if not present in the list. + If the list contains several copies of ELT, the node of the leftmost one is + returned. */ +extern gl_list_node_t gl_sortedlist_search_from_to (gl_list_t list, + gl_listelement_compar_fn compar, + size_t start_index, + size_t end_index, + const void *elt); + +/* Search whether an element is already in the list. + The list is assumed to be sorted with COMPAR. + Return its position if found, or (size_t)(-1) if not present in the list. + If the list contains several copies of ELT, the position of the leftmost one + is returned. */ +extern size_t gl_sortedlist_indexof (gl_list_t list, + gl_listelement_compar_fn compar, + const void *elt); + +/* Search whether an element is already in the list. + The list is assumed to be sorted with COMPAR. + Only list elements with indices >= START_INDEX and < END_INDEX are + considered; the implementation uses these bounds to minimize the number + of COMPAR invocations. + Return its position if found, or (size_t)(-1) if not present in the list. + If the list contains several copies of ELT, the position of the leftmost one + is returned. */ +extern size_t gl_sortedlist_indexof_from_to (gl_list_t list, + gl_listelement_compar_fn compar, + size_t start_index, + size_t end_index, + const void *elt); + +/* Add an element at the appropriate position in the list. + The list is assumed to be sorted with COMPAR. + Return its node. */ +extern gl_list_node_t gl_sortedlist_add (gl_list_t list, + gl_listelement_compar_fn compar, + const void *elt); + +/* Search and remove an element from the list. + The list is assumed to be sorted with COMPAR. + Return true if it was found and removed. + If the list contains several copies of ELT, only the leftmost one is + removed. */ +extern bool gl_sortedlist_remove (gl_list_t list, + gl_listelement_compar_fn compar, + const void *elt); + +/* ------------------------ Implementation Details ------------------------ */ + +struct gl_list_implementation +{ + /* gl_list_t functions. */ + gl_list_t (*create_empty) (gl_list_implementation_t implementation, + gl_listelement_equals_fn equals_fn, + gl_listelement_hashcode_fn hashcode_fn, + bool allow_duplicates); + gl_list_t (*create) (gl_list_implementation_t implementation, + gl_listelement_equals_fn equals_fn, + gl_listelement_hashcode_fn hashcode_fn, + bool allow_duplicates, + size_t count, const void **contents); + size_t (*size) (gl_list_t list); + const void * (*node_value) (gl_list_t list, gl_list_node_t node); + gl_list_node_t (*next_node) (gl_list_t list, gl_list_node_t node); + gl_list_node_t (*previous_node) (gl_list_t list, gl_list_node_t node); + const void * (*get_at) (gl_list_t list, size_t position); + gl_list_node_t (*set_at) (gl_list_t list, size_t position, const void *elt); + gl_list_node_t (*search_from_to) (gl_list_t list, size_t start_index, + size_t end_index, const void *elt); + size_t (*indexof_from_to) (gl_list_t list, size_t start_index, + size_t end_index, const void *elt); + gl_list_node_t (*add_first) (gl_list_t list, const void *elt); + gl_list_node_t (*add_last) (gl_list_t list, const void *elt); + gl_list_node_t (*add_before) (gl_list_t list, gl_list_node_t node, + const void *elt); + gl_list_node_t (*add_after) (gl_list_t list, gl_list_node_t node, + const void *elt); + gl_list_node_t (*add_at) (gl_list_t list, size_t position, + const void *elt); + bool (*remove_node) (gl_list_t list, gl_list_node_t node); + bool (*remove_at) (gl_list_t list, size_t position); + bool (*remove) (gl_list_t list, const void *elt); + void (*list_free) (gl_list_t list); + /* gl_list_iterator_t functions. */ + gl_list_iterator_t (*iterator) (gl_list_t list); + gl_list_iterator_t (*iterator_from_to) (gl_list_t list, + size_t start_index, + size_t end_index); + bool (*iterator_next) (gl_list_iterator_t *iterator, + const void **eltp, gl_list_node_t *nodep); + void (*iterator_free) (gl_list_iterator_t *iterator); + /* Sorted gl_list_t functions. */ + gl_list_node_t (*sortedlist_search) (gl_list_t list, + gl_listelement_compar_fn compar, + const void *elt); + gl_list_node_t (*sortedlist_search_from_to) (gl_list_t list, + gl_listelement_compar_fn compar, + size_t start_index, + size_t end_index, + const void *elt); + size_t (*sortedlist_indexof) (gl_list_t list, + gl_listelement_compar_fn compar, + const void *elt); + size_t (*sortedlist_indexof_from_to) (gl_list_t list, + gl_listelement_compar_fn compar, + size_t start_index, size_t end_index, + const void *elt); + gl_list_node_t (*sortedlist_add) (gl_list_t list, + gl_listelement_compar_fn compar, + const void *elt); + bool (*sortedlist_remove) (gl_list_t list, + gl_listelement_compar_fn compar, + const void *elt); +}; + +struct gl_list_impl_base +{ + const struct gl_list_implementation *vtable; + gl_listelement_equals_fn equals_fn; + gl_listelement_hashcode_fn hashcode_fn; + bool allow_duplicates; +}; + +#if HAVE_INLINE + +/* Define all functions of this file as inline accesses to the + struct gl_list_implementation. + Use #define to avoid a warning because of extern vs. static. */ + +# define gl_list_create_empty gl_list_create_empty_inline +static inline gl_list_t +gl_list_create_empty (gl_list_implementation_t implementation, + gl_listelement_equals_fn equals_fn, + gl_listelement_hashcode_fn hashcode_fn, + bool allow_duplicates) +{ + return implementation->create_empty (implementation, equals_fn, hashcode_fn, + allow_duplicates); +} + +# define gl_list_create gl_list_create_inline +static inline gl_list_t +gl_list_create (gl_list_implementation_t implementation, + gl_listelement_equals_fn equals_fn, + gl_listelement_hashcode_fn hashcode_fn, + bool allow_duplicates, + size_t count, const void **contents) +{ + return implementation->create (implementation, equals_fn, hashcode_fn, + allow_duplicates, count, contents); +} + +# define gl_list_size gl_list_size_inline +static inline size_t +gl_list_size (gl_list_t list) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->size (list); +} + +# define gl_list_node_value gl_list_node_value_inline +static inline const void * +gl_list_node_value (gl_list_t list, gl_list_node_t node) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->node_value (list, node); +} + +# define gl_list_next_node gl_list_next_node_inline +static inline gl_list_node_t +gl_list_next_node (gl_list_t list, gl_list_node_t node) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->next_node (list, node); +} + +# define gl_list_previous_node gl_list_previous_node_inline +static inline gl_list_node_t +gl_list_previous_node (gl_list_t list, gl_list_node_t node) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->previous_node (list, node); +} + +# define gl_list_get_at gl_list_get_at_inline +static inline const void * +gl_list_get_at (gl_list_t list, size_t position) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->get_at (list, position); +} + +# define gl_list_set_at gl_list_set_at_inline +static inline gl_list_node_t +gl_list_set_at (gl_list_t list, size_t position, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->set_at (list, position, elt); +} + +# define gl_list_search gl_list_search_inline +static inline gl_list_node_t +gl_list_search (gl_list_t list, const void *elt) +{ + size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); + return ((const struct gl_list_impl_base *) list)->vtable + ->search_from_to (list, 0, size, elt); +} + +# define gl_list_search_from gl_list_search_from_inline +static inline gl_list_node_t +gl_list_search_from (gl_list_t list, size_t start_index, const void *elt) +{ + size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); + return ((const struct gl_list_impl_base *) list)->vtable + ->search_from_to (list, start_index, size, elt); +} + +# define gl_list_search_from_to gl_list_search_from_to_inline +static inline gl_list_node_t +gl_list_search_from_to (gl_list_t list, size_t start_index, size_t end_index, + const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->search_from_to (list, start_index, end_index, elt); +} + +# define gl_list_indexof gl_list_indexof_inline +static inline size_t +gl_list_indexof (gl_list_t list, const void *elt) +{ + size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); + return ((const struct gl_list_impl_base *) list)->vtable + ->indexof_from_to (list, 0, size, elt); +} + +# define gl_list_indexof_from gl_list_indexof_from_inline +static inline size_t +gl_list_indexof_from (gl_list_t list, size_t start_index, const void *elt) +{ + size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list); + return ((const struct gl_list_impl_base *) list)->vtable + ->indexof_from_to (list, start_index, size, elt); +} + +# define gl_list_indexof_from_to gl_list_indexof_from_to_inline +static inline size_t +gl_list_indexof_from_to (gl_list_t list, size_t start_index, size_t end_index, + const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->indexof_from_to (list, start_index, end_index, elt); +} + +# define gl_list_add_first gl_list_add_first_inline +static inline gl_list_node_t +gl_list_add_first (gl_list_t list, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->add_first (list, elt); +} + +# define gl_list_add_last gl_list_add_last_inline +static inline gl_list_node_t +gl_list_add_last (gl_list_t list, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->add_last (list, elt); +} + +# define gl_list_add_before gl_list_add_before_inline +static inline gl_list_node_t +gl_list_add_before (gl_list_t list, gl_list_node_t node, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->add_before (list, node, elt); +} + +# define gl_list_add_after gl_list_add_after_inline +static inline gl_list_node_t +gl_list_add_after (gl_list_t list, gl_list_node_t node, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->add_after (list, node, elt); +} + +# define gl_list_add_at gl_list_add_at_inline +static inline gl_list_node_t +gl_list_add_at (gl_list_t list, size_t position, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->add_at (list, position, elt); +} + +# define gl_list_remove_node gl_list_remove_node_inline +static inline bool +gl_list_remove_node (gl_list_t list, gl_list_node_t node) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->remove_node (list, node); +} + +# define gl_list_remove_at gl_list_remove_at_inline +static inline bool +gl_list_remove_at (gl_list_t list, size_t position) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->remove_at (list, position); +} + +# define gl_list_remove gl_list_remove_inline +static inline bool +gl_list_remove (gl_list_t list, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->remove (list, elt); +} + +# define gl_list_free gl_list_free_inline +static inline void +gl_list_free (gl_list_t list) +{ + ((const struct gl_list_impl_base *) list)->vtable->list_free (list); +} + +# define gl_list_iterator gl_list_iterator_inline +static inline gl_list_iterator_t +gl_list_iterator (gl_list_t list) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->iterator (list); +} + +# define gl_list_iterator_from_to gl_list_iterator_from_to_inline +static inline gl_list_iterator_t +gl_list_iterator_from_to (gl_list_t list, size_t start_index, size_t end_index) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->iterator_from_to (list, start_index, end_index); +} + +# define gl_list_iterator_next gl_list_iterator_next_inline +static inline bool +gl_list_iterator_next (gl_list_iterator_t *iterator, + const void **eltp, gl_list_node_t *nodep) +{ + return iterator->vtable->iterator_next (iterator, eltp, nodep); +} + +# define gl_list_iterator_free gl_list_iterator_free_inline +static inline void +gl_list_iterator_free (gl_list_iterator_t *iterator) +{ + iterator->vtable->iterator_free (iterator); +} + +# define gl_sortedlist_search gl_sortedlist_search_inline +static inline gl_list_node_t +gl_sortedlist_search (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_search (list, compar, elt); +} + +# define gl_sortedlist_search_from_to gl_sortedlist_search_from_to_inline +static inline gl_list_node_t +gl_sortedlist_search_from_to (gl_list_t list, gl_listelement_compar_fn compar, size_t start_index, size_t end_index, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_search_from_to (list, compar, start_index, end_index, + elt); +} + +# define gl_sortedlist_indexof gl_sortedlist_indexof_inline +static inline size_t +gl_sortedlist_indexof (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_indexof (list, compar, elt); +} + +# define gl_sortedlist_indexof_from_to gl_sortedlist_indexof_from_to_inline +static inline size_t +gl_sortedlist_indexof_from_to (gl_list_t list, gl_listelement_compar_fn compar, size_t start_index, size_t end_index, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_indexof_from_to (list, compar, start_index, end_index, + elt); +} + +# define gl_sortedlist_add gl_sortedlist_add_inline +static inline gl_list_node_t +gl_sortedlist_add (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_add (list, compar, elt); +} + +# define gl_sortedlist_remove gl_sortedlist_remove_inline +static inline bool +gl_sortedlist_remove (gl_list_t list, gl_listelement_compar_fn compar, const void *elt) +{ + return ((const struct gl_list_impl_base *) list)->vtable + ->sortedlist_remove (list, compar, elt); +} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _GL_LIST_H */ diff --git a/src/bin/m4/lib/gl_oset.c b/src/bin/m4/lib/gl_oset.c new file mode 100644 index 0000000000..83f964e0ce --- /dev/null +++ b/src/bin/m4/lib/gl_oset.c @@ -0,0 +1,94 @@ +/* Abstract ordered set data type. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "gl_oset.h" + +#if !HAVE_INLINE + +/* Define all functions of this file as inline accesses to the + struct gl_list_implementation. + Use #define to avoid a warning because of extern vs. static. */ + +gl_oset_t +gl_oset_create_empty (gl_oset_implementation_t implementation, + gl_setelement_compar_fn compar_fn) +{ + return implementation->create_empty (implementation, compar_fn); +} + +size_t +gl_oset_size (gl_oset_t set) +{ + return ((const struct gl_oset_impl_base *) set)->vtable->size (set); +} + +bool +gl_oset_search (gl_oset_t set, const void *elt) +{ + return ((const struct gl_oset_impl_base *) set)->vtable->search (set, elt); +} + +bool +gl_oset_search_atleast (gl_oset_t set, + gl_setelement_threshold_fn threshold_fn, + const void *threshold, const void **eltp) +{ + return ((const struct gl_oset_impl_base *) set)->vtable + ->search_atleast (set, threshold_fn, threshold, eltp); +} + +bool +gl_oset_add (gl_oset_t set, const void *elt) +{ + return ((const struct gl_oset_impl_base *) set)->vtable->add (set, elt); +} + +bool +gl_oset_remove (gl_oset_t set, const void *elt) +{ + return ((const struct gl_oset_impl_base *) set)->vtable->remove (set, elt); +} + +void +gl_oset_free (gl_oset_t set) +{ + ((const struct gl_oset_impl_base *) set)->vtable->oset_free (set); +} + +gl_oset_iterator_t +gl_oset_iterator (gl_oset_t set) +{ + return ((const struct gl_oset_impl_base *) set)->vtable->iterator (set); +} + +bool +gl_oset_iterator_next (gl_oset_iterator_t *iterator, const void **eltp) +{ + return iterator->vtable->iterator_next (iterator, eltp); +} + +void +gl_oset_iterator_free (gl_oset_iterator_t *iterator) +{ + iterator->vtable->iterator_free (iterator); +} + +#endif diff --git a/src/bin/m4/lib/gl_oset.h b/src/bin/m4/lib/gl_oset.h new file mode 100644 index 0000000000..fb36aac1cf --- /dev/null +++ b/src/bin/m4/lib/gl_oset.h @@ -0,0 +1,265 @@ +/* Abstract ordered set data type. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GL_OSET_H +#define _GL_OSET_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* gl_oset is an abstract ordered set data type. It can contain any number + of objects ('void *' or 'const void *' pointers) in the order of a given + comparator function. Duplicates (in the sense of the comparator) are + forbidden. + + There are several implementations of this ordered set datatype, optimized + for different operations or for memory. You can start using the simplest + ordered set implementation, GL_ARRAY_OSET, and switch to a different + implementation later, when you realize which operations are performed + the most frequently. The API of the different implementations is exactly + the same; when switching to a different implementation, you only have to + change the gl_oset_create call. + + The implementations are: + GL_ARRAY_OSET a growable array + GL_AVLTREE_OSET a binary tree (AVL tree) + GL_RBTREE_OSET a binary tree (red-black tree) + + The memory consumption is asymptotically the same: O(1) for every object + in the set. When looking more closely at the average memory consumed + for an object, GL_ARRAY_OSET is the most compact representation, and + GL_AVLTREE_OSET, GL_RBTREE_OSET need more memory. + + The guaranteed average performance of the operations is, for a set of + n elements: + + Operation ARRAY TREE + + gl_oset_size O(1) O(1) + gl_oset_add O(n) O(log n) + gl_oset_remove O(n) O(log n) + gl_oset_search O(log n) O(log n) + gl_oset_search_atleast O(log n) O(log n) + gl_oset_iterator O(1) O(log n) + gl_oset_iterator_next O(1) O(log n) + */ + +/* -------------------------- gl_oset_t Data Type -------------------------- */ + +/* Type of function used to compare two elements. Same as for qsort(). + NULL denotes pointer comparison. */ +typedef int (*gl_setelement_compar_fn) (const void *elt1, const void *elt2); + +/* Type of function used to compare an element with a threshold. + Return true if the element is greater or equal than the threshold. */ +typedef bool (*gl_setelement_threshold_fn) (const void *elt, const void *threshold); + +struct gl_oset_impl; +/* Type representing an entire ordered set. */ +typedef struct gl_oset_impl * gl_oset_t; + +struct gl_oset_implementation; +/* Type representing a ordered set datatype implementation. */ +typedef const struct gl_oset_implementation * gl_oset_implementation_t; + +/* Create an empty set. + IMPLEMENTATION is one of GL_ARRAY_OSET, GL_AVLTREE_OSET, GL_RBTREE_OSET. + COMPAR_FN is an element comparison function or NULL. */ +extern gl_oset_t gl_oset_create_empty (gl_oset_implementation_t implementation, + gl_setelement_compar_fn compar_fn); + +/* Return the current number of elements in an ordered set. */ +extern size_t gl_oset_size (gl_oset_t set); + +/* Search whether an element is already in the ordered set. + Return true if found, or false if not present in the set. */ +extern bool gl_oset_search (gl_oset_t set, const void *elt); + +/* Search the least element in the ordered set that compares greater or equal + to the given THRESHOLD. The representation of the THRESHOLD is defined + by the THRESHOLD_FN. + Return true and store the found element in *ELTP if found, otherwise return + false. */ +extern bool gl_oset_search_atleast (gl_oset_t set, + gl_setelement_threshold_fn threshold_fn, + const void *threshold, + const void **eltp); + +/* Add an element to an ordered set. + Return true if it was not already in the set and added. */ +extern bool gl_oset_add (gl_oset_t set, const void *elt); + +/* Remove an element from an ordered set. + Return true if it was found and removed. */ +extern bool gl_oset_remove (gl_oset_t set, const void *elt); + +/* Free an entire ordered set. + (But this call does not free the elements of the set.) */ +extern void gl_oset_free (gl_oset_t set); + +/* --------------------- gl_oset_iterator_t Data Type --------------------- */ + +/* Functions for iterating through an ordered set. */ + +/* Type of an iterator that traverses an ordered set. + This is a fixed-size struct, so that creation of an iterator doesn't need + memory allocation on the heap. */ +typedef struct +{ + /* For fast dispatch of gl_oset_iterator_next. */ + const struct gl_oset_implementation *vtable; + /* For detecting whether the last returned element was removed. */ + gl_oset_t set; + size_t count; + /* Other, implementation-private fields. */ + void *p; void *q; + size_t i; size_t j; +} gl_oset_iterator_t; + +/* Create an iterator traversing an ordered set. + The set's contents must not be modified while the iterator is in use, + except for removing the last returned element. */ +extern gl_oset_iterator_t gl_oset_iterator (gl_oset_t set); + +/* If there is a next element, store the next element in *ELTP, advance the + iterator and return true. Otherwise, return false. */ +extern bool gl_oset_iterator_next (gl_oset_iterator_t *iterator, + const void **eltp); + +/* Free an iterator. */ +extern void gl_oset_iterator_free (gl_oset_iterator_t *iterator); + +/* ------------------------ Implementation Details ------------------------ */ + +struct gl_oset_implementation +{ + /* gl_oset_t functions. */ + gl_oset_t (*create_empty) (gl_oset_implementation_t implementation, + gl_setelement_compar_fn compar_fn); + size_t (*size) (gl_oset_t set); + bool (*search) (gl_oset_t set, const void *elt); + bool (*search_atleast) (gl_oset_t set, + gl_setelement_threshold_fn threshold_fn, + const void *threshold, const void **eltp); + bool (*add) (gl_oset_t set, const void *elt); + bool (*remove) (gl_oset_t set, const void *elt); + void (*oset_free) (gl_oset_t set); + /* gl_oset_iterator_t functions. */ + gl_oset_iterator_t (*iterator) (gl_oset_t set); + bool (*iterator_next) (gl_oset_iterator_t *iterator, const void **eltp); + void (*iterator_free) (gl_oset_iterator_t *iterator); +}; + +struct gl_oset_impl_base +{ + const struct gl_oset_implementation *vtable; + gl_setelement_compar_fn compar_fn; +}; + +#if HAVE_INLINE + +/* Define all functions of this file as inline accesses to the + struct gl_oset_implementation. + Use #define to avoid a warning because of extern vs. static. */ + +# define gl_oset_create_empty gl_oset_create_empty_inline +static inline gl_oset_t +gl_oset_create_empty (gl_oset_implementation_t implementation, + gl_setelement_compar_fn compar_fn) +{ + return implementation->create_empty (implementation, compar_fn); +} + +# define gl_oset_size gl_oset_size_inline +static inline size_t +gl_oset_size (gl_oset_t set) +{ + return ((const struct gl_oset_impl_base *) set)->vtable->size (set); +} + +# define gl_oset_search gl_oset_search_inline +static inline bool +gl_oset_search (gl_oset_t set, const void *elt) +{ + return ((const struct gl_oset_impl_base *) set)->vtable->search (set, elt); +} + +# define gl_oset_search_atleast gl_oset_search_atleast_inline +static inline bool +gl_oset_search_atleast (gl_oset_t set, + gl_setelement_threshold_fn threshold_fn, + const void *threshold, const void **eltp) +{ + return ((const struct gl_oset_impl_base *) set)->vtable + ->search_atleast (set, threshold_fn, threshold, eltp); +} + +# define gl_oset_add gl_oset_add_inline +static inline bool +gl_oset_add (gl_oset_t set, const void *elt) +{ + return ((const struct gl_oset_impl_base *) set)->vtable->add (set, elt); +} + +# define gl_oset_remove gl_oset_remove_inline +static inline bool +gl_oset_remove (gl_oset_t set, const void *elt) +{ + return ((const struct gl_oset_impl_base *) set)->vtable->remove (set, elt); +} + +# define gl_oset_free gl_oset_free_inline +static inline void +gl_oset_free (gl_oset_t set) +{ + ((const struct gl_oset_impl_base *) set)->vtable->oset_free (set); +} + +# define gl_oset_iterator gl_oset_iterator_inline +static inline gl_oset_iterator_t +gl_oset_iterator (gl_oset_t set) +{ + return ((const struct gl_oset_impl_base *) set)->vtable->iterator (set); +} + +# define gl_oset_iterator_next gl_oset_iterator_next_inline +static inline bool +gl_oset_iterator_next (gl_oset_iterator_t *iterator, const void **eltp) +{ + return iterator->vtable->iterator_next (iterator, eltp); +} + +# define gl_oset_iterator_free gl_oset_iterator_free_inline +static inline void +gl_oset_iterator_free (gl_oset_iterator_t *iterator) +{ + iterator->vtable->iterator_free (iterator); +} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _GL_OSET_H */ diff --git a/src/bin/m4/lib/malloc.c b/src/bin/m4/lib/malloc.c new file mode 100644 index 0000000000..d4dae3e182 --- /dev/null +++ b/src/bin/m4/lib/malloc.c @@ -0,0 +1,35 @@ +/* malloc() function that is glibc compatible. + + Copyright (C) 1997, 1998, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* written by Jim Meyering */ + +#include +#undef malloc + +#include + +/* Allocate an N-byte block of memory from the heap. + If N is zero, allocate a 1-byte block. */ + +void * +rpl_malloc (size_t n) +{ + if (n == 0) + n = 1; + return malloc (n); +} diff --git a/src/bin/m4/lib/mbchar.c b/src/bin/m4/lib/mbchar.c new file mode 100644 index 0000000000..95373f58ba --- /dev/null +++ b/src/bin/m4/lib/mbchar.c @@ -0,0 +1,36 @@ +/* Copyright (C) 2001, 2006 Free Software Foundation, Inc. + + 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + +#include + +#include + +#include "mbchar.h" + +#if IS_BASIC_ASCII + +/* Bit table of characters in the ISO C "basic character set". */ +const unsigned int is_basic_table [UCHAR_MAX / 32 + 1] = +{ + 0x00001a00, /* '\t' '\v' '\f' */ + 0xffffffef, /* ' '...'#' '%'...'?' */ + 0xfffffffe, /* 'A'...'Z' '[' '\\' ']' '^' '_' */ + 0x7ffffffe /* 'a'...'z' '{' '|' '}' '~' */ + /* The remaining bits are 0. */ +}; + +#endif /* IS_BASIC_ASCII */ diff --git a/src/bin/m4/lib/mbchar.h b/src/bin/m4/lib/mbchar.h new file mode 100644 index 0000000000..a0c2cdcb99 --- /dev/null +++ b/src/bin/m4/lib/mbchar.h @@ -0,0 +1,478 @@ +/* Multibyte character data type. + Copyright (C) 2001, 2005-2006 Free Software Foundation, Inc. + + 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by Bruno Haible . */ + +/* A multibyte character is a short subsequence of a char* string, + representing a single wide character. + + We use multibyte characters instead of wide characters because of + the following goals: + 1) correct multibyte handling, i.e. operate according to the LC_CTYPE + locale, + 2) ease of maintenance, i.e. the maintainer needs not know all details + of the ISO C 99 standard, + 3) don't fail grossly if the input is not in the encoding set by the + locale, because often different encodings are in use in the same + countries (ISO-8859-1/UTF-8, EUC-JP/Shift_JIS, ...), + 4) fast in the case of ASCII characters, + 5) portability, i.e. don't make unportable assumptions about wchar_t. + + Multibyte characters are only accessed through the mb* macros. + + mb_ptr (mbc) + return a pointer to the beginning of the multibyte sequence. + + mb_len (mbc) + returns the number of bytes occupied by the multibyte sequence. + Always > 0. + + mb_iseq (mbc, sc) + returns true if mbc is the standard ASCII character sc. + + mb_isnul (mbc) + returns true if mbc is the nul character. + + mb_cmp (mbc1, mbc2) + returns a positive, zero, or negative value depending on whether mbc1 + sorts after, same or before mbc2. + + mb_casecmp (mbc1, mbc2) + returns a positive, zero, or negative value depending on whether mbc1 + sorts after, same or before mbc2, modulo upper/lowercase conversion. + + mb_equal (mbc1, mbc2) + returns true if mbc1 and mbc2 are equal. + + mb_caseequal (mbc1, mbc2) + returns true if mbc1 and mbc2 are equal modulo upper/lowercase conversion. + + mb_isalnum (mbc) + returns true if mbc is alphanumeric. + + mb_isalpha (mbc) + returns true if mbc is alphabetic. + + mb_isascii(mbc) + returns true if mbc is plain ASCII. + + mb_isblank (mbc) + returns true if mbc is a blank. + + mb_iscntrl (mbc) + returns true if mbc is a control character. + + mb_isdigit (mbc) + returns true if mbc is a decimal digit. + + mb_isgraph (mbc) + returns true if mbc is a graphic character. + + mb_islower (mbc) + returns true if mbc is lowercase. + + mb_isprint (mbc) + returns true if mbc is a printable character. + + mb_ispunct (mbc) + returns true if mbc is a punctuation character. + + mb_isspace (mbc) + returns true if mbc is a space character. + + mb_isupper (mbc) + returns true if mbc is uppercase. + + mb_isxdigit (mbc) + returns true if mbc is a hexadecimal digit. + + mb_width (mbc) + returns the number of columns on the output device occupied by mbc. + Always >= 0. + + mb_putc (mbc, stream) + outputs mbc on stream, a byte oriented FILE stream opened for output. + + mb_setascii (&mbc, sc) + assigns the standard ASCII character sc to mbc. + + mb_copy (&destmbc, &srcmbc) + copies srcmbc to destmbc. + + Here are the function prototypes of the macros. + + extern const char * mb_ptr (const mbchar_t mbc); + extern size_t mb_len (const mbchar_t mbc); + extern bool mb_iseq (const mbchar_t mbc, char sc); + extern bool mb_isnul (const mbchar_t mbc); + extern int mb_cmp (const mbchar_t mbc1, const mbchar_t mbc2); + extern int mb_casecmp (const mbchar_t mbc1, const mbchar_t mbc2); + extern bool mb_equal (const mbchar_t mbc1, const mbchar_t mbc2); + extern bool mb_caseequal (const mbchar_t mbc1, const mbchar_t mbc2); + extern bool mb_isalnum (const mbchar_t mbc); + extern bool mb_isalpha (const mbchar_t mbc); + extern bool mb_isascii (const mbchar_t mbc); + extern bool mb_isblank (const mbchar_t mbc); + extern bool mb_iscntrl (const mbchar_t mbc); + extern bool mb_isdigit (const mbchar_t mbc); + extern bool mb_isgraph (const mbchar_t mbc); + extern bool mb_islower (const mbchar_t mbc); + extern bool mb_isprint (const mbchar_t mbc); + extern bool mb_ispunct (const mbchar_t mbc); + extern bool mb_isspace (const mbchar_t mbc); + extern bool mb_isupper (const mbchar_t mbc); + extern bool mb_isxdigit (const mbchar_t mbc); + extern int mb_width (const mbchar_t mbc); + extern void mb_putc (const mbchar_t mbc, FILE *stream); + extern void mb_setascii (mbchar_t *new, char sc); + extern void mb_copy (mbchar_t *new, const mbchar_t *old); + */ + +#ifndef _MBCHAR_H +#define _MBCHAR_H 1 + +#include +#include + +/* Tru64 with Desktop Toolkit C has a bug: must be included before + . + BSD/OS 4.1 has a bug: and must be included before + . */ +#include +#include +#include + +/* BeOS 5 has the functions but no . */ +#if HAVE_WCTYPE_H +# include +#endif +/* FreeBSD 4.4 to 4.11 has but lacks the functions. + Assume all 12 functions are implemented the same way, or not at all. */ +#if !defined iswalnum && !HAVE_ISWCNTRL +static inline int +iswalnum (wint_t wc) +{ + return (wc >= 0 && wc < 128 + ? (wc >= '0' && wc <= '9') || ((wc & ~0x20) >= 'A' && (wc & ~0x20) <= 'Z') + : 0); +} +# define iswalnum iswalnum +#endif +#if !defined iswalpha && !HAVE_ISWCNTRL +static inline int +iswalpha (wint_t wc) +{ + return (wc >= 0 && wc < 128 + ? (wc & ~0x20) >= 'A' && (wc & ~0x20) <= 'Z' + : 0); +} +# define iswalpha iswalpha +#endif +#if !defined iswblank && !HAVE_ISWCNTRL +static inline int +iswblank (wint_t wc) +{ + return (wc >= 0 && wc < 128 + ? wc == ' ' || wc == '\t' + : 0); +} +# define iswblank iswblank +#endif +#if !defined iswcntrl && !HAVE_ISWCNTRL +static inline int +iswcntrl (wint_t wc) +{ + return (wc >= 0 && wc < 128 + ? (wc & ~0x1f) == 0 || wc == 0x7f + : 0); +} +# define iswcntrl iswcntrl +#endif +#if !defined iswdigit && !HAVE_ISWCNTRL +static inline int +iswdigit (wint_t wc) +{ + return (wc >= '0' && wc <= '9'); +} +# define iswdigit iswdigit +#endif +#if !defined iswgraph && !HAVE_ISWCNTRL +static inline int +iswgraph (wint_t wc) +{ + return (wc >= 0 && wc < 128 + ? wc >= '!' && wc <= '~' + : 1); +} +# define iswgraph iswgraph +#endif +#if !defined iswlower && !HAVE_ISWCNTRL +static inline int +iswlower (wint_t wc) +{ + return (wc >= 0 && wc < 128 + ? wc >= 'a' && wc <= 'z' + : 0); +} +# define iswlower iswlower +#endif +#if !defined iswprint && !HAVE_ISWCNTRL +static inline int +iswprint (wint_t wc) +{ + return (wc >= 0 && wc < 128 + ? wc >= ' ' && wc <= '~' + : 1); +} +# define iswprint iswprint +#endif +#if !defined iswpunct && !HAVE_ISWCNTRL +static inline int +iswpunct (wint_t wc) +{ + return (wc >= 0 && wc < 128 + ? wc >= '!' && wc <= '~' + && !((wc >= '0' && wc <= '9') + || ((wc & ~0x20) >= 'A' && (wc & ~0x20) <= 'Z')) + : 1); +} +# define iswpunct iswpunct +#endif +#if !defined iswspace && !HAVE_ISWCNTRL +static inline int +iswspace (wint_t wc) +{ + return (wc >= 0 && wc < 128 + ? wc == ' ' || wc == '\t' + || wc == '\n' || wc == '\v' || wc == '\f' || wc == '\r' + : 0); +} +# define iswspace iswspace +#endif +#if !defined iswupper && !HAVE_ISWCNTRL +static inline int +iswupper (wint_t wc) +{ + return (wc >= 0 && wc < 128 + ? wc >= 'A' && wc <= 'Z' + : 0); +} +# define iswupper iswupper +#endif +#if !defined iswxdigit && !HAVE_ISWCNTRL +static inline int +iswxdigit (wint_t wc) +{ + return (wc >= '0' && wc <= '9') || ((wc & ~0x20) >= 'A' && (wc & ~0x20) <= 'F'); +} +# define iswxdigit iswxdigit +#endif + +#include "wcwidth.h" + +#define MBCHAR_BUF_SIZE 24 + +struct mbchar +{ + const char *ptr; /* pointer to current character */ + size_t bytes; /* number of bytes of current character, > 0 */ + bool wc_valid; /* true if wc is a valid wide character */ + wchar_t wc; /* if wc_valid: the current character */ + char buf[MBCHAR_BUF_SIZE]; /* room for the bytes, used for file input only */ +}; + +/* EOF (not a real character) is represented with bytes = 0 and + wc_valid = false. */ + +typedef struct mbchar mbchar_t; + +/* Access the current character. */ +#define mb_ptr(mbc) ((mbc).ptr) +#define mb_len(mbc) ((mbc).bytes) + +/* Comparison of characters. */ +#define mb_iseq(mbc, sc) ((mbc).wc_valid && (mbc).wc == (sc)) +#define mb_isnul(mbc) ((mbc).wc_valid && (mbc).wc == 0) +#define mb_cmp(mbc1, mbc2) \ + ((mbc1).wc_valid \ + ? ((mbc2).wc_valid \ + ? (int) (mbc1).wc - (int) (mbc2).wc \ + : -1) \ + : ((mbc2).wc_valid \ + ? 1 \ + : (mbc1).bytes == (mbc2).bytes \ + ? memcmp ((mbc1).ptr, (mbc2).ptr, (mbc1).bytes) \ + : (mbc1).bytes < (mbc2).bytes \ + ? (memcmp ((mbc1).ptr, (mbc2).ptr, (mbc1).bytes) > 0 ? 1 : -1) \ + : (memcmp ((mbc1).ptr, (mbc2).ptr, (mbc2).bytes) >= 0 ? 1 : -1))) +#define mb_casecmp(mbc1, mbc2) \ + ((mbc1).wc_valid \ + ? ((mbc2).wc_valid \ + ? (int) towlower ((mbc1).wc) - (int) towlower ((mbc2).wc) \ + : -1) \ + : ((mbc2).wc_valid \ + ? 1 \ + : (mbc1).bytes == (mbc2).bytes \ + ? memcmp ((mbc1).ptr, (mbc2).ptr, (mbc1).bytes) \ + : (mbc1).bytes < (mbc2).bytes \ + ? (memcmp ((mbc1).ptr, (mbc2).ptr, (mbc1).bytes) > 0 ? 1 : -1) \ + : (memcmp ((mbc1).ptr, (mbc2).ptr, (mbc2).bytes) >= 0 ? 1 : -1))) +#define mb_equal(mbc1, mbc2) \ + ((mbc1).wc_valid && (mbc2).wc_valid \ + ? (mbc1).wc == (mbc2).wc \ + : (mbc1).bytes == (mbc2).bytes \ + && memcmp ((mbc1).ptr, (mbc2).ptr, (mbc1).bytes) == 0) +#define mb_caseequal(mbc1, mbc2) \ + ((mbc1).wc_valid && (mbc2).wc_valid \ + ? towlower ((mbc1).wc) == towlower ((mbc2).wc) \ + : (mbc1).bytes == (mbc2).bytes \ + && memcmp ((mbc1).ptr, (mbc2).ptr, (mbc1).bytes) == 0) + +/* , classification. */ +#define mb_isascii(mbc) \ + ((mbc).wc_valid && (mbc).wc >= 0 && (mbc).wc <= 127) +#define mb_isalnum(mbc) ((mbc).wc_valid && iswalnum ((mbc).wc)) +#define mb_isalpha(mbc) ((mbc).wc_valid && iswalpha ((mbc).wc)) +#define mb_isblank(mbc) ((mbc).wc_valid && iswblank ((mbc).wc)) +#define mb_iscntrl(mbc) ((mbc).wc_valid && iswcntrl ((mbc).wc)) +#define mb_isdigit(mbc) ((mbc).wc_valid && iswdigit ((mbc).wc)) +#define mb_isgraph(mbc) ((mbc).wc_valid && iswgraph ((mbc).wc)) +#define mb_islower(mbc) ((mbc).wc_valid && iswlower ((mbc).wc)) +#define mb_isprint(mbc) ((mbc).wc_valid && iswprint ((mbc).wc)) +#define mb_ispunct(mbc) ((mbc).wc_valid && iswpunct ((mbc).wc)) +#define mb_isspace(mbc) ((mbc).wc_valid && iswspace ((mbc).wc)) +#define mb_isupper(mbc) ((mbc).wc_valid && iswupper ((mbc).wc)) +#define mb_isxdigit(mbc) ((mbc).wc_valid && iswxdigit ((mbc).wc)) + +/* Extra function. */ + +/* Unprintable characters appear as a small box of width 1. */ +#define MB_UNPRINTABLE_WIDTH 1 + +static inline int +mb_width_aux (wint_t wc) +{ + int w = wcwidth (wc); + /* For unprintable characters, arbitrarily return 0 for control characters + and MB_UNPRINTABLE_WIDTH otherwise. */ + return (w >= 0 ? w : iswcntrl (wc) ? 0 : MB_UNPRINTABLE_WIDTH); +} + +#define mb_width(mbc) \ + ((mbc).wc_valid ? mb_width_aux ((mbc).wc) : MB_UNPRINTABLE_WIDTH) + +/* Output. */ +#define mb_putc(mbc, stream) fwrite ((mbc).ptr, 1, (mbc).bytes, (stream)) + +/* Assignment. */ +#define mb_setascii(mbc, sc) \ + ((mbc)->ptr = (mbc)->buf, (mbc)->bytes = 1, (mbc)->wc_valid = 1, \ + (mbc)->wc = (mbc)->buf[0] = (sc)) + +/* Copying a character. */ +static inline void +mb_copy (mbchar_t *new_mbc, const mbchar_t *old_mbc) +{ + if (old_mbc->ptr == &old_mbc->buf[0]) + { + memcpy (&new_mbc->buf[0], &old_mbc->buf[0], old_mbc->bytes); + new_mbc->ptr = &new_mbc->buf[0]; + } + else + new_mbc->ptr = old_mbc->ptr; + new_mbc->bytes = old_mbc->bytes; + if ((new_mbc->wc_valid = old_mbc->wc_valid)) + new_mbc->wc = old_mbc->wc; +} + + +/* is_basic(c) tests whether the single-byte character c is in the + ISO C "basic character set". + This is a convenience function, and is in this file only to share code + between mbiter_multi.h and mbfile_multi.h. */ +#if (' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126) +/* The character set is ISO-646, not EBCDIC. */ +# define IS_BASIC_ASCII 1 + +extern const unsigned int is_basic_table[]; + +static inline bool +is_basic (char c) +{ + return (is_basic_table [(unsigned char) c >> 5] >> ((unsigned char) c & 31)) + & 1; +} + +#else + +static inline bool +is_basic (char c) +{ + switch (c) + { + case '\t': case '\v': case '\f': + case ' ': case '!': case '"': case '#': case '%': + case '&': case '\'': case '(': case ')': case '*': + case '+': case ',': case '-': case '.': case '/': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case ':': case ';': case '<': case '=': case '>': + case '?': + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': + case '[': case '\\': case ']': case '^': case '_': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': case '{': case '|': case '}': case '~': + return 1; + default: + return 0; + } +} + +#endif + +#endif /* _MBCHAR_H */ diff --git a/src/bin/m4/lib/mbuiter.h b/src/bin/m4/lib/mbuiter.h new file mode 100644 index 0000000000..9da3a6c79c --- /dev/null +++ b/src/bin/m4/lib/mbuiter.h @@ -0,0 +1,203 @@ +/* Iterating through multibyte strings: macros for multi-byte encodings. + Copyright (C) 2001, 2005 Free Software Foundation, Inc. + + 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by Bruno Haible . */ + +/* The macros in this file implement forward iteration through a + multi-byte string, without knowing its length a-priori. + + With these macros, an iteration loop that looks like + + char *iter; + for (iter = buf; *iter != '\0'; iter++) + { + do_something (*iter); + } + + becomes + + mbui_iterator_t iter; + for (mbui_init (iter, buf); mbui_avail (iter); mbui_advance (iter)) + { + do_something (mbui_cur_ptr (iter), mb_len (mbui_cur (iter))); + } + + The benefit of these macros over plain use of mbrtowc is: + - Handling of invalid multibyte sequences is possible without + making the code more complicated, while still preserving the + invalid multibyte sequences. + + Compared to mbiter.h, the macros here don't need to know the string's + length a-priori. The downside is that at each step, the look-ahead + that guards against overrunning the terminating '\0' is more expensive. + The mbui_* macros are therefore suitable when there is a high probability + that only the first few multibyte characters need to be inspected. + Whereas the mbi_* macros are better if usually the iteration runs + through the entire string. + + mbui_iterator_t + is a type usable for variable declarations. + + mbui_init (iter, startptr) + initializes the iterator, starting at startptr. + + mbui_avail (iter) + returns true if there are more multibyte chracters available before + the end of string is reached. In this case, mbui_cur (iter) is + initialized to the next multibyte chracter. + + mbui_advance (iter) + advances the iterator by one multibyte character. + + mbui_cur (iter) + returns the current multibyte character, of type mbchar_t. All the + macros defined in mbchar.h can be used on it. + + mbui_cur_ptr (iter) + return a pointer to the beginning of the current multibyte character. + + mbui_reloc (iter, ptrdiff) + relocates iterator when the string is moved by ptrdiff bytes. + + Here are the function prototypes of the macros. + + extern void mbui_init (mbui_iterator_t iter, const char *startptr); + extern bool mbui_avail (mbui_iterator_t iter); + extern void mbui_advance (mbui_iterator_t iter); + extern mbchar_t mbui_cur (mbui_iterator_t iter); + extern const char * mbui_cur_ptr (mbui_iterator_t iter); + extern void mbui_reloc (mbui_iterator_t iter, ptrdiff_t ptrdiff); + */ + +#ifndef _MBUITER_H +#define _MBUITER_H 1 + +#include +#include +#include + +/* Tru64 with Desktop Toolkit C has a bug: must be included before + . + BSD/OS 4.1 has a bug: and must be included before + . */ +#include +#include +#include + +#include "mbchar.h" +#include "strnlen1.h" + +struct mbuiter_multi +{ + bool in_shift; /* true if next byte may not be interpreted as ASCII */ + mbstate_t state; /* if in_shift: current shift state */ + bool next_done; /* true if mbui_avail has already filled the following */ + struct mbchar cur; /* the current character: + const char *cur.ptr pointer to current character + The following are only valid after mbui_avail. + size_t cur.bytes number of bytes of current character + bool cur.wc_valid true if wc is a valid wide character + wchar_t cur.wc if wc_valid: the current character + */ +}; + +static inline void +mbuiter_multi_next (struct mbuiter_multi *iter) +{ + if (iter->next_done) + return; + if (iter->in_shift) + goto with_shift; + /* Handle most ASCII characters quickly, without calling mbrtowc(). */ + if (is_basic (*iter->cur.ptr)) + { + /* These characters are part of the basic character set. ISO C 99 + guarantees that their wide character code is identical to their + char code. */ + iter->cur.bytes = 1; + iter->cur.wc = *iter->cur.ptr; + iter->cur.wc_valid = true; + } + else + { + assert (mbsinit (&iter->state)); + iter->in_shift = true; + with_shift: + iter->cur.bytes = mbrtowc (&iter->cur.wc, iter->cur.ptr, + strnlen1 (iter->cur.ptr, MB_CUR_MAX), + &iter->state); + if (iter->cur.bytes == (size_t) -1) + { + /* An invalid multibyte sequence was encountered. */ + iter->cur.bytes = 1; + iter->cur.wc_valid = false; + /* Whether to set iter->in_shift = false and reset iter->state + or not is not very important; the string is bogus anyway. */ + } + else if (iter->cur.bytes == (size_t) -2) + { + /* An incomplete multibyte character at the end. */ + iter->cur.bytes = strlen (iter->cur.ptr); + iter->cur.wc_valid = false; + /* Whether to set iter->in_shift = false and reset iter->state + or not is not important; the string end is reached anyway. */ + } + else + { + if (iter->cur.bytes == 0) + { + /* A null wide character was encountered. */ + iter->cur.bytes = 1; + assert (*iter->cur.ptr == '\0'); + assert (iter->cur.wc == 0); + } + iter->cur.wc_valid = true; + + /* When in the initial state, we can go back treating ASCII + characters more quickly. */ + if (mbsinit (&iter->state)) + iter->in_shift = false; + } + } + iter->next_done = true; +} + +static inline void +mbuiter_multi_reloc (struct mbuiter_multi *iter, ptrdiff_t ptrdiff) +{ + iter->cur.ptr += ptrdiff; +} + +/* Iteration macros. */ +typedef struct mbuiter_multi mbui_iterator_t; +#define mbui_init(iter, startptr) \ + ((iter).cur.ptr = (startptr), \ + (iter).in_shift = false, memset (&(iter).state, '\0', sizeof (mbstate_t)), \ + (iter).next_done = false) +#define mbui_avail(iter) \ + (mbuiter_multi_next (&(iter)), !mb_isnul ((iter).cur)) +#define mbui_advance(iter) \ + ((iter).cur.ptr += (iter).cur.bytes, (iter).next_done = false) + +/* Access to the current character. */ +#define mbui_cur(iter) (iter).cur +#define mbui_cur_ptr(iter) (iter).cur.ptr + +/* Relocation. */ +#define mbui_reloc(iter, ptrdiff) mbuiter_multi_reloc (&iter, ptrdiff) + +#endif /* _MBUITER_H */ diff --git a/src/bin/m4/lib/memchr.c b/src/bin/m4/lib/memchr.c new file mode 100644 index 0000000000..d44ad6deb5 --- /dev/null +++ b/src/bin/m4/lib/memchr.c @@ -0,0 +1,201 @@ +/* Copyright (C) 1991, 1993, 1996, 1997, 1999, 2000, 2003, 2004, 2006 Free + Software Foundation, Inc. + + Based on strlen implementation by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se) and + commentary by Jim Blandy (jimb@ai.mit.edu); + adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu), + and implemented by Roland McGrath (roland@ai.mit.edu). + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _LIBC +# include +#endif + +#include + +#include + +#if defined _LIBC +# include +#else +# define reg_char char +#endif + +#include + +#if HAVE_BP_SYM_H || defined _LIBC +# include +#else +# define BP_SYM(sym) sym +#endif + +#undef memchr +#undef __memchr + +/* Search no more than N bytes of S for C. */ +void * +__memchr (void const *s, int c_in, size_t n) +{ + const unsigned char *char_ptr; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, charmask; + unsigned reg_char c; + int i; + + c = (unsigned char) c_in; + + /* Handle the first few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a longword boundary. */ + for (char_ptr = (const unsigned char *) s; + n > 0 && (size_t) char_ptr % sizeof longword != 0; + --n, ++char_ptr) + if (*char_ptr == c) + return (void *) char_ptr; + + /* All these elucidatory comments refer to 4-byte longwords, + but the theory applies equally well to any size longwords. */ + + longword_ptr = (const unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + + /* Set MAGIC_BITS to be this pattern of 1 and 0 bits. + Set CHARMASK to be a longword, each of whose bytes is C. */ + + magic_bits = 0xfefefefe; + charmask = c | (c << 8); + charmask |= charmask << 16; +#if 0xffffffffU < ULONG_MAX + magic_bits |= magic_bits << 32; + charmask |= charmask << 32; + if (8 < sizeof longword) + for (i = 64; i < sizeof longword * 8; i *= 2) + { + magic_bits |= magic_bits << i; + charmask |= charmask << i; + } +#endif + magic_bits = (ULONG_MAX >> 1) & (magic_bits | 1); + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + while (n >= sizeof longword) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. + + 3) But wait! Aren't we looking for C, not zero? + Good point. So what we do is XOR LONGWORD with a longword, + each of whose bytes is C. This turns each byte that is C + into a zero. */ + + longword = *longword_ptr++ ^ charmask; + + /* Add MAGIC_BITS to LONGWORD. */ + if ((((longword + magic_bits) + + /* Set those bits that were unchanged by the addition. */ + ^ ~longword) + + /* Look at only the hole bits. If any of the hole bits + are unchanged, most likely one of the bytes was a + zero. */ + & ~magic_bits) != 0) + { + /* Which of the bytes was C? If none of them were, it was + a misfire; continue the search. */ + + const unsigned char *cp = (const unsigned char *) (longword_ptr - 1); + + if (cp[0] == c) + return (void *) cp; + if (cp[1] == c) + return (void *) &cp[1]; + if (cp[2] == c) + return (void *) &cp[2]; + if (cp[3] == c) + return (void *) &cp[3]; + if (4 < sizeof longword && cp[4] == c) + return (void *) &cp[4]; + if (5 < sizeof longword && cp[5] == c) + return (void *) &cp[5]; + if (6 < sizeof longword && cp[6] == c) + return (void *) &cp[6]; + if (7 < sizeof longword && cp[7] == c) + return (void *) &cp[7]; + if (8 < sizeof longword) + for (i = 8; i < sizeof longword; i++) + if (cp[i] == c) + return (void *) &cp[i]; + } + + n -= sizeof longword; + } + + char_ptr = (const unsigned char *) longword_ptr; + + while (n-- > 0) + { + if (*char_ptr == c) + return (void *) char_ptr; + else + ++char_ptr; + } + + return 0; +} +#ifdef weak_alias +weak_alias (__memchr, BP_SYM (memchr)) +#endif diff --git a/src/bin/m4/lib/mkdtemp.c b/src/bin/m4/lib/mkdtemp.c new file mode 100644 index 0000000000..e916d385b1 --- /dev/null +++ b/src/bin/m4/lib/mkdtemp.c @@ -0,0 +1,40 @@ +/* Copyright (C) 1999, 2001-2003, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Extracted from misc/mkdtemp.c. */ + +#include + +/* Specification. */ +#include "mkdtemp.h" + +#include "tempname.h" +#include + +/* Generate a unique temporary directory from TEMPLATE. + The last six characters of TEMPLATE must be "XXXXXX"; + they are replaced with a string that makes the filename unique. + The directory is created, mode 700, and its name is returned. + (This function comes from OpenBSD.) */ +char * +mkdtemp (char *template) +{ + if (gen_tempname (template, GT_DIR)) + return NULL; + else + return template; +} diff --git a/src/bin/m4/lib/mkdtemp.h b/src/bin/m4/lib/mkdtemp.h new file mode 100644 index 0000000000..6176048019 --- /dev/null +++ b/src/bin/m4/lib/mkdtemp.h @@ -0,0 +1,32 @@ +/* Creating a private temporary directory. + Copyright (C) 2001-2002 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#if HAVE_MKDTEMP + +/* Get mkdtemp() declaration. */ +#include + +#else + +/* Create a unique temporary directory from TEMPLATE. + The last six characters of TEMPLATE must be "XXXXXX"; + they are replaced with a string that makes the directory name unique. + Returns TEMPLATE, or a null pointer if it cannot get a unique name. + The directory is created mode 700. */ +extern char * mkdtemp (char *template); + +#endif diff --git a/src/bin/m4/lib/mkstemp-safer.c b/src/bin/m4/lib/mkstemp-safer.c new file mode 100644 index 0000000000..c8c5bbd6e4 --- /dev/null +++ b/src/bin/m4/lib/mkstemp-safer.c @@ -0,0 +1,35 @@ +/* Invoke mkstemp, but avoid some glitches. + + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#include + +#include "stdlib-safer.h" + +#include "mkstemp.h" +#include "unistd-safer.h" + +/* Like mkstemp, but do not return STDIN_FILENO, STDOUT_FILENO, or + STDERR_FILENO. */ + +int +mkstemp_safer (char *templ) +{ + return fd_safer (mkstemp (templ)); +} diff --git a/src/bin/m4/lib/mkstemp.c b/src/bin/m4/lib/mkstemp.c new file mode 100644 index 0000000000..34c9e474c0 --- /dev/null +++ b/src/bin/m4/lib/mkstemp.c @@ -0,0 +1,42 @@ +/* Copyright (C) 1998, 1999, 2001, 2005, 2006 Free Software Foundation, Inc. + This file is derived from the one in the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#if !_LIBC +# include +# include "mkstemp.h" +# include "tempname.h" +# define __gen_tempname gen_tempname +# define __GT_FILE GT_FILE +#endif + +#include +#include + +#ifndef __GT_FILE +# define __GT_FILE 0 +#endif + +/* Generate a unique temporary file name from TEMPLATE. + The last six characters of TEMPLATE must be "XXXXXX"; + they are replaced with a string that makes the file name unique. + Then open the file and return a fd. */ +int +mkstemp (template) + char *template; +{ + return __gen_tempname (template, __GT_FILE); +} diff --git a/src/bin/m4/lib/mkstemp.h b/src/bin/m4/lib/mkstemp.h new file mode 100644 index 0000000000..9ed74116d2 --- /dev/null +++ b/src/bin/m4/lib/mkstemp.h @@ -0,0 +1,31 @@ +/* Create a unique temporary file. + + Copyright (C) 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* written by Jim Meyering */ + +#include +#include + +#ifdef __MKSTEMP_PREFIX +# define _GL_CONCAT(x, y) x ## y +# define _GL_XCONCAT(x, y) _GL_CONCAT (x, y) +# define __MKSTEMP_ID(y) _GL_XCONCAT (__MKSTEMP_PREFIX, y) +# undef mkstemp +# define mkstemp __MKSTEMP_ID (mkstemp) +int mkstemp (char *); +#endif diff --git a/src/bin/m4/lib/obstack.c b/src/bin/m4/lib/obstack.c new file mode 100644 index 0000000000..5cd0b7a6d0 --- /dev/null +++ b/src/bin/m4/lib/obstack.c @@ -0,0 +1,431 @@ +/* obstack.c - subroutines used implicitly by object stack macros + + Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software + Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifdef _LIBC +# include +# include +#else +# include +# include "obstack.h" +#endif + +/* NOTE BEFORE MODIFYING THIS FILE: This version number must be + incremented whenever callers compiled using an old obstack.h can no + longer properly call the functions in this obstack.c. */ +#define OBSTACK_INTERFACE_VERSION 1 + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself, and the installed library + supports the same library interface we do. This code is part of the GNU + C Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object + files, it is simpler to just do this in the source for each such file. */ + +#include /* Random thing to get __GNU_LIBRARY__. */ +#if !defined _LIBC && defined __GNU_LIBRARY__ && __GNU_LIBRARY__ > 1 +# include +# if _GNU_OBSTACK_INTERFACE_VERSION == OBSTACK_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#include + +#ifndef ELIDE_CODE + +# include + +/* Determine default alignment. */ +union fooround +{ + uintmax_t i; + long double d; + void *p; +}; +struct fooalign +{ + char c; + union fooround u; +}; +/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT. + But in fact it might be less smart and round addresses to as much as + DEFAULT_ROUNDING. So we prepare for it to do that. */ +enum + { + DEFAULT_ALIGNMENT = offsetof (struct fooalign, u), + DEFAULT_ROUNDING = sizeof (union fooround) + }; + +/* When we copy a long block of data, this is the unit to do it with. + On some machines, copying successive ints does not work; + in such a case, redefine COPYING_UNIT to `long' (if that works) + or `char' as a last resort. */ +# ifndef COPYING_UNIT +# define COPYING_UNIT int +# endif + + +/* The functions allocating more room by calling `obstack_chunk_alloc' + jump to the handler pointed to by `obstack_alloc_failed_handler'. + This can be set to a user defined function which should either + abort gracefully or use longjump - but shouldn't return. This + variable by default points to the internal function + `print_and_abort'. */ +static void print_and_abort (void); +void (*obstack_alloc_failed_handler) (void) = print_and_abort; + +/* Exit value used when `print_and_abort' is used. */ +# include +# ifdef _LIBC +int obstack_exit_failure = EXIT_FAILURE; +# else +# include "exitfail.h" +# define obstack_exit_failure exit_failure +# endif + +# ifdef _LIBC +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4) +/* A looong time ago (before 1994, anyway; we're not sure) this global variable + was used by non-GNU-C macros to avoid multiple evaluation. The GNU C + library still exports it because somebody might use it. */ +struct obstack *_obstack_compat; +compat_symbol (libc, _obstack_compat, _obstack, GLIBC_2_0); +# endif +# endif + +/* Define a macro that either calls functions with the traditional malloc/free + calling interface, or calls functions with the mmalloc/mfree interface + (that adds an extra first argument), based on the state of use_extra_arg. + For free, do not use ?:, since some compilers, like the MIPS compilers, + do not allow (expr) ? void : void. */ + +# define CALL_CHUNKFUN(h, size) \ + (((h) -> use_extra_arg) \ + ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \ + : (*(struct _obstack_chunk *(*) (long)) (h)->chunkfun) ((size))) + +# define CALL_FREEFUN(h, old_chunk) \ + do { \ + if ((h) -> use_extra_arg) \ + (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \ + else \ + (*(void (*) (void *)) (h)->freefun) ((old_chunk)); \ + } while (0) + + +/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default). + Objects start on multiples of ALIGNMENT (0 means use default). + CHUNKFUN is the function to use to allocate chunks, + and FREEFUN the function to free them. + + Return nonzero if successful, calls obstack_alloc_failed_handler if + allocation fails. */ + +int +_obstack_begin (struct obstack *h, + int size, int alignment, + void *(*chunkfun) (long), + void (*freefun) (void *)) +{ + register struct _obstack_chunk *chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun; + h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + h->use_extra_arg = 0; + + chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size); + if (!chunk) + (*obstack_alloc_failed_handler) (); + h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents, + alignment - 1); + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; + /* The initial chunk now contains no empty object. */ + h->maybe_empty_object = 0; + h->alloc_failed = 0; + return 1; +} + +int +_obstack_begin_1 (struct obstack *h, int size, int alignment, + void *(*chunkfun) (void *, long), + void (*freefun) (void *, void *), + void *arg) +{ + register struct _obstack_chunk *chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun; + h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + h->extra_arg = arg; + h->use_extra_arg = 1; + + chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size); + if (!chunk) + (*obstack_alloc_failed_handler) (); + h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents, + alignment - 1); + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; + /* The initial chunk now contains no empty object. */ + h->maybe_empty_object = 0; + h->alloc_failed = 0; + return 1; +} + +/* Allocate a new current chunk for the obstack *H + on the assumption that LENGTH bytes need to be added + to the current object, or a new object of length LENGTH allocated. + Copies any partial object from the end of the old chunk + to the beginning of the new one. */ + +void +_obstack_newchunk (struct obstack *h, int length) +{ + register struct _obstack_chunk *old_chunk = h->chunk; + register struct _obstack_chunk *new_chunk; + register long new_size; + register long obj_size = h->next_free - h->object_base; + register long i; + long already; + char *object_base; + + /* Compute size for new chunk. */ + new_size = (obj_size + length) + (obj_size >> 3) + h->alignment_mask + 100; + if (new_size < h->chunk_size) + new_size = h->chunk_size; + + /* Allocate and initialize the new chunk. */ + new_chunk = CALL_CHUNKFUN (h, new_size); + if (!new_chunk) + (*obstack_alloc_failed_handler) (); + h->chunk = new_chunk; + new_chunk->prev = old_chunk; + new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size; + + /* Compute an aligned object_base in the new chunk */ + object_base = + __PTR_ALIGN ((char *) new_chunk, new_chunk->contents, h->alignment_mask); + + /* Move the existing object to the new chunk. + Word at a time is fast and is safe if the object + is sufficiently aligned. */ + if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT) + { + for (i = obj_size / sizeof (COPYING_UNIT) - 1; + i >= 0; i--) + ((COPYING_UNIT *)object_base)[i] + = ((COPYING_UNIT *)h->object_base)[i]; + /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT, + but that can cross a page boundary on a machine + which does not do strict alignment for COPYING_UNITS. */ + already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT); + } + else + already = 0; + /* Copy remaining bytes one by one. */ + for (i = already; i < obj_size; i++) + object_base[i] = h->object_base[i]; + + /* If the object just copied was the only data in OLD_CHUNK, + free that chunk and remove it from the chain. + But not if that chunk might contain an empty object. */ + if (! h->maybe_empty_object + && (h->object_base + == __PTR_ALIGN ((char *) old_chunk, old_chunk->contents, + h->alignment_mask))) + { + new_chunk->prev = old_chunk->prev; + CALL_FREEFUN (h, old_chunk); + } + + h->object_base = object_base; + h->next_free = h->object_base + obj_size; + /* The new chunk certainly contains no empty object yet. */ + h->maybe_empty_object = 0; +} +# ifdef _LIBC +libc_hidden_def (_obstack_newchunk) +# endif + +/* Return nonzero if object OBJ has been allocated from obstack H. + This is here for debugging. + If you use it in a program, you are probably losing. */ + +/* Suppress -Wmissing-prototypes warning. We don't want to declare this in + obstack.h because it is just for debugging. */ +int _obstack_allocated_p (struct obstack *h, void *obj); + +int +_obstack_allocated_p (struct obstack *h, void *obj) +{ + register struct _obstack_chunk *lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk *plp; /* point to previous chunk if any */ + + lp = (h)->chunk; + /* We use >= rather than > since the object cannot be exactly at + the beginning of the chunk but might be an empty object exactly + at the end of an adjacent chunk. */ + while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj)) + { + plp = lp->prev; + lp = plp; + } + return lp != 0; +} + +/* Free objects in obstack H, including OBJ and everything allocate + more recently than OBJ. If OBJ is zero, free everything in H. */ + +# undef obstack_free + +void +__obstack_free (struct obstack *h, void *obj) +{ + register struct _obstack_chunk *lp; /* below addr of any objects in this chunk */ + register struct _obstack_chunk *plp; /* point to previous chunk if any */ + + lp = h->chunk; + /* We use >= because there cannot be an object at the beginning of a chunk. + But there can be an empty object at that address + at the end of another chunk. */ + while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj)) + { + plp = lp->prev; + CALL_FREEFUN (h, lp); + lp = plp; + /* If we switch chunks, we can't tell whether the new current + chunk contains an empty object, so assume that it may. */ + h->maybe_empty_object = 1; + } + if (lp) + { + h->object_base = h->next_free = (char *) (obj); + h->chunk_limit = lp->limit; + h->chunk = lp; + } + else if (obj != 0) + /* obj is not in any of the chunks! */ + abort (); +} + +# ifdef _LIBC +/* Older versions of libc used a function _obstack_free intended to be + called by non-GCC compilers. */ +strong_alias (obstack_free, _obstack_free) +# endif + +int +_obstack_memory_used (struct obstack *h) +{ + register struct _obstack_chunk* lp; + register int nbytes = 0; + + for (lp = h->chunk; lp != 0; lp = lp->prev) + { + nbytes += lp->limit - (char *) lp; + } + return nbytes; +} + +/* Define the error handler. */ +# ifdef _LIBC +# include +# else +# include "gettext.h" +# endif +# ifndef _ +# define _(msgid) gettext (msgid) +# endif + +# ifdef _LIBC +# include +# endif + +# ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) +# define __attribute__(Spec) /* empty */ +# endif +# endif + +static void +__attribute__ ((noreturn)) +print_and_abort (void) +{ + /* Don't change any of these strings. Yes, it would be possible to add + the newline to the string and use fputs or so. But this must not + happen because the "memory exhausted" message appears in other places + like this and the translation should be reused instead of creating + a very similar string which requires a separate translation. */ +# ifdef _LIBC + (void) __fxprintf (NULL, "%s\n", _("memory exhausted")); +# else + fprintf (stderr, "%s\n", _("memory exhausted")); +# endif + exit (obstack_exit_failure); +} + +#endif /* !ELIDE_CODE */ diff --git a/src/bin/m4/lib/obstack.h b/src/bin/m4/lib/obstack.h new file mode 100644 index 0000000000..3315dfe617 --- /dev/null +++ b/src/bin/m4/lib/obstack.h @@ -0,0 +1,513 @@ +/* obstack.h - object stack macros + Copyright (C) 1988-1994,1996-1999,2003,2004,2005,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Summary: + +All the apparent functions defined here are macros. The idea +is that you would use these pre-tested macros to solve a +very specific set of problems, and they would run fast. +Caution: no side-effects in arguments please!! They may be +evaluated MANY times!! + +These macros operate a stack of objects. Each object starts life +small, and may grow to maturity. (Consider building a word syllable +by syllable.) An object can move while it is growing. Once it has +been "finished" it never changes address again. So the "top of the +stack" is typically an immature growing object, while the rest of the +stack is of mature, fixed size and fixed address objects. + +These routines grab large chunks of memory, using a function you +supply, called `obstack_chunk_alloc'. On occasion, they free chunks, +by calling `obstack_chunk_free'. You must define them and declare +them before using any obstack macros. + +Each independent stack is represented by a `struct obstack'. +Each of the obstack macros expects a pointer to such a structure +as the first argument. + +One motivation for this package is the problem of growing char strings +in symbol tables. Unless you are "fascist pig with a read-only mind" +--Gosper's immortal quote from HAKMEM item 154, out of context--you +would not like to put any arbitrary upper limit on the length of your +symbols. + +In practice this often means you will build many short symbols and a +few long symbols. At the time you are reading a symbol you don't know +how long it is. One traditional method is to read a symbol into a +buffer, realloc()ating the buffer every time you try to read a symbol +that is longer than the buffer. This is beaut, but you still will +want to copy the symbol from the buffer to a more permanent +symbol-table entry say about half the time. + +With obstacks, you can work differently. Use one obstack for all symbol +names. As you read a symbol, grow the name in the obstack gradually. +When the name is complete, finalize it. Then, if the symbol exists already, +free the newly read name. + +The way we do this is to take a large chunk, allocating memory from +low addresses. When you want to build a symbol in the chunk you just +add chars above the current "high water mark" in the chunk. When you +have finished adding chars, because you got to the end of the symbol, +you know how long the chars are, and you can create a new object. +Mostly the chars will not burst over the highest address of the chunk, +because you would typically expect a chunk to be (say) 100 times as +long as an average object. + +In case that isn't clear, when we have enough chars to make up +the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed) +so we just point to it where it lies. No moving of chars is +needed and this is the second win: potentially long strings need +never be explicitly shuffled. Once an object is formed, it does not +change its address during its lifetime. + +When the chars burst over a chunk boundary, we allocate a larger +chunk, and then copy the partly formed object from the end of the old +chunk to the beginning of the new larger chunk. We then carry on +accreting characters to the end of the object as we normally would. + +A special macro is provided to add a single char at a time to a +growing object. This allows the use of register variables, which +break the ordinary 'growth' macro. + +Summary: + We allocate large chunks. + We carve out one object at a time from the current chunk. + Once carved, an object never moves. + We are free to append data of any size to the currently + growing object. + Exactly one object is growing in an obstack at any one time. + You can run one obstack per control block. + You may have as many control blocks as you dare. + Because of the way we do it, you can `unwind' an obstack + back to a previous state. (You may remove objects much + as you would with a stack.) +*/ + + +/* Don't do the contents of this file more than once. */ + +#ifndef _OBSTACK_H +#define _OBSTACK_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* We need the type of a pointer subtraction. If __PTRDIFF_TYPE__ is + defined, as with GNU C, use that; that way we don't pollute the + namespace with 's symbols. Otherwise, include + and use ptrdiff_t. */ + +#ifdef __PTRDIFF_TYPE__ +# define PTR_INT_TYPE __PTRDIFF_TYPE__ +#else +# include +# define PTR_INT_TYPE ptrdiff_t +#endif + +/* If B is the base of an object addressed by P, return the result of + aligning P to the next multiple of A + 1. B and P must be of type + char *. A + 1 must be a power of 2. */ + +#define __BPTR_ALIGN(B, P, A) ((B) + (((P) - (B) + (A)) & ~(A))) + +/* Similiar to _BPTR_ALIGN (B, P, A), except optimize the common case + where pointers can be converted to integers, aligned as integers, + and converted back again. If PTR_INT_TYPE is narrower than a + pointer (e.g., the AS/400), play it safe and compute the alignment + relative to B. Otherwise, use the faster strategy of computing the + alignment relative to 0. */ + +#define __PTR_ALIGN(B, P, A) \ + __BPTR_ALIGN (sizeof (PTR_INT_TYPE) < sizeof (void *) ? (B) : (char *) 0, \ + P, A) + +#include + +struct _obstack_chunk /* Lives at front of each chunk. */ +{ + char *limit; /* 1 past end of this chunk */ + struct _obstack_chunk *prev; /* address of prior chunk or NULL */ + char contents[4]; /* objects begin here */ +}; + +struct obstack /* control current object in current chunk */ +{ + long chunk_size; /* preferred size to allocate chunks in */ + struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */ + char *object_base; /* address of object we are building */ + char *next_free; /* where to add next char to current object */ + char *chunk_limit; /* address of char after current chunk */ + union + { + PTR_INT_TYPE tempint; + void *tempptr; + } temp; /* Temporary for some macros. */ + int alignment_mask; /* Mask of alignment for each object. */ + /* These prototypes vary based on `use_extra_arg', and we use + casts to the prototypeless function type in all assignments, + but having prototypes here quiets -Wstrict-prototypes. */ + struct _obstack_chunk *(*chunkfun) (void *, long); + void (*freefun) (void *, struct _obstack_chunk *); + void *extra_arg; /* first arg for chunk alloc/dealloc funcs */ + unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */ + unsigned maybe_empty_object:1;/* There is a possibility that the current + chunk contains a zero-length object. This + prevents freeing the chunk if we allocate + a bigger chunk to replace it. */ + unsigned alloc_failed:1; /* No longer used, as we now call the failed + handler on error, but retained for binary + compatibility. */ +}; + +/* Declare the external functions we use; they are in obstack.c. */ + +extern void _obstack_newchunk (struct obstack *, int); +extern int _obstack_begin (struct obstack *, int, int, + void *(*) (long), void (*) (void *)); +extern int _obstack_begin_1 (struct obstack *, int, int, + void *(*) (void *, long), + void (*) (void *, void *), void *); +extern int _obstack_memory_used (struct obstack *); + +/* The default name of the function for freeing a chunk is 'obstack_free', + but gnulib users can override this by defining '__obstack_free'. */ +#ifndef __obstack_free +# define __obstack_free obstack_free +#endif +extern void __obstack_free (struct obstack *obstack, void *block); + + +/* Error handler called when `obstack_chunk_alloc' failed to allocate + more memory. This can be set to a user defined function which + should either abort gracefully or use longjump - but shouldn't + return. The default action is to print a message and abort. */ +extern void (*obstack_alloc_failed_handler) (void); + +/* Exit value used when `print_and_abort' is used. */ +extern int obstack_exit_failure; + +/* Pointer to beginning of object being allocated or to be allocated next. + Note that this might not be the final address of the object + because a new chunk might be needed to hold the final size. */ + +#define obstack_base(h) ((void *) (h)->object_base) + +/* Size for allocating ordinary chunks. */ + +#define obstack_chunk_size(h) ((h)->chunk_size) + +/* Pointer to next byte not yet allocated in current chunk. */ + +#define obstack_next_free(h) ((h)->next_free) + +/* Mask specifying low bits that should be clear in address of an object. */ + +#define obstack_alignment_mask(h) ((h)->alignment_mask) + +/* To prevent prototype warnings provide complete argument list. */ +#define obstack_init(h) \ + _obstack_begin ((h), 0, 0, \ + (void *(*) (long)) obstack_chunk_alloc, \ + (void (*) (void *)) obstack_chunk_free) + +#define obstack_begin(h, size) \ + _obstack_begin ((h), (size), 0, \ + (void *(*) (long)) obstack_chunk_alloc, \ + (void (*) (void *)) obstack_chunk_free) + +#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \ + _obstack_begin ((h), (size), (alignment), \ + (void *(*) (long)) (chunkfun), \ + (void (*) (void *)) (freefun)) + +#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \ + _obstack_begin_1 ((h), (size), (alignment), \ + (void *(*) (void *, long)) (chunkfun), \ + (void (*) (void *, void *)) (freefun), (arg)) + +#define obstack_chunkfun(h, newchunkfun) \ + ((h) -> chunkfun = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun)) + +#define obstack_freefun(h, newfreefun) \ + ((h) -> freefun = (void (*)(void *, struct _obstack_chunk *)) (newfreefun)) + +#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = (achar)) + +#define obstack_blank_fast(h,n) ((h)->next_free += (n)) + +#define obstack_memory_used(h) _obstack_memory_used (h) + +#if defined __GNUC__ && defined __STDC__ && __STDC__ +/* NextStep 2.0 cc is really gcc 1.93 but it defines __GNUC__ = 2 and + does not implement __extension__. But that compiler doesn't define + __GNUC_MINOR__. */ +# if __GNUC__ < 2 || (__NeXT__ && !__GNUC_MINOR__) +# define __extension__ +# endif + +/* For GNU C, if not -traditional, + we can define these macros to compute all args only once + without using a global variable. + Also, we can avoid using the `temp' slot, to make faster code. */ + +# define obstack_object_size(OBSTACK) \ + __extension__ \ + ({ struct obstack const *__o = (OBSTACK); \ + (unsigned) (__o->next_free - __o->object_base); }) + +# define obstack_room(OBSTACK) \ + __extension__ \ + ({ struct obstack const *__o = (OBSTACK); \ + (unsigned) (__o->chunk_limit - __o->next_free); }) + +# define obstack_make_room(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->chunk_limit - __o->next_free < __len) \ + _obstack_newchunk (__o, __len); \ + (void) 0; }) + +# define obstack_empty_p(OBSTACK) \ + __extension__ \ + ({ struct obstack const *__o = (OBSTACK); \ + (__o->chunk->prev == 0 \ + && __o->next_free == __PTR_ALIGN ((char *) __o->chunk, \ + __o->chunk->contents, \ + __o->alignment_mask)); }) + +# define obstack_grow(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->next_free + __len > __o->chunk_limit) \ + _obstack_newchunk (__o, __len); \ + memcpy (__o->next_free, where, __len); \ + __o->next_free += __len; \ + (void) 0; }) + +# define obstack_grow0(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->next_free + __len + 1 > __o->chunk_limit) \ + _obstack_newchunk (__o, __len + 1); \ + memcpy (__o->next_free, where, __len); \ + __o->next_free += __len; \ + *(__o->next_free)++ = 0; \ + (void) 0; }) + +# define obstack_1grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + 1 > __o->chunk_limit) \ + _obstack_newchunk (__o, 1); \ + obstack_1grow_fast (__o, datum); \ + (void) 0; }) + +/* These assume that the obstack alignment is good enough for pointers + or ints, and that the data added so far to the current object + shares that much alignment. */ + +# define obstack_ptr_grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + sizeof (void *) > __o->chunk_limit) \ + _obstack_newchunk (__o, sizeof (void *)); \ + obstack_ptr_grow_fast (__o, datum); }) \ + +# define obstack_int_grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + sizeof (int) > __o->chunk_limit) \ + _obstack_newchunk (__o, sizeof (int)); \ + obstack_int_grow_fast (__o, datum); }) + +# define obstack_ptr_grow_fast(OBSTACK,aptr) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + *(const void **) __o1->next_free = (aptr); \ + __o1->next_free += sizeof (const void *); \ + (void) 0; }) + +# define obstack_int_grow_fast(OBSTACK,aint) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + *(int *) __o1->next_free = (aint); \ + __o1->next_free += sizeof (int); \ + (void) 0; }) + +# define obstack_blank(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->chunk_limit - __o->next_free < __len) \ + _obstack_newchunk (__o, __len); \ + obstack_blank_fast (__o, __len); \ + (void) 0; }) + +# define obstack_alloc(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_blank (__h, (length)); \ + obstack_finish (__h); }) + +# define obstack_copy(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow (__h, (where), (length)); \ + obstack_finish (__h); }) + +# define obstack_copy0(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow0 (__h, (where), (length)); \ + obstack_finish (__h); }) + +/* The local variable is named __o1 to avoid a name conflict + when obstack_blank is called. */ +# define obstack_finish(OBSTACK) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + void *__value = (void *) __o1->object_base; \ + if (__o1->next_free == __value) \ + __o1->maybe_empty_object = 1; \ + __o1->next_free \ + = __PTR_ALIGN (__o1->object_base, __o1->next_free, \ + __o1->alignment_mask); \ + if (__o1->next_free - (char *)__o1->chunk \ + > __o1->chunk_limit - (char *)__o1->chunk) \ + __o1->next_free = __o1->chunk_limit; \ + __o1->object_base = __o1->next_free; \ + __value; }) + +# define obstack_free(OBSTACK, OBJ) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + void *__obj = (OBJ); \ + if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \ + __o->next_free = __o->object_base = (char *)__obj; \ + else (__obstack_free) (__o, __obj); }) + +#else /* not __GNUC__ or not __STDC__ */ + +# define obstack_object_size(h) \ + (unsigned) ((h)->next_free - (h)->object_base) + +# define obstack_room(h) \ + (unsigned) ((h)->chunk_limit - (h)->next_free) + +# define obstack_empty_p(h) \ + ((h)->chunk->prev == 0 \ + && (h)->next_free == __PTR_ALIGN ((char *) (h)->chunk, \ + (h)->chunk->contents, \ + (h)->alignment_mask)) + +/* Note that the call to _obstack_newchunk is enclosed in (..., 0) + so that we can avoid having void expressions + in the arms of the conditional expression. + Casting the third operand to void was tried before, + but some compilers won't accept it. */ + +# define obstack_make_room(h,length) \ +( (h)->temp.tempint = (length), \ + (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0)) + +# define obstack_grow(h,where,length) \ +( (h)->temp.tempint = (length), \ + (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0), \ + memcpy ((h)->next_free, where, (h)->temp.tempint), \ + (h)->next_free += (h)->temp.tempint) + +# define obstack_grow0(h,where,length) \ +( (h)->temp.tempint = (length), \ + (((h)->next_free + (h)->temp.tempint + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint + 1), 0) : 0), \ + memcpy ((h)->next_free, where, (h)->temp.tempint), \ + (h)->next_free += (h)->temp.tempint, \ + *((h)->next_free)++ = 0) + +# define obstack_1grow(h,datum) \ +( (((h)->next_free + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), 1), 0) : 0), \ + obstack_1grow_fast (h, datum)) + +# define obstack_ptr_grow(h,datum) \ +( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \ + obstack_ptr_grow_fast (h, datum)) + +# define obstack_int_grow(h,datum) \ +( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \ + obstack_int_grow_fast (h, datum)) + +# define obstack_ptr_grow_fast(h,aptr) \ + (((const void **) ((h)->next_free += sizeof (void *)))[-1] = (aptr)) + +# define obstack_int_grow_fast(h,aint) \ + (((int *) ((h)->next_free += sizeof (int)))[-1] = (aint)) + +# define obstack_blank(h,length) \ +( (h)->temp.tempint = (length), \ + (((h)->chunk_limit - (h)->next_free < (h)->temp.tempint) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0), \ + obstack_blank_fast (h, (h)->temp.tempint)) + +# define obstack_alloc(h,length) \ + (obstack_blank ((h), (length)), obstack_finish ((h))) + +# define obstack_copy(h,where,length) \ + (obstack_grow ((h), (where), (length)), obstack_finish ((h))) + +# define obstack_copy0(h,where,length) \ + (obstack_grow0 ((h), (where), (length)), obstack_finish ((h))) + +# define obstack_finish(h) \ +( ((h)->next_free == (h)->object_base \ + ? (((h)->maybe_empty_object = 1), 0) \ + : 0), \ + (h)->temp.tempptr = (h)->object_base, \ + (h)->next_free \ + = __PTR_ALIGN ((h)->object_base, (h)->next_free, \ + (h)->alignment_mask), \ + (((h)->next_free - (char *) (h)->chunk \ + > (h)->chunk_limit - (char *) (h)->chunk) \ + ? ((h)->next_free = (h)->chunk_limit) : 0), \ + (h)->object_base = (h)->next_free, \ + (h)->temp.tempptr) + +# define obstack_free(h,obj) \ +( (h)->temp.tempint = (char *) (obj) - (char *) (h)->chunk, \ + ((((h)->temp.tempint > 0 \ + && (h)->temp.tempint < (h)->chunk_limit - (char *) (h)->chunk)) \ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp.tempint + (char *) (h)->chunk) \ + : (((__obstack_free) ((h), (h)->temp.tempint + (char *) (h)->chunk), 0), 0))) + +#endif /* not __GNUC__ or not __STDC__ */ + +#ifdef __cplusplus +} /* C++ */ +#endif + +#endif /* obstack.h */ diff --git a/src/bin/m4/lib/pathmax.h b/src/bin/m4/lib/pathmax.h new file mode 100644 index 0000000000..6941e457e3 --- /dev/null +++ b/src/bin/m4/lib/pathmax.h @@ -0,0 +1,47 @@ +/* Define PATH_MAX somehow. Requires sys/types.h. + Copyright (C) 1992, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _PATHMAX_H +# define _PATHMAX_H + +# include + +# include + +# ifndef _POSIX_PATH_MAX +# define _POSIX_PATH_MAX 256 +# endif + +# if !defined PATH_MAX && defined _PC_PATH_MAX +# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 \ + : pathconf ("/", _PC_PATH_MAX)) +# endif + +/* Don't include sys/param.h if it already has been. */ +# if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN +# include +# endif + +# if !defined PATH_MAX && defined MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# endif + +# ifndef PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +# endif + +#endif /* _PATHMAX_H */ diff --git a/src/bin/m4/lib/pipe-safer.c b/src/bin/m4/lib/pipe-safer.c new file mode 100644 index 0000000000..e4431b33a4 --- /dev/null +++ b/src/bin/m4/lib/pipe-safer.c @@ -0,0 +1,57 @@ +/* Invoke pipe, but avoid some glitches. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Jim Meyering. */ + +#include + +#include "unistd-safer.h" + +#include +#include + +/* Like pipe, but ensure that neither of the file descriptors is + STDIN_FILENO, STDOUT_FILENO, or STDERR_FILENO. Fail with ENOSYS on + platforms that lack pipe. */ + +int +pipe_safer (int fd[2]) +{ +#if HAVE_PIPE + if (pipe (fd) == 0) + { + int i; + for (i = 0; i < 2; i++) + { + fd[i] = fd_safer (fd[i]); + if (fd[i] < 0) + { + int e = errno; + close (fd[1 - i]); + errno = e; + return -1; + } + } + + return 0; + } +#else + errno = ENOSYS; +#endif + + return -1; +} diff --git a/src/bin/m4/lib/printf-args.c b/src/bin/m4/lib/printf-args.c new file mode 100644 index 0000000000..358801c97b --- /dev/null +++ b/src/bin/m4/lib/printf-args.c @@ -0,0 +1,141 @@ +/* Decomposed printf argument list. + Copyright (C) 1999, 2002-2003, 2005-2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "printf-args.h" + +#ifdef STATIC +STATIC +#endif +int +printf_fetchargs (va_list args, arguments *a) +{ + size_t i; + argument *ap; + + for (i = 0, ap = &a->arg[0]; i < a->count; i++, ap++) + switch (ap->type) + { + case TYPE_SCHAR: + ap->a.a_schar = va_arg (args, /*signed char*/ int); + break; + case TYPE_UCHAR: + ap->a.a_uchar = va_arg (args, /*unsigned char*/ int); + break; + case TYPE_SHORT: + ap->a.a_short = va_arg (args, /*short*/ int); + break; + case TYPE_USHORT: + ap->a.a_ushort = va_arg (args, /*unsigned short*/ int); + break; + case TYPE_INT: + ap->a.a_int = va_arg (args, int); + break; + case TYPE_UINT: + ap->a.a_uint = va_arg (args, unsigned int); + break; + case TYPE_LONGINT: + ap->a.a_longint = va_arg (args, long int); + break; + case TYPE_ULONGINT: + ap->a.a_ulongint = va_arg (args, unsigned long int); + break; +#ifdef HAVE_LONG_LONG_INT + case TYPE_LONGLONGINT: + ap->a.a_longlongint = va_arg (args, long long int); + break; + case TYPE_ULONGLONGINT: + ap->a.a_ulonglongint = va_arg (args, unsigned long long int); + break; +#endif + case TYPE_DOUBLE: + ap->a.a_double = va_arg (args, double); + break; +#ifdef HAVE_LONG_DOUBLE + case TYPE_LONGDOUBLE: + ap->a.a_longdouble = va_arg (args, long double); + break; +#endif + case TYPE_CHAR: + ap->a.a_char = va_arg (args, int); + break; +#ifdef HAVE_WINT_T + case TYPE_WIDE_CHAR: + /* Although ISO C 99 7.24.1.(2) says that wint_t is "unchanged by + default argument promotions", this is not the case in mingw32, + where wint_t is 'unsigned short'. */ + ap->a.a_wide_char = + (sizeof (wint_t) < sizeof (int) + ? va_arg (args, int) + : va_arg (args, wint_t)); + break; +#endif + case TYPE_STRING: + ap->a.a_string = va_arg (args, const char *); + /* A null pointer is an invalid argument for "%s", but in practice + it occurs quite frequently in printf statements that produce + debug output. Use a fallback in this case. */ + if (ap->a.a_string == NULL) + ap->a.a_string = "(NULL)"; + break; +#ifdef HAVE_WCHAR_T + case TYPE_WIDE_STRING: + ap->a.a_wide_string = va_arg (args, const wchar_t *); + /* A null pointer is an invalid argument for "%ls", but in practice + it occurs quite frequently in printf statements that produce + debug output. Use a fallback in this case. */ + if (ap->a.a_wide_string == NULL) + { + static const wchar_t wide_null_string[] = + { + (wchar_t)'(', + (wchar_t)'N', (wchar_t)'U', (wchar_t)'L', (wchar_t)'L', + (wchar_t)')', + (wchar_t)0 + }; + ap->a.a_wide_string = wide_null_string; + } + break; +#endif + case TYPE_POINTER: + ap->a.a_pointer = va_arg (args, void *); + break; + case TYPE_COUNT_SCHAR_POINTER: + ap->a.a_count_schar_pointer = va_arg (args, signed char *); + break; + case TYPE_COUNT_SHORT_POINTER: + ap->a.a_count_short_pointer = va_arg (args, short *); + break; + case TYPE_COUNT_INT_POINTER: + ap->a.a_count_int_pointer = va_arg (args, int *); + break; + case TYPE_COUNT_LONGINT_POINTER: + ap->a.a_count_longint_pointer = va_arg (args, long int *); + break; +#ifdef HAVE_LONG_LONG_INT + case TYPE_COUNT_LONGLONGINT_POINTER: + ap->a.a_count_longlongint_pointer = va_arg (args, long long int *); + break; +#endif + default: + /* Unknown type. */ + return -1; + } + return 0; +} diff --git a/src/bin/m4/lib/printf-args.h b/src/bin/m4/lib/printf-args.h new file mode 100644 index 0000000000..5759da0ee9 --- /dev/null +++ b/src/bin/m4/lib/printf-args.h @@ -0,0 +1,136 @@ +/* Decomposed printf argument list. + Copyright (C) 1999, 2002-2003, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _PRINTF_ARGS_H +#define _PRINTF_ARGS_H + +/* Get size_t. */ +#include + +/* Get wchar_t. */ +#ifdef HAVE_WCHAR_T +# include +#endif + +/* Get wint_t. */ +#ifdef HAVE_WINT_T +# include +#endif + +/* Get va_list. */ +#include + + +/* Argument types */ +typedef enum +{ + TYPE_NONE, + TYPE_SCHAR, + TYPE_UCHAR, + TYPE_SHORT, + TYPE_USHORT, + TYPE_INT, + TYPE_UINT, + TYPE_LONGINT, + TYPE_ULONGINT, +#ifdef HAVE_LONG_LONG_INT + TYPE_LONGLONGINT, + TYPE_ULONGLONGINT, +#endif + TYPE_DOUBLE, +#ifdef HAVE_LONG_DOUBLE + TYPE_LONGDOUBLE, +#endif + TYPE_CHAR, +#ifdef HAVE_WINT_T + TYPE_WIDE_CHAR, +#endif + TYPE_STRING, +#ifdef HAVE_WCHAR_T + TYPE_WIDE_STRING, +#endif + TYPE_POINTER, + TYPE_COUNT_SCHAR_POINTER, + TYPE_COUNT_SHORT_POINTER, + TYPE_COUNT_INT_POINTER, + TYPE_COUNT_LONGINT_POINTER +#ifdef HAVE_LONG_LONG_INT +, TYPE_COUNT_LONGLONGINT_POINTER +#endif +} arg_type; + +/* Polymorphic argument */ +typedef struct +{ + arg_type type; + union + { + signed char a_schar; + unsigned char a_uchar; + short a_short; + unsigned short a_ushort; + int a_int; + unsigned int a_uint; + long int a_longint; + unsigned long int a_ulongint; +#ifdef HAVE_LONG_LONG_INT + long long int a_longlongint; + unsigned long long int a_ulonglongint; +#endif + float a_float; + double a_double; +#ifdef HAVE_LONG_DOUBLE + long double a_longdouble; +#endif + int a_char; +#ifdef HAVE_WINT_T + wint_t a_wide_char; +#endif + const char* a_string; +#ifdef HAVE_WCHAR_T + const wchar_t* a_wide_string; +#endif + void* a_pointer; + signed char * a_count_schar_pointer; + short * a_count_short_pointer; + int * a_count_int_pointer; + long int * a_count_longint_pointer; +#ifdef HAVE_LONG_LONG_INT + long long int * a_count_longlongint_pointer; +#endif + } + a; +} +argument; + +typedef struct +{ + size_t count; + argument *arg; +} +arguments; + + +/* Fetch the arguments, putting them into a. */ +#ifdef STATIC +STATIC +#else +extern +#endif +int printf_fetchargs (va_list args, arguments *a); + +#endif /* _PRINTF_ARGS_H */ diff --git a/src/bin/m4/lib/printf-parse.c b/src/bin/m4/lib/printf-parse.c new file mode 100644 index 0000000000..9a86f7738e --- /dev/null +++ b/src/bin/m4/lib/printf-parse.c @@ -0,0 +1,543 @@ +/* Formatted output to strings. + Copyright (C) 1999-2000, 2002-2003, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#if WIDE_CHAR_VERSION +# include "wprintf-parse.h" +#else +# include "printf-parse.h" +#endif + +/* Get size_t, NULL. */ +#include + +/* Get intmax_t. */ +#if HAVE_STDINT_H_WITH_UINTMAX +# include +#endif +#if HAVE_INTTYPES_H_WITH_UINTMAX +# include +#endif + +/* malloc(), realloc(), free(). */ +#include + +/* Checked size_t computations. */ +#include "xsize.h" + +#if WIDE_CHAR_VERSION +# define PRINTF_PARSE wprintf_parse +# define CHAR_T wchar_t +# define DIRECTIVE wchar_t_directive +# define DIRECTIVES wchar_t_directives +#else +# define PRINTF_PARSE printf_parse +# define CHAR_T char +# define DIRECTIVE char_directive +# define DIRECTIVES char_directives +#endif + +#ifdef STATIC +STATIC +#endif +int +PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a) +{ + const CHAR_T *cp = format; /* pointer into format */ + size_t arg_posn = 0; /* number of regular arguments consumed */ + size_t d_allocated; /* allocated elements of d->dir */ + size_t a_allocated; /* allocated elements of a->arg */ + size_t max_width_length = 0; + size_t max_precision_length = 0; + + d->count = 0; + d_allocated = 1; + d->dir = (DIRECTIVE *) malloc (d_allocated * sizeof (DIRECTIVE)); + if (d->dir == NULL) + /* Out of memory. */ + return -1; + + a->count = 0; + a_allocated = 0; + a->arg = NULL; + +#define REGISTER_ARG(_index_,_type_) \ + { \ + size_t n = (_index_); \ + if (n >= a_allocated) \ + { \ + size_t memory_size; \ + argument *memory; \ + \ + a_allocated = xtimes (a_allocated, 2); \ + if (a_allocated <= n) \ + a_allocated = xsum (n, 1); \ + memory_size = xtimes (a_allocated, sizeof (argument)); \ + if (size_overflow_p (memory_size)) \ + /* Overflow, would lead to out of memory. */ \ + goto error; \ + memory = (argument *) (a->arg \ + ? realloc (a->arg, memory_size) \ + : malloc (memory_size)); \ + if (memory == NULL) \ + /* Out of memory. */ \ + goto error; \ + a->arg = memory; \ + } \ + while (a->count <= n) \ + a->arg[a->count++].type = TYPE_NONE; \ + if (a->arg[n].type == TYPE_NONE) \ + a->arg[n].type = (_type_); \ + else if (a->arg[n].type != (_type_)) \ + /* Ambiguous type for positional argument. */ \ + goto error; \ + } + + while (*cp != '\0') + { + CHAR_T c = *cp++; + if (c == '%') + { + size_t arg_index = ARG_NONE; + DIRECTIVE *dp = &d->dir[d->count];/* pointer to next directive */ + + /* Initialize the next directive. */ + dp->dir_start = cp - 1; + dp->flags = 0; + dp->width_start = NULL; + dp->width_end = NULL; + dp->width_arg_index = ARG_NONE; + dp->precision_start = NULL; + dp->precision_end = NULL; + dp->precision_arg_index = ARG_NONE; + dp->arg_index = ARG_NONE; + + /* Test for positional argument. */ + if (*cp >= '0' && *cp <= '9') + { + const CHAR_T *np; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + ; + if (*np == '$') + { + size_t n = 0; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + n = xsum (xtimes (n, 10), *np - '0'); + if (n == 0) + /* Positional argument 0. */ + goto error; + if (size_overflow_p (n)) + /* n too large, would lead to out of memory later. */ + goto error; + arg_index = n - 1; + cp = np + 1; + } + } + + /* Read the flags. */ + for (;;) + { + if (*cp == '\'') + { + dp->flags |= FLAG_GROUP; + cp++; + } + else if (*cp == '-') + { + dp->flags |= FLAG_LEFT; + cp++; + } + else if (*cp == '+') + { + dp->flags |= FLAG_SHOWSIGN; + cp++; + } + else if (*cp == ' ') + { + dp->flags |= FLAG_SPACE; + cp++; + } + else if (*cp == '#') + { + dp->flags |= FLAG_ALT; + cp++; + } + else if (*cp == '0') + { + dp->flags |= FLAG_ZERO; + cp++; + } + else + break; + } + + /* Parse the field width. */ + if (*cp == '*') + { + dp->width_start = cp; + cp++; + dp->width_end = cp; + if (max_width_length < 1) + max_width_length = 1; + + /* Test for positional argument. */ + if (*cp >= '0' && *cp <= '9') + { + const CHAR_T *np; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + ; + if (*np == '$') + { + size_t n = 0; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + n = xsum (xtimes (n, 10), *np - '0'); + if (n == 0) + /* Positional argument 0. */ + goto error; + if (size_overflow_p (n)) + /* n too large, would lead to out of memory later. */ + goto error; + dp->width_arg_index = n - 1; + cp = np + 1; + } + } + if (dp->width_arg_index == ARG_NONE) + { + dp->width_arg_index = arg_posn++; + if (dp->width_arg_index == ARG_NONE) + /* arg_posn wrapped around. */ + goto error; + } + REGISTER_ARG (dp->width_arg_index, TYPE_INT); + } + else if (*cp >= '0' && *cp <= '9') + { + size_t width_length; + + dp->width_start = cp; + for (; *cp >= '0' && *cp <= '9'; cp++) + ; + dp->width_end = cp; + width_length = dp->width_end - dp->width_start; + if (max_width_length < width_length) + max_width_length = width_length; + } + + /* Parse the precision. */ + if (*cp == '.') + { + cp++; + if (*cp == '*') + { + dp->precision_start = cp - 1; + cp++; + dp->precision_end = cp; + if (max_precision_length < 2) + max_precision_length = 2; + + /* Test for positional argument. */ + if (*cp >= '0' && *cp <= '9') + { + const CHAR_T *np; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + ; + if (*np == '$') + { + size_t n = 0; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + n = xsum (xtimes (n, 10), *np - '0'); + if (n == 0) + /* Positional argument 0. */ + goto error; + if (size_overflow_p (n)) + /* n too large, would lead to out of memory + later. */ + goto error; + dp->precision_arg_index = n - 1; + cp = np + 1; + } + } + if (dp->precision_arg_index == ARG_NONE) + { + dp->precision_arg_index = arg_posn++; + if (dp->precision_arg_index == ARG_NONE) + /* arg_posn wrapped around. */ + goto error; + } + REGISTER_ARG (dp->precision_arg_index, TYPE_INT); + } + else + { + size_t precision_length; + + dp->precision_start = cp - 1; + for (; *cp >= '0' && *cp <= '9'; cp++) + ; + dp->precision_end = cp; + precision_length = dp->precision_end - dp->precision_start; + if (max_precision_length < precision_length) + max_precision_length = precision_length; + } + } + + { + arg_type type; + + /* Parse argument type/size specifiers. */ + { + int flags = 0; + + for (;;) + { + if (*cp == 'h') + { + flags |= (1 << (flags & 1)); + cp++; + } + else if (*cp == 'L') + { + flags |= 4; + cp++; + } + else if (*cp == 'l') + { + flags += 8; + cp++; + } +#ifdef HAVE_INTMAX_T + else if (*cp == 'j') + { + if (sizeof (intmax_t) > sizeof (long)) + { + /* intmax_t = long long */ + flags += 16; + } + else if (sizeof (intmax_t) > sizeof (int)) + { + /* intmax_t = long */ + flags += 8; + } + cp++; + } +#endif + else if (*cp == 'z' || *cp == 'Z') + { + /* 'z' is standardized in ISO C 99, but glibc uses 'Z' + because the warning facility in gcc-2.95.2 understands + only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */ + if (sizeof (size_t) > sizeof (long)) + { + /* size_t = long long */ + flags += 16; + } + else if (sizeof (size_t) > sizeof (int)) + { + /* size_t = long */ + flags += 8; + } + cp++; + } + else if (*cp == 't') + { + if (sizeof (ptrdiff_t) > sizeof (long)) + { + /* ptrdiff_t = long long */ + flags += 16; + } + else if (sizeof (ptrdiff_t) > sizeof (int)) + { + /* ptrdiff_t = long */ + flags += 8; + } + cp++; + } + else + break; + } + + /* Read the conversion character. */ + c = *cp++; + switch (c) + { + case 'd': case 'i': +#ifdef HAVE_LONG_LONG_INT + /* If 'long long' exists and is larger than 'long': */ + if (flags >= 16 || (flags & 4)) + type = TYPE_LONGLONGINT; + else +#endif + /* If 'long long' exists and is the same as 'long', we parse + "lld" into TYPE_LONGINT. */ + if (flags >= 8) + type = TYPE_LONGINT; + else if (flags & 2) + type = TYPE_SCHAR; + else if (flags & 1) + type = TYPE_SHORT; + else + type = TYPE_INT; + break; + case 'o': case 'u': case 'x': case 'X': +#ifdef HAVE_LONG_LONG_INT + /* If 'long long' exists and is larger than 'long': */ + if (flags >= 16 || (flags & 4)) + type = TYPE_ULONGLONGINT; + else +#endif + /* If 'unsigned long long' exists and is the same as + 'unsigned long', we parse "llu" into TYPE_ULONGINT. */ + if (flags >= 8) + type = TYPE_ULONGINT; + else if (flags & 2) + type = TYPE_UCHAR; + else if (flags & 1) + type = TYPE_USHORT; + else + type = TYPE_UINT; + break; + case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': + case 'a': case 'A': +#ifdef HAVE_LONG_DOUBLE + if (flags >= 16 || (flags & 4)) + type = TYPE_LONGDOUBLE; + else +#endif + type = TYPE_DOUBLE; + break; + case 'c': + if (flags >= 8) +#ifdef HAVE_WINT_T + type = TYPE_WIDE_CHAR; +#else + goto error; +#endif + else + type = TYPE_CHAR; + break; +#ifdef HAVE_WINT_T + case 'C': + type = TYPE_WIDE_CHAR; + c = 'c'; + break; +#endif + case 's': + if (flags >= 8) +#ifdef HAVE_WCHAR_T + type = TYPE_WIDE_STRING; +#else + goto error; +#endif + else + type = TYPE_STRING; + break; +#ifdef HAVE_WCHAR_T + case 'S': + type = TYPE_WIDE_STRING; + c = 's'; + break; +#endif + case 'p': + type = TYPE_POINTER; + break; + case 'n': +#ifdef HAVE_LONG_LONG_INT + /* If 'long long' exists and is larger than 'long': */ + if (flags >= 16 || (flags & 4)) + type = TYPE_COUNT_LONGLONGINT_POINTER; + else +#endif + /* If 'long long' exists and is the same as 'long', we parse + "lln" into TYPE_COUNT_LONGINT_POINTER. */ + if (flags >= 8) + type = TYPE_COUNT_LONGINT_POINTER; + else if (flags & 2) + type = TYPE_COUNT_SCHAR_POINTER; + else if (flags & 1) + type = TYPE_COUNT_SHORT_POINTER; + else + type = TYPE_COUNT_INT_POINTER; + break; + case '%': + type = TYPE_NONE; + break; + default: + /* Unknown conversion character. */ + goto error; + } + } + + if (type != TYPE_NONE) + { + dp->arg_index = arg_index; + if (dp->arg_index == ARG_NONE) + { + dp->arg_index = arg_posn++; + if (dp->arg_index == ARG_NONE) + /* arg_posn wrapped around. */ + goto error; + } + REGISTER_ARG (dp->arg_index, type); + } + dp->conversion = c; + dp->dir_end = cp; + } + + d->count++; + if (d->count >= d_allocated) + { + size_t memory_size; + DIRECTIVE *memory; + + d_allocated = xtimes (d_allocated, 2); + memory_size = xtimes (d_allocated, sizeof (DIRECTIVE)); + if (size_overflow_p (memory_size)) + /* Overflow, would lead to out of memory. */ + goto error; + memory = (DIRECTIVE *) realloc (d->dir, memory_size); + if (memory == NULL) + /* Out of memory. */ + goto error; + d->dir = memory; + } + } + } + d->dir[d->count].dir_start = cp; + + d->max_width_length = max_width_length; + d->max_precision_length = max_precision_length; + return 0; + +error: + if (a->arg) + free (a->arg); + if (d->dir) + free (d->dir); + return -1; +} + +#undef DIRECTIVES +#undef DIRECTIVE +#undef CHAR_T +#undef PRINTF_PARSE diff --git a/src/bin/m4/lib/printf-parse.h b/src/bin/m4/lib/printf-parse.h new file mode 100644 index 0000000000..82a0d37cd7 --- /dev/null +++ b/src/bin/m4/lib/printf-parse.h @@ -0,0 +1,74 @@ +/* Parse printf format string. + Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _PRINTF_PARSE_H +#define _PRINTF_PARSE_H + +#include "printf-args.h" + + +/* Flags */ +#define FLAG_GROUP 1 /* ' flag */ +#define FLAG_LEFT 2 /* - flag */ +#define FLAG_SHOWSIGN 4 /* + flag */ +#define FLAG_SPACE 8 /* space flag */ +#define FLAG_ALT 16 /* # flag */ +#define FLAG_ZERO 32 + +/* arg_index value indicating that no argument is consumed. */ +#define ARG_NONE (~(size_t)0) + +/* A parsed directive. */ +typedef struct +{ + const char* dir_start; + const char* dir_end; + int flags; + const char* width_start; + const char* width_end; + size_t width_arg_index; + const char* precision_start; + const char* precision_end; + size_t precision_arg_index; + char conversion; /* d i o u x X f e E g G c s p n U % but not C S */ + size_t arg_index; +} +char_directive; + +/* A parsed format string. */ +typedef struct +{ + size_t count; + char_directive *dir; + size_t max_width_length; + size_t max_precision_length; +} +char_directives; + + +/* Parses the format string. Fills in the number N of directives, and fills + in directives[0], ..., directives[N-1], and sets directives[N].dir_start + to the end of the format string. Also fills in the arg_type fields of the + arguments and the needed count of arguments. */ +#ifdef STATIC +STATIC +#else +extern +#endif +int printf_parse (const char *format, char_directives *d, arguments *a); + +#endif /* _PRINTF_PARSE_H */ diff --git a/src/bin/m4/lib/quotearg.c b/src/bin/m4/lib/quotearg.c new file mode 100644 index 0000000000..6f3ee9e86c --- /dev/null +++ b/src/bin/m4/lib/quotearg.c @@ -0,0 +1,713 @@ +/* quotearg.c - quote arguments for output + + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free + Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert */ + +#include + +#include "quotearg.h" + +#include "xalloc.h" + +#include +#include +#include +#include +#include +#include + +#include "gettext.h" +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +#if HAVE_WCHAR_H + +/* BSD/OS 4.1 wchar.h requires FILE and struct tm to be declared. */ +# include +# include + +# include +#endif + +#if !HAVE_MBRTOWC +/* Disable multibyte processing entirely. Since MB_CUR_MAX is 1, the + other macros are defined only for documentation and to satisfy C + syntax. */ +# undef MB_CUR_MAX +# define MB_CUR_MAX 1 +# undef mbstate_t +# define mbstate_t int +# define mbrtowc(pwc, s, n, ps) ((*(pwc) = *(s)) != 0) +# define iswprint(wc) isprint ((unsigned char) (wc)) +# undef HAVE_MBSINIT +#endif + +#if !defined mbsinit && !HAVE_MBSINIT +# define mbsinit(ps) 1 +#endif + +#ifndef iswprint +# if HAVE_WCTYPE_H +# include +# endif +# if !defined iswprint && !HAVE_ISWPRINT +# define iswprint(wc) 1 +# endif +#endif + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +#define INT_BITS (sizeof (int) * CHAR_BIT) + +struct quoting_options +{ + /* Basic quoting style. */ + enum quoting_style style; + + /* Quote the characters indicated by this bit vector even if the + quoting style would not normally require them to be quoted. */ + unsigned int quote_these_too[(UCHAR_MAX / INT_BITS) + 1]; +}; + +/* Names of quoting styles. */ +char const *const quoting_style_args[] = +{ + "literal", + "shell", + "shell-always", + "c", + "escape", + "locale", + "clocale", + 0 +}; + +/* Correspondences to quoting style names. */ +enum quoting_style const quoting_style_vals[] = +{ + literal_quoting_style, + shell_quoting_style, + shell_always_quoting_style, + c_quoting_style, + escape_quoting_style, + locale_quoting_style, + clocale_quoting_style +}; + +/* The default quoting options. */ +static struct quoting_options default_quoting_options; + +/* Allocate a new set of quoting options, with contents initially identical + to O if O is not null, or to the default if O is null. + It is the caller's responsibility to free the result. */ +struct quoting_options * +clone_quoting_options (struct quoting_options *o) +{ + int e = errno; + struct quoting_options *p = xmemdup (o ? o : &default_quoting_options, + sizeof *o); + errno = e; + return p; +} + +/* Get the value of O's quoting style. If O is null, use the default. */ +enum quoting_style +get_quoting_style (struct quoting_options *o) +{ + return (o ? o : &default_quoting_options)->style; +} + +/* In O (or in the default if O is null), + set the value of the quoting style to S. */ +void +set_quoting_style (struct quoting_options *o, enum quoting_style s) +{ + (o ? o : &default_quoting_options)->style = s; +} + +/* In O (or in the default if O is null), + set the value of the quoting options for character C to I. + Return the old value. Currently, the only values defined for I are + 0 (the default) and 1 (which means to quote the character even if + it would not otherwise be quoted). */ +int +set_char_quoting (struct quoting_options *o, char c, int i) +{ + unsigned char uc = c; + unsigned int *p = + (o ? o : &default_quoting_options)->quote_these_too + uc / INT_BITS; + int shift = uc % INT_BITS; + int r = (*p >> shift) & 1; + *p ^= ((i & 1) ^ r) << shift; + return r; +} + +/* MSGID approximates a quotation mark. Return its translation if it + has one; otherwise, return either it or "\"", depending on S. */ +static char const * +gettext_quote (char const *msgid, enum quoting_style s) +{ + char const *translation = _(msgid); + if (translation == msgid && s == clocale_quoting_style) + translation = "\""; + return translation; +} + +/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of + argument ARG (of size ARGSIZE), using QUOTING_STYLE and the + non-quoting-style part of O to control quoting. + Terminate the output with a null character, and return the written + size of the output, not counting the terminating null. + If BUFFERSIZE is too small to store the output string, return the + value that would have been returned had BUFFERSIZE been large enough. + If ARGSIZE is SIZE_MAX, use the string length of the argument for ARGSIZE. + + This function acts like quotearg_buffer (BUFFER, BUFFERSIZE, ARG, + ARGSIZE, O), except it uses QUOTING_STYLE instead of the quoting + style specified by O, and O may not be null. */ + +static size_t +quotearg_buffer_restyled (char *buffer, size_t buffersize, + char const *arg, size_t argsize, + enum quoting_style quoting_style, + struct quoting_options const *o) +{ + size_t i; + size_t len = 0; + char const *quote_string = 0; + size_t quote_string_len = 0; + bool backslash_escapes = false; + bool unibyte_locale = MB_CUR_MAX == 1; + +#define STORE(c) \ + do \ + { \ + if (len < buffersize) \ + buffer[len] = (c); \ + len++; \ + } \ + while (0) + + switch (quoting_style) + { + case c_quoting_style: + STORE ('"'); + backslash_escapes = true; + quote_string = "\""; + quote_string_len = 1; + break; + + case escape_quoting_style: + backslash_escapes = true; + break; + + case locale_quoting_style: + case clocale_quoting_style: + { + /* TRANSLATORS: + Get translations for open and closing quotation marks. + + The message catalog should translate "`" to a left + quotation mark suitable for the locale, and similarly for + "'". If the catalog has no translation, + locale_quoting_style quotes `like this', and + clocale_quoting_style quotes "like this". + + For example, an American English Unicode locale should + translate "`" to U+201C (LEFT DOUBLE QUOTATION MARK), and + should translate "'" to U+201D (RIGHT DOUBLE QUOTATION + MARK). A British English Unicode locale should instead + translate these to U+2018 (LEFT SINGLE QUOTATION MARK) and + U+2019 (RIGHT SINGLE QUOTATION MARK), respectively. + + If you don't know what to put here, please see + + and use glyphs suitable for your language. */ + + char const *left = gettext_quote (N_("`"), quoting_style); + char const *right = gettext_quote (N_("'"), quoting_style); + for (quote_string = left; *quote_string; quote_string++) + STORE (*quote_string); + backslash_escapes = true; + quote_string = right; + quote_string_len = strlen (quote_string); + } + break; + + case shell_always_quoting_style: + STORE ('\''); + quote_string = "'"; + quote_string_len = 1; + break; + + default: + break; + } + + for (i = 0; ! (argsize == SIZE_MAX ? arg[i] == '\0' : i == argsize); i++) + { + unsigned char c; + unsigned char esc; + + if (backslash_escapes + && quote_string_len + && i + quote_string_len <= argsize + && memcmp (arg + i, quote_string, quote_string_len) == 0) + STORE ('\\'); + + c = arg[i]; + switch (c) + { + case '\0': + if (backslash_escapes) + { + STORE ('\\'); + STORE ('0'); + STORE ('0'); + c = '0'; + } + break; + + case '?': + switch (quoting_style) + { + case shell_quoting_style: + goto use_shell_always_quoting_style; + + case c_quoting_style: + if (i + 2 < argsize && arg[i + 1] == '?') + switch (arg[i + 2]) + { + case '!': case '\'': + case '(': case ')': case '-': case '/': + case '<': case '=': case '>': + /* Escape the second '?' in what would otherwise be + a trigraph. */ + c = arg[i + 2]; + i += 2; + STORE ('?'); + STORE ('\\'); + STORE ('?'); + break; + + default: + break; + } + break; + + default: + break; + } + break; + + case '\a': esc = 'a'; goto c_escape; + case '\b': esc = 'b'; goto c_escape; + case '\f': esc = 'f'; goto c_escape; + case '\n': esc = 'n'; goto c_and_shell_escape; + case '\r': esc = 'r'; goto c_and_shell_escape; + case '\t': esc = 't'; goto c_and_shell_escape; + case '\v': esc = 'v'; goto c_escape; + case '\\': esc = c; goto c_and_shell_escape; + + c_and_shell_escape: + if (quoting_style == shell_quoting_style) + goto use_shell_always_quoting_style; + c_escape: + if (backslash_escapes) + { + c = esc; + goto store_escape; + } + break; + + case '{': case '}': /* sometimes special if isolated */ + if (! (argsize == SIZE_MAX ? arg[1] == '\0' : argsize == 1)) + break; + /* Fall through. */ + case '#': case '~': + if (i != 0) + break; + /* Fall through. */ + case ' ': + case '!': /* special in bash */ + case '"': case '$': case '&': + case '(': case ')': case '*': case ';': + case '<': + case '=': /* sometimes special in 0th or (with "set -k") later args */ + case '>': case '[': + case '^': /* special in old /bin/sh, e.g. SunOS 4.1.4 */ + case '`': case '|': + /* A shell special character. In theory, '$' and '`' could + be the first bytes of multibyte characters, which means + we should check them with mbrtowc, but in practice this + doesn't happen so it's not worth worrying about. */ + if (quoting_style == shell_quoting_style) + goto use_shell_always_quoting_style; + break; + + case '\'': + switch (quoting_style) + { + case shell_quoting_style: + goto use_shell_always_quoting_style; + + case shell_always_quoting_style: + STORE ('\''); + STORE ('\\'); + STORE ('\''); + break; + + default: + break; + } + break; + + case '%': case '+': case ',': case '-': case '.': case '/': + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': case ':': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': + case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': case ']': case '_': case 'a': case 'b': + case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': + /* These characters don't cause problems, no matter what the + quoting style is. They cannot start multibyte sequences. */ + break; + + default: + /* If we have a multibyte sequence, copy it until we reach + its end, find an error, or come back to the initial shift + state. For C-like styles, if the sequence has + unprintable characters, escape the whole sequence, since + we can't easily escape single characters within it. */ + { + /* Length of multibyte sequence found so far. */ + size_t m; + + bool printable; + + if (unibyte_locale) + { + m = 1; + printable = isprint (c) != 0; + } + else + { + mbstate_t mbstate; + memset (&mbstate, 0, sizeof mbstate); + + m = 0; + printable = true; + if (argsize == SIZE_MAX) + argsize = strlen (arg); + + do + { + wchar_t w; + size_t bytes = mbrtowc (&w, &arg[i + m], + argsize - (i + m), &mbstate); + if (bytes == 0) + break; + else if (bytes == (size_t) -1) + { + printable = false; + break; + } + else if (bytes == (size_t) -2) + { + printable = false; + while (i + m < argsize && arg[i + m]) + m++; + break; + } + else + { + /* Work around a bug with older shells that "see" a '\' + that is really the 2nd byte of a multibyte character. + In practice the problem is limited to ASCII + chars >= '@' that are shell special chars. */ + if ('[' == 0x5b && quoting_style == shell_quoting_style) + { + size_t j; + for (j = 1; j < bytes; j++) + switch (arg[i + m + j]) + { + case '[': case '\\': case '^': + case '`': case '|': + goto use_shell_always_quoting_style; + + default: + break; + } + } + + if (! iswprint (w)) + printable = false; + m += bytes; + } + } + while (! mbsinit (&mbstate)); + } + + if (1 < m || (backslash_escapes && ! printable)) + { + /* Output a multibyte sequence, or an escaped + unprintable unibyte character. */ + size_t ilim = i + m; + + for (;;) + { + if (backslash_escapes && ! printable) + { + STORE ('\\'); + STORE ('0' + (c >> 6)); + STORE ('0' + ((c >> 3) & 7)); + c = '0' + (c & 7); + } + if (ilim <= i + 1) + break; + STORE (c); + c = arg[++i]; + } + + goto store_c; + } + } + } + + if (! (backslash_escapes + && o->quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS)))) + goto store_c; + + store_escape: + STORE ('\\'); + + store_c: + STORE (c); + } + + if (i == 0 && quoting_style == shell_quoting_style) + goto use_shell_always_quoting_style; + + if (quote_string) + for (; *quote_string; quote_string++) + STORE (*quote_string); + + if (len < buffersize) + buffer[len] = '\0'; + return len; + + use_shell_always_quoting_style: + return quotearg_buffer_restyled (buffer, buffersize, arg, argsize, + shell_always_quoting_style, o); +} + +/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of + argument ARG (of size ARGSIZE), using O to control quoting. + If O is null, use the default. + Terminate the output with a null character, and return the written + size of the output, not counting the terminating null. + If BUFFERSIZE is too small to store the output string, return the + value that would have been returned had BUFFERSIZE been large enough. + If ARGSIZE is SIZE_MAX, use the string length of the argument for + ARGSIZE. */ +size_t +quotearg_buffer (char *buffer, size_t buffersize, + char const *arg, size_t argsize, + struct quoting_options const *o) +{ + struct quoting_options const *p = o ? o : &default_quoting_options; + int e = errno; + size_t r = quotearg_buffer_restyled (buffer, buffersize, arg, argsize, + p->style, p); + errno = e; + return r; +} + +/* Like quotearg_buffer (..., ARG, ARGSIZE, O), except return newly + allocated storage containing the quoted string. */ +char * +quotearg_alloc (char const *arg, size_t argsize, + struct quoting_options const *o) +{ + int e = errno; + size_t bufsize = quotearg_buffer (0, 0, arg, argsize, o) + 1; + char *buf = xcharalloc (bufsize); + quotearg_buffer (buf, bufsize, arg, argsize, o); + errno = e; + return buf; +} + +/* A storage slot with size and pointer to a value. */ +struct slotvec +{ + size_t size; + char *val; +}; + +/* Preallocate a slot 0 buffer, so that the caller can always quote + one small component of a "memory exhausted" message in slot 0. */ +static char slot0[256]; +static unsigned int nslots = 1; +static struct slotvec slotvec0 = {sizeof slot0, slot0}; +static struct slotvec *slotvec = &slotvec0; + +void +quotearg_free (void) +{ + struct slotvec *sv = slotvec; + unsigned int i; + for (i = 1; i < nslots; i++) + free (sv[i].val); + if (sv[0].val != slot0) + { + free (sv[0].val); + slotvec0.size = sizeof slot0; + slotvec0.val = slot0; + } + if (sv != &slotvec0) + { + free (sv); + slotvec = &slotvec0; + } + nslots = 1; +} + +/* Use storage slot N to return a quoted version of argument ARG. + ARG is of size ARGSIZE, but if that is SIZE_MAX, ARG is a + null-terminated string. + OPTIONS specifies the quoting options. + The returned value points to static storage that can be + reused by the next call to this function with the same value of N. + N must be nonnegative. N is deliberately declared with type "int" + to allow for future extensions (using negative values). */ +static char * +quotearg_n_options (int n, char const *arg, size_t argsize, + struct quoting_options const *options) +{ + int e = errno; + + unsigned int n0 = n; + struct slotvec *sv = slotvec; + + if (n < 0) + abort (); + + if (nslots <= n0) + { + /* FIXME: technically, the type of n1 should be `unsigned int', + but that evokes an unsuppressible warning from gcc-4.0.1 and + older. If gcc ever provides an option to suppress that warning, + revert to the original type, so that the test in xalloc_oversized + is once again performed only at compile time. */ + size_t n1 = n0 + 1; + bool preallocated = (sv == &slotvec0); + + if (xalloc_oversized (n1, sizeof *sv)) + xalloc_die (); + + slotvec = sv = xrealloc (preallocated ? NULL : sv, n1 * sizeof *sv); + if (preallocated) + *sv = slotvec0; + memset (sv + nslots, 0, (n1 - nslots) * sizeof *sv); + nslots = n1; + } + + { + size_t size = sv[n].size; + char *val = sv[n].val; + size_t qsize = quotearg_buffer (val, size, arg, argsize, options); + + if (size <= qsize) + { + sv[n].size = size = qsize + 1; + if (val != slot0) + free (val); + sv[n].val = val = xcharalloc (size); + quotearg_buffer (val, size, arg, argsize, options); + } + + errno = e; + return val; + } +} + +char * +quotearg_n (int n, char const *arg) +{ + return quotearg_n_options (n, arg, SIZE_MAX, &default_quoting_options); +} + +char * +quotearg (char const *arg) +{ + return quotearg_n (0, arg); +} + +/* Return quoting options for STYLE, with no extra quoting. */ +static struct quoting_options +quoting_options_from_style (enum quoting_style style) +{ + struct quoting_options o; + o.style = style; + memset (o.quote_these_too, 0, sizeof o.quote_these_too); + return o; +} + +char * +quotearg_n_style (int n, enum quoting_style s, char const *arg) +{ + struct quoting_options const o = quoting_options_from_style (s); + return quotearg_n_options (n, arg, SIZE_MAX, &o); +} + +char * +quotearg_n_style_mem (int n, enum quoting_style s, + char const *arg, size_t argsize) +{ + struct quoting_options const o = quoting_options_from_style (s); + return quotearg_n_options (n, arg, argsize, &o); +} + +char * +quotearg_style (enum quoting_style s, char const *arg) +{ + return quotearg_n_style (0, s, arg); +} + +char * +quotearg_char (char const *arg, char ch) +{ + struct quoting_options options; + options = default_quoting_options; + set_char_quoting (&options, ch, 1); + return quotearg_n_options (0, arg, SIZE_MAX, &options); +} + +char * +quotearg_colon (char const *arg) +{ + return quotearg_char (arg, ':'); +} diff --git a/src/bin/m4/lib/quotearg.h b/src/bin/m4/lib/quotearg.h new file mode 100644 index 0000000000..4887df3b6d --- /dev/null +++ b/src/bin/m4/lib/quotearg.h @@ -0,0 +1,140 @@ +/* quotearg.h - quote arguments for output + + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2006 Free + Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert */ + +#ifndef QUOTEARG_H_ +# define QUOTEARG_H_ 1 + +# include + +/* Basic quoting styles. */ +enum quoting_style + { + /* Output names as-is (ls --quoting-style=literal). */ + literal_quoting_style, + + /* Quote names for the shell if they contain shell metacharacters + or would cause ambiguous output (ls --quoting-style=shell). */ + shell_quoting_style, + + /* Quote names for the shell, even if they would normally not + require quoting (ls --quoting-style=shell-always). */ + shell_always_quoting_style, + + /* Quote names as for a C language string (ls --quoting-style=c). */ + c_quoting_style, + + /* Like c_quoting_style except omit the surrounding double-quote + characters (ls --quoting-style=escape). */ + escape_quoting_style, + + /* Like clocale_quoting_style, but quote `like this' instead of + "like this" in the default C locale (ls --quoting-style=locale). */ + locale_quoting_style, + + /* Like c_quoting_style except use quotation marks appropriate for + the locale (ls --quoting-style=clocale). */ + clocale_quoting_style + }; + +/* For now, --quoting-style=literal is the default, but this may change. */ +# ifndef DEFAULT_QUOTING_STYLE +# define DEFAULT_QUOTING_STYLE literal_quoting_style +# endif + +/* Names of quoting styles and their corresponding values. */ +extern char const *const quoting_style_args[]; +extern enum quoting_style const quoting_style_vals[]; + +struct quoting_options; + +/* The functions listed below set and use a hidden variable + that contains the default quoting style options. */ + +/* Allocate a new set of quoting options, with contents initially identical + to O if O is not null, or to the default if O is null. + It is the caller's responsibility to free the result. */ +struct quoting_options *clone_quoting_options (struct quoting_options *o); + +/* Get the value of O's quoting style. If O is null, use the default. */ +enum quoting_style get_quoting_style (struct quoting_options *o); + +/* In O (or in the default if O is null), + set the value of the quoting style to S. */ +void set_quoting_style (struct quoting_options *o, enum quoting_style s); + +/* In O (or in the default if O is null), + set the value of the quoting options for character C to I. + Return the old value. Currently, the only values defined for I are + 0 (the default) and 1 (which means to quote the character even if + it would not otherwise be quoted). */ +int set_char_quoting (struct quoting_options *o, char c, int i); + +/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of + argument ARG (of size ARGSIZE), using O to control quoting. + If O is null, use the default. + Terminate the output with a null character, and return the written + size of the output, not counting the terminating null. + If BUFFERSIZE is too small to store the output string, return the + value that would have been returned had BUFFERSIZE been large enough. + If ARGSIZE is -1, use the string length of the argument for ARGSIZE. */ +size_t quotearg_buffer (char *buffer, size_t buffersize, + char const *arg, size_t argsize, + struct quoting_options const *o); + +/* Like quotearg_buffer, except return the result in a newly allocated + buffer. It is the caller's responsibility to free the result. */ +char *quotearg_alloc (char const *arg, size_t argsize, + struct quoting_options const *o); + +/* Use storage slot N to return a quoted version of the string ARG. + Use the default quoting options. + The returned value points to static storage that can be + reused by the next call to this function with the same value of N. + N must be nonnegative. */ +char *quotearg_n (int n, char const *arg); + +/* Equivalent to quotearg_n (0, ARG). */ +char *quotearg (char const *arg); + +/* Use style S and storage slot N to return a quoted version of the string ARG. + This is like quotearg_n (N, ARG), except that it uses S with no other + options to specify the quoting method. */ +char *quotearg_n_style (int n, enum quoting_style s, char const *arg); + +/* Use style S and storage slot N to return a quoted version of the + argument ARG of size ARGSIZE. This is like quotearg_n_style + (N, S, ARG), except it can quote null bytes. */ +char *quotearg_n_style_mem (int n, enum quoting_style s, + char const *arg, size_t argsize); + +/* Equivalent to quotearg_n_style (0, S, ARG). */ +char *quotearg_style (enum quoting_style s, char const *arg); + +/* Like quotearg (ARG), except also quote any instances of CH. */ +char *quotearg_char (char const *arg, char ch); + +/* Equivalent to quotearg_char (ARG, ':'). */ +char *quotearg_colon (char const *arg); + +/* Free any dynamically allocated memory. */ +void quotearg_free (void); + +#endif /* !QUOTEARG_H_ */ diff --git a/src/bin/m4/lib/regcomp.c b/src/bin/m4/lib/regcomp.c new file mode 100644 index 0000000000..6e317f5134 --- /dev/null +++ b/src/bin/m4/lib/regcomp.c @@ -0,0 +1,3858 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002,2003,2004,2005,2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, + size_t length, reg_syntax_t syntax); +static void re_compile_fastmap_iter (regex_t *bufp, + const re_dfastate_t *init_state, + char *fastmap); +static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len); +#ifdef RE_ENABLE_I18N +static void free_charset (re_charset_t *cset); +#endif /* RE_ENABLE_I18N */ +static void free_workarea_compile (regex_t *preg); +static reg_errcode_t create_initial_state (re_dfa_t *dfa); +#ifdef RE_ENABLE_I18N +static void optimize_utf8 (re_dfa_t *dfa); +#endif +static reg_errcode_t analyze (regex_t *preg); +static reg_errcode_t preorder (bin_tree_t *root, + reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra); +static reg_errcode_t postorder (bin_tree_t *root, + reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra); +static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node); +static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node); +static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg, + bin_tree_t *node); +static reg_errcode_t calc_first (void *extra, bin_tree_t *node); +static reg_errcode_t calc_next (void *extra, bin_tree_t *node); +static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node); +static Idx duplicate_node (re_dfa_t *dfa, Idx org_idx, unsigned int constraint); +static Idx search_duplicated_node (const re_dfa_t *dfa, Idx org_node, + unsigned int constraint); +static reg_errcode_t calc_eclosure (re_dfa_t *dfa); +static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, + Idx node, bool root); +static reg_errcode_t calc_inveclosure (re_dfa_t *dfa); +static Idx fetch_number (re_string_t *input, re_token_t *token, + reg_syntax_t syntax); +static int peek_token (re_token_t *token, re_string_t *input, + reg_syntax_t syntax) internal_function; +static bin_tree_t *parse (re_string_t *regexp, regex_t *preg, + reg_syntax_t syntax, reg_errcode_t *err); +static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + Idx nest, reg_errcode_t *err); +static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + Idx nest, reg_errcode_t *err); +static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + Idx nest, reg_errcode_t *err); +static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + Idx nest, reg_errcode_t *err); +static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp, + re_dfa_t *dfa, re_token_t *token, + reg_syntax_t syntax, reg_errcode_t *err); +static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, + re_token_t *token, reg_syntax_t syntax, + reg_errcode_t *err); +static reg_errcode_t parse_bracket_element (bracket_elem_t *elem, + re_string_t *regexp, + re_token_t *token, int token_len, + re_dfa_t *dfa, + reg_syntax_t syntax, + bool accept_hyphen); +static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem, + re_string_t *regexp, + re_token_t *token); +#ifdef RE_ENABLE_I18N +static reg_errcode_t build_equiv_class (bitset_t sbcset, + re_charset_t *mbcset, + Idx *equiv_class_alloc, + const unsigned char *name); +static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, + bitset_t sbcset, + re_charset_t *mbcset, + Idx *char_class_alloc, + const unsigned char *class_name, + reg_syntax_t syntax); +#else /* not RE_ENABLE_I18N */ +static reg_errcode_t build_equiv_class (bitset_t sbcset, + const unsigned char *name); +static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, + bitset_t sbcset, + const unsigned char *class_name, + reg_syntax_t syntax); +#endif /* not RE_ENABLE_I18N */ +static bin_tree_t *build_charclass_op (re_dfa_t *dfa, + RE_TRANSLATE_TYPE trans, + const unsigned char *class_name, + const unsigned char *extra, + bool non_match, reg_errcode_t *err); +static bin_tree_t *create_tree (re_dfa_t *dfa, + bin_tree_t *left, bin_tree_t *right, + re_token_type_t type); +static bin_tree_t *create_token_tree (re_dfa_t *dfa, + bin_tree_t *left, bin_tree_t *right, + const re_token_t *token); +static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa); +static void free_token (re_token_t *node); +static reg_errcode_t free_tree (void *extra, bin_tree_t *node); +static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node); + +/* This table gives an error message for each of the error codes listed + in regex.h. Obviously the order here has to be same as there. + POSIX doesn't require that we do anything for REG_NOERROR, + but why not be nice? */ + +static const char __re_error_msgid[] = + { +#define REG_NOERROR_IDX 0 + gettext_noop ("Success") /* REG_NOERROR */ + "\0" +#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success") + gettext_noop ("No match") /* REG_NOMATCH */ + "\0" +#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match") + gettext_noop ("Invalid regular expression") /* REG_BADPAT */ + "\0" +#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression") + gettext_noop ("Invalid collation character") /* REG_ECOLLATE */ + "\0" +#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character") + gettext_noop ("Invalid character class name") /* REG_ECTYPE */ + "\0" +#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name") + gettext_noop ("Trailing backslash") /* REG_EESCAPE */ + "\0" +#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash") + gettext_noop ("Invalid back reference") /* REG_ESUBREG */ + "\0" +#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference") + gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */ + "\0" +#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^") + gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */ + "\0" +#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(") + gettext_noop ("Unmatched \\{") /* REG_EBRACE */ + "\0" +#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{") + gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */ + "\0" +#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}") + gettext_noop ("Invalid range end") /* REG_ERANGE */ + "\0" +#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end") + gettext_noop ("Memory exhausted") /* REG_ESPACE */ + "\0" +#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted") + gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */ + "\0" +#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression") + gettext_noop ("Premature end of regular expression") /* REG_EEND */ + "\0" +#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression") + gettext_noop ("Regular expression too big") /* REG_ESIZE */ + "\0" +#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big") + gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */ + }; + +static const size_t __re_error_msgid_idx[] = + { + REG_NOERROR_IDX, + REG_NOMATCH_IDX, + REG_BADPAT_IDX, + REG_ECOLLATE_IDX, + REG_ECTYPE_IDX, + REG_EESCAPE_IDX, + REG_ESUBREG_IDX, + REG_EBRACK_IDX, + REG_EPAREN_IDX, + REG_EBRACE_IDX, + REG_BADBR_IDX, + REG_ERANGE_IDX, + REG_ESPACE_IDX, + REG_BADRPT_IDX, + REG_EEND_IDX, + REG_ESIZE_IDX, + REG_ERPAREN_IDX + }; + +/* Entry points for GNU code. */ + +/* re_compile_pattern is the GNU regular expression compiler: it + compiles PATTERN (of length LENGTH) and puts the result in BUFP. + Returns 0 if the pattern was valid, otherwise an error string. + + Assumes the `allocated' (and perhaps `buffer') and `translate' fields + are set in BUFP on entry. */ + +#ifdef _LIBC +const char * +re_compile_pattern (pattern, length, bufp) + const char *pattern; + size_t length; + struct re_pattern_buffer *bufp; +#else /* size_t might promote */ +const char * +re_compile_pattern (const char *pattern, size_t length, + struct re_pattern_buffer *bufp) +#endif +{ + reg_errcode_t ret; + + /* And GNU code determines whether or not to get register information + by passing null for the REGS argument to re_match, etc., not by + setting no_sub, unless RE_NO_SUB is set. */ + bufp->no_sub = !!(re_syntax_options & RE_NO_SUB); + + /* Match anchors at newline. */ + bufp->newline_anchor = 1; + + ret = re_compile_internal (bufp, pattern, length, re_syntax_options); + + if (!ret) + return NULL; + return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); +} +#ifdef _LIBC +weak_alias (__re_compile_pattern, re_compile_pattern) +#endif + +/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can + also be assigned to arbitrarily: each pattern buffer stores its own + syntax, so it can be changed between regex compilations. */ +/* This has no initializer because initialized variables in Emacs + become read-only after dumping. */ +reg_syntax_t re_syntax_options; + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit mask comprised of the various bits + defined in regex.h. We return the old syntax. */ + +reg_syntax_t +re_set_syntax (syntax) + reg_syntax_t syntax; +{ + reg_syntax_t ret = re_syntax_options; + + re_syntax_options = syntax; + return ret; +} +#ifdef _LIBC +weak_alias (__re_set_syntax, re_set_syntax) +#endif + +int +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + char *fastmap = bufp->fastmap; + + memset (fastmap, '\0', sizeof (char) * SBC_MAX); + re_compile_fastmap_iter (bufp, dfa->init_state, fastmap); + if (dfa->init_state != dfa->init_state_word) + re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap); + if (dfa->init_state != dfa->init_state_nl) + re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap); + if (dfa->init_state != dfa->init_state_begbuf) + re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap); + bufp->fastmap_accurate = 1; + return 0; +} +#ifdef _LIBC +weak_alias (__re_compile_fastmap, re_compile_fastmap) +#endif + +static inline void +__attribute ((always_inline)) +re_set_fastmap (char *fastmap, bool icase, int ch) +{ + fastmap[ch] = 1; + if (icase) + fastmap[tolower (ch)] = 1; +} + +/* Helper function for re_compile_fastmap. + Compile fastmap for the initial_state INIT_STATE. */ + +static void +re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state, + char *fastmap) +{ + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + Idx node_cnt; + bool icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE)); + for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt) + { + Idx node = init_state->nodes.elems[node_cnt]; + re_token_type_t type = dfa->nodes[node].type; + + if (type == CHARACTER) + { + re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c); +#ifdef RE_ENABLE_I18N + if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) + { + unsigned char buf[MB_LEN_MAX]; + unsigned char *p; + wchar_t wc; + mbstate_t state; + + p = buf; + *p++ = dfa->nodes[node].opr.c; + while (++node < dfa->nodes_len + && dfa->nodes[node].type == CHARACTER + && dfa->nodes[node].mb_partial) + *p++ = dfa->nodes[node].opr.c; + memset (&state, '\0', sizeof (state)); + if (mbrtowc (&wc, (const char *) buf, p - buf, + &state) == p - buf + && (__wcrtomb ((char *) buf, towlower (wc), &state) + != (size_t) -1)) + re_set_fastmap (fastmap, false, buf[0]); + } +#endif + } + else if (type == SIMPLE_BRACKET) + { + int i, ch; + for (i = 0, ch = 0; i < BITSET_WORDS; ++i) + { + int j; + bitset_word_t w = dfa->nodes[node].opr.sbcset[i]; + for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) + if (w & ((bitset_word_t) 1 << j)) + re_set_fastmap (fastmap, icase, ch); + } + } +#ifdef RE_ENABLE_I18N + else if (type == COMPLEX_BRACKET) + { + Idx i; + re_charset_t *cset = dfa->nodes[node].opr.mbcset; + if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes + || cset->nranges || cset->nchar_classes) + { +# ifdef _LIBC + if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0) + { + /* In this case we want to catch the bytes which are + the first byte of any collation elements. + e.g. In da_DK, we want to catch 'a' since "aa" + is a valid collation element, and don't catch + 'b' since 'b' is the only collation element + which starts from 'b'. */ + const int32_t *table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + for (i = 0; i < SBC_MAX; ++i) + if (table[i] < 0) + re_set_fastmap (fastmap, icase, i); + } +# else + if (dfa->mb_cur_max > 1) + for (i = 0; i < SBC_MAX; ++i) + if (__btowc (i) == WEOF) + re_set_fastmap (fastmap, icase, i); +# endif /* not _LIBC */ + } + for (i = 0; i < cset->nmbchars; ++i) + { + char buf[256]; + mbstate_t state; + memset (&state, '\0', sizeof (state)); + if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1) + re_set_fastmap (fastmap, icase, *(unsigned char *) buf); + if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) + { + if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state) + != (size_t) -1) + re_set_fastmap (fastmap, false, *(unsigned char *) buf); + } + } + } +#endif /* RE_ENABLE_I18N */ + else if (type == OP_PERIOD +#ifdef RE_ENABLE_I18N + || type == OP_UTF8_PERIOD +#endif /* RE_ENABLE_I18N */ + || type == END_OF_RE) + { + memset (fastmap, '\1', sizeof (char) * SBC_MAX); + if (type == END_OF_RE) + bufp->can_be_null = 1; + return; + } + } +} + +/* Entry point for POSIX code. */ +/* regcomp takes a regular expression as a string and compiles it. + + PREG is a regex_t *. We do not expect any fields to be initialized, + since POSIX says we shouldn't. Thus, we set + + `buffer' to the compiled pattern; + `used' to the length of the compiled pattern; + `syntax' to RE_SYNTAX_POSIX_EXTENDED if the + REG_EXTENDED bit in CFLAGS is set; otherwise, to + RE_SYNTAX_POSIX_BASIC; + `newline_anchor' to REG_NEWLINE being set in CFLAGS; + `fastmap' to an allocated space for the fastmap; + `fastmap_accurate' to zero; + `re_nsub' to the number of subexpressions in PATTERN. + + PATTERN is the address of the pattern string. + + CFLAGS is a series of bits which affect compilation. + + If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we + use POSIX basic syntax. + + If REG_NEWLINE is set, then . and [^...] don't match newline. + Also, regexec will try a match beginning after every newline. + + If REG_ICASE is set, then we considers upper- and lowercase + versions of letters to be equivalent when matching. + + If REG_NOSUB is set, then when PREG is passed to regexec, that + routine will report only success or failure, and nothing about the + registers. + + It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for + the return codes and their meanings.) */ + +int +regcomp (preg, pattern, cflags) + regex_t *__restrict preg; + const char *__restrict pattern; + int cflags; +{ + reg_errcode_t ret; + reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED + : RE_SYNTAX_POSIX_BASIC); + + preg->buffer = NULL; + preg->allocated = 0; + preg->used = 0; + + /* Try to allocate space for the fastmap. */ + preg->fastmap = re_malloc (char, SBC_MAX); + if (BE (preg->fastmap == NULL, 0)) + return REG_ESPACE; + + syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0; + + /* If REG_NEWLINE is set, newlines are treated differently. */ + if (cflags & REG_NEWLINE) + { /* REG_NEWLINE implies neither . nor [^...] match newline. */ + syntax &= ~RE_DOT_NEWLINE; + syntax |= RE_HAT_LISTS_NOT_NEWLINE; + /* It also changes the matching behavior. */ + preg->newline_anchor = 1; + } + else + preg->newline_anchor = 0; + preg->no_sub = !!(cflags & REG_NOSUB); + preg->translate = NULL; + + ret = re_compile_internal (preg, pattern, strlen (pattern), syntax); + + /* POSIX doesn't distinguish between an unmatched open-group and an + unmatched close-group: both are REG_EPAREN. */ + if (ret == REG_ERPAREN) + ret = REG_EPAREN; + + /* We have already checked preg->fastmap != NULL. */ + if (BE (ret == REG_NOERROR, 1)) + /* Compute the fastmap now, since regexec cannot modify the pattern + buffer. This function never fails in this implementation. */ + (void) re_compile_fastmap (preg); + else + { + /* Some error occurred while compiling the expression. */ + re_free (preg->fastmap); + preg->fastmap = NULL; + } + + return (int) ret; +} +#ifdef _LIBC +weak_alias (__regcomp, regcomp) +#endif + +/* Returns a message corresponding to an error code, ERRCODE, returned + from either regcomp or regexec. We don't use PREG here. */ + +#ifdef _LIBC +size_t +regerror (errcode, preg, errbuf, errbuf_size) + int errcode; + const regex_t *__restrict preg; + char *__restrict errbuf; + size_t errbuf_size; +#else /* size_t might promote */ +size_t +regerror (int errcode, const regex_t *__restrict preg, + char *__restrict errbuf, size_t errbuf_size) +#endif +{ + const char *msg; + size_t msg_size; + + if (BE (errcode < 0 + || errcode >= (int) (sizeof (__re_error_msgid_idx) + / sizeof (__re_error_msgid_idx[0])), 0)) + /* Only error codes returned by the rest of the code should be passed + to this routine. If we are given anything else, or if other regex + code generates an invalid error code, then the program has a bug. + Dump core so we can fix it. */ + abort (); + + msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]); + + msg_size = strlen (msg) + 1; /* Includes the null. */ + + if (BE (errbuf_size != 0, 1)) + { + if (BE (msg_size > errbuf_size, 0)) + { +#if defined HAVE_MEMPCPY || defined _LIBC + *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0'; +#else + memcpy (errbuf, msg, errbuf_size - 1); + errbuf[errbuf_size - 1] = 0; +#endif + } + else + memcpy (errbuf, msg, msg_size); + } + + return msg_size; +} +#ifdef _LIBC +weak_alias (__regerror, regerror) +#endif + + +#ifdef RE_ENABLE_I18N +/* This static array is used for the map to single-byte characters when + UTF-8 is used. Otherwise we would allocate memory just to initialize + it the same all the time. UTF-8 is the preferred encoding so this is + a worthwhile optimization. */ +static const bitset_t utf8_sb_map = +{ + /* Set the first 128 bits. */ +# if 4 * BITSET_WORD_BITS < ASCII_CHARS +# error "bitset_word_t is narrower than 32 bits" +# elif 3 * BITSET_WORD_BITS < ASCII_CHARS + BITSET_WORD_MAX, BITSET_WORD_MAX, BITSET_WORD_MAX, +# elif 2 * BITSET_WORD_BITS < ASCII_CHARS + BITSET_WORD_MAX, BITSET_WORD_MAX, +# elif 1 * BITSET_WORD_BITS < ASCII_CHARS + BITSET_WORD_MAX, +# endif + (BITSET_WORD_MAX + >> (SBC_MAX % BITSET_WORD_BITS == 0 + ? 0 + : BITSET_WORD_BITS - SBC_MAX % BITSET_WORD_BITS)) +}; +#endif + + +static void +free_dfa_content (re_dfa_t *dfa) +{ + Idx i, j; + + if (dfa->nodes) + for (i = 0; i < dfa->nodes_len; ++i) + free_token (dfa->nodes + i); + re_free (dfa->nexts); + for (i = 0; i < dfa->nodes_len; ++i) + { + if (dfa->eclosures != NULL) + re_node_set_free (dfa->eclosures + i); + if (dfa->inveclosures != NULL) + re_node_set_free (dfa->inveclosures + i); + if (dfa->edests != NULL) + re_node_set_free (dfa->edests + i); + } + re_free (dfa->edests); + re_free (dfa->eclosures); + re_free (dfa->inveclosures); + re_free (dfa->nodes); + + if (dfa->state_table) + for (i = 0; i <= dfa->state_hash_mask; ++i) + { + struct re_state_table_entry *entry = dfa->state_table + i; + for (j = 0; j < entry->num; ++j) + { + re_dfastate_t *state = entry->array[j]; + free_state (state); + } + re_free (entry->array); + } + re_free (dfa->state_table); +#ifdef RE_ENABLE_I18N + if (dfa->sb_char != utf8_sb_map) + re_free (dfa->sb_char); +#endif + re_free (dfa->subexp_map); +#ifdef DEBUG + re_free (dfa->re_str); +#endif + + re_free (dfa); +} + + +/* Free dynamically allocated space used by PREG. */ + +void +regfree (preg) + regex_t *preg; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + if (BE (dfa != NULL, 1)) + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + + re_free (preg->fastmap); + preg->fastmap = NULL; + + re_free (preg->translate); + preg->translate = NULL; +} +#ifdef _LIBC +weak_alias (__regfree, regfree) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC + +/* BSD has one and only one pattern buffer. */ +static struct re_pattern_buffer re_comp_buf; + +char * +# ifdef _LIBC +/* Make these definitions weak in libc, so POSIX programs can redefine + these names if they don't use our functions, and still use + regcomp/regexec above without link errors. */ +weak_function +# endif +re_comp (s) + const char *s; +{ + reg_errcode_t ret; + char *fastmap; + + if (!s) + { + if (!re_comp_buf.buffer) + return gettext ("No previous regular expression"); + return 0; + } + + if (re_comp_buf.buffer) + { + fastmap = re_comp_buf.fastmap; + re_comp_buf.fastmap = NULL; + __regfree (&re_comp_buf); + memset (&re_comp_buf, '\0', sizeof (re_comp_buf)); + re_comp_buf.fastmap = fastmap; + } + + if (re_comp_buf.fastmap == NULL) + { + re_comp_buf.fastmap = (char *) malloc (SBC_MAX); + if (re_comp_buf.fastmap == NULL) + return (char *) gettext (__re_error_msgid + + __re_error_msgid_idx[(int) REG_ESPACE]); + } + + /* Since `re_exec' always passes NULL for the `regs' argument, we + don't need to initialize the pattern buffer fields which affect it. */ + + /* Match anchors at newlines. */ + re_comp_buf.newline_anchor = 1; + + ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options); + + if (!ret) + return NULL; + + /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ + return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); +} + +#ifdef _LIBC +libc_freeres_fn (free_mem) +{ + __regfree (&re_comp_buf); +} +#endif + +#endif /* _REGEX_RE_COMP */ + +/* Internal entry point. + Compile the regular expression PATTERN, whose length is LENGTH. + SYNTAX indicate regular expression's syntax. */ + +static reg_errcode_t +re_compile_internal (regex_t *preg, const char * pattern, size_t length, + reg_syntax_t syntax) +{ + reg_errcode_t err = REG_NOERROR; + re_dfa_t *dfa; + re_string_t regexp; + + /* Initialize the pattern buffer. */ + preg->fastmap_accurate = 0; + preg->syntax = syntax; + preg->not_bol = preg->not_eol = 0; + preg->used = 0; + preg->re_nsub = 0; + preg->can_be_null = 0; + preg->regs_allocated = REGS_UNALLOCATED; + + /* Initialize the dfa. */ + dfa = (re_dfa_t *) preg->buffer; + if (BE (preg->allocated < sizeof (re_dfa_t), 0)) + { + /* If zero allocated, but buffer is non-null, try to realloc + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. If ->buffer is NULL this + is a simple allocation. */ + dfa = re_realloc (preg->buffer, re_dfa_t, 1); + if (dfa == NULL) + return REG_ESPACE; + preg->allocated = sizeof (re_dfa_t); + preg->buffer = (unsigned char *) dfa; + } + preg->used = sizeof (re_dfa_t); + + err = init_dfa (dfa, length); + if (BE (err != REG_NOERROR, 0)) + { + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + return err; + } +#ifdef DEBUG + /* Note: length+1 will not overflow since it is checked in init_dfa. */ + dfa->re_str = re_malloc (char, length + 1); + strncpy (dfa->re_str, pattern, length + 1); +#endif + + __libc_lock_init (dfa->lock); + + err = re_string_construct (®exp, pattern, length, preg->translate, + syntax & RE_ICASE, dfa); + if (BE (err != REG_NOERROR, 0)) + { + re_compile_internal_free_return: + free_workarea_compile (preg); + re_string_destruct (®exp); + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + return err; + } + + /* Parse the regular expression, and build a structure tree. */ + preg->re_nsub = 0; + dfa->str_tree = parse (®exp, preg, syntax, &err); + if (BE (dfa->str_tree == NULL, 0)) + goto re_compile_internal_free_return; + + /* Analyze the tree and create the nfa. */ + err = analyze (preg); + if (BE (err != REG_NOERROR, 0)) + goto re_compile_internal_free_return; + +#ifdef RE_ENABLE_I18N + /* If possible, do searching in single byte encoding to speed things up. */ + if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL) + optimize_utf8 (dfa); +#endif + + /* Then create the initial state of the dfa. */ + err = create_initial_state (dfa); + + /* Release work areas. */ + free_workarea_compile (preg); + re_string_destruct (®exp); + + if (BE (err != REG_NOERROR, 0)) + { + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + } + + return err; +} + +/* Initialize DFA. We use the length of the regular expression PAT_LEN + as the initial length of some arrays. */ + +static reg_errcode_t +init_dfa (re_dfa_t *dfa, size_t pat_len) +{ + __re_size_t table_size; +#ifndef _LIBC + char *codeset_name; +#endif +#ifdef RE_ENABLE_I18N + size_t max_i18n_object_size = MAX (sizeof (wchar_t), sizeof (wctype_t)); +#else + size_t max_i18n_object_size = 0; +#endif + size_t max_object_size = + MAX (sizeof (struct re_state_table_entry), + MAX (sizeof (re_token_t), + MAX (sizeof (re_node_set), + MAX (sizeof (regmatch_t), + max_i18n_object_size)))); + + memset (dfa, '\0', sizeof (re_dfa_t)); + + /* Force allocation of str_tree_storage the first time. */ + dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; + + /* Avoid overflows. The extra "/ 2" is for the table_size doubling + calculation below, and for similar doubling calculations + elsewhere. And it's <= rather than <, because some of the + doubling calculations add 1 afterwards. */ + if (BE (SIZE_MAX / max_object_size / 2 <= pat_len, 0)) + return REG_ESPACE; + + dfa->nodes_alloc = pat_len + 1; + dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc); + + /* table_size = 2 ^ ceil(log pat_len) */ + for (table_size = 1; ; table_size <<= 1) + if (table_size > pat_len) + break; + + dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size); + dfa->state_hash_mask = table_size - 1; + + dfa->mb_cur_max = MB_CUR_MAX; +#ifdef _LIBC + if (dfa->mb_cur_max == 6 + && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0) + dfa->is_utf8 = 1; + dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII) + != 0); +#else +# ifdef HAVE_LANGINFO_CODESET + codeset_name = nl_langinfo (CODESET); +# else + codeset_name = getenv ("LC_ALL"); + if (codeset_name == NULL || codeset_name[0] == '\0') + codeset_name = getenv ("LC_CTYPE"); + if (codeset_name == NULL || codeset_name[0] == '\0') + codeset_name = getenv ("LANG"); + if (codeset_name == NULL) + codeset_name = ""; + else if (strchr (codeset_name, '.') != NULL) + codeset_name = strchr (codeset_name, '.') + 1; +# endif + + if (strcasecmp (codeset_name, "UTF-8") == 0 + || strcasecmp (codeset_name, "UTF8") == 0) + dfa->is_utf8 = 1; + + /* We check exhaustively in the loop below if this charset is a + superset of ASCII. */ + dfa->map_notascii = 0; +#endif + +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + if (dfa->is_utf8) + dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map; + else + { + int i, j, ch; + + dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); + if (BE (dfa->sb_char == NULL, 0)) + return REG_ESPACE; + + /* Set the bits corresponding to single byte chars. */ + for (i = 0, ch = 0; i < BITSET_WORDS; ++i) + for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) + { + wint_t wch = __btowc (ch); + if (wch != WEOF) + dfa->sb_char[i] |= (bitset_word_t) 1 << j; +# ifndef _LIBC + if (isascii (ch) && wch != ch) + dfa->map_notascii = 1; +# endif + } + } + } +#endif + + if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0)) + return REG_ESPACE; + return REG_NOERROR; +} + +/* Initialize WORD_CHAR table, which indicate which character is + "word". In this case "word" means that it is the word construction + character used by some operators like "\<", "\>", etc. */ + +static void +internal_function +init_word_char (re_dfa_t *dfa) +{ + int i, j, ch; + dfa->word_ops_used = 1; + for (i = 0, ch = 0; i < BITSET_WORDS; ++i) + for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) + if (isalnum (ch) || ch == '_') + dfa->word_char[i] |= (bitset_word_t) 1 << j; +} + +/* Free the work area which are only used while compiling. */ + +static void +free_workarea_compile (regex_t *preg) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_storage_t *storage, *next; + for (storage = dfa->str_tree_storage; storage; storage = next) + { + next = storage->next; + re_free (storage); + } + dfa->str_tree_storage = NULL; + dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; + dfa->str_tree = NULL; + re_free (dfa->org_indices); + dfa->org_indices = NULL; +} + +/* Create initial states for all contexts. */ + +static reg_errcode_t +create_initial_state (re_dfa_t *dfa) +{ + Idx first, i; + reg_errcode_t err; + re_node_set init_nodes; + + /* Initial states have the epsilon closure of the node which is + the first node of the regular expression. */ + first = dfa->str_tree->first->node_idx; + dfa->init_node = first; + err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* The back-references which are in initial states can epsilon transit, + since in this case all of the subexpressions can be null. + Then we add epsilon closures of the nodes which are the next nodes of + the back-references. */ + if (dfa->nbackref > 0) + for (i = 0; i < init_nodes.nelem; ++i) + { + Idx node_idx = init_nodes.elems[i]; + re_token_type_t type = dfa->nodes[node_idx].type; + + Idx clexp_idx; + if (type != OP_BACK_REF) + continue; + for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx) + { + re_token_t *clexp_node; + clexp_node = dfa->nodes + init_nodes.elems[clexp_idx]; + if (clexp_node->type == OP_CLOSE_SUBEXP + && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx) + break; + } + if (clexp_idx == init_nodes.nelem) + continue; + + if (type == OP_BACK_REF) + { + Idx dest_idx = dfa->edests[node_idx].elems[0]; + if (!re_node_set_contains (&init_nodes, dest_idx)) + { + re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx); + i = 0; + } + } + } + + /* It must be the first time to invoke acquire_state. */ + dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0); + /* We don't check ERR here, since the initial state must not be NULL. */ + if (BE (dfa->init_state == NULL, 0)) + return err; + if (dfa->init_state->has_constraint) + { + dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes, + CONTEXT_WORD); + dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes, + CONTEXT_NEWLINE); + dfa->init_state_begbuf = re_acquire_state_context (&err, dfa, + &init_nodes, + CONTEXT_NEWLINE + | CONTEXT_BEGBUF); + if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL + || dfa->init_state_begbuf == NULL, 0)) + return err; + } + else + dfa->init_state_word = dfa->init_state_nl + = dfa->init_state_begbuf = dfa->init_state; + + re_node_set_free (&init_nodes); + return REG_NOERROR; +} + +#ifdef RE_ENABLE_I18N +/* If it is possible to do searching in single byte encoding instead of UTF-8 + to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change + DFA nodes where needed. */ + +static void +optimize_utf8 (re_dfa_t *dfa) +{ + Idx node; + int i; + bool mb_chars = false; + bool has_period = false; + + for (node = 0; node < dfa->nodes_len; ++node) + switch (dfa->nodes[node].type) + { + case CHARACTER: + if (dfa->nodes[node].opr.c >= ASCII_CHARS) + mb_chars = true; + break; + case ANCHOR: + switch (dfa->nodes[node].opr.idx) + { + case LINE_FIRST: + case LINE_LAST: + case BUF_FIRST: + case BUF_LAST: + break; + default: + /* Word anchors etc. cannot be handled. */ + return; + } + break; + case OP_PERIOD: + has_period = true; + break; + case OP_BACK_REF: + case OP_ALT: + case END_OF_RE: + case OP_DUP_ASTERISK: + case OP_OPEN_SUBEXP: + case OP_CLOSE_SUBEXP: + break; + case COMPLEX_BRACKET: + return; + case SIMPLE_BRACKET: + /* Just double check. */ + { + int rshift = (ASCII_CHARS % BITSET_WORD_BITS == 0 + ? 0 + : BITSET_WORD_BITS - ASCII_CHARS % BITSET_WORD_BITS); + for (i = ASCII_CHARS / BITSET_WORD_BITS; i < BITSET_WORDS; ++i) + { + if (dfa->nodes[node].opr.sbcset[i] >> rshift != 0) + return; + rshift = 0; + } + } + break; + default: + abort (); + } + + if (mb_chars || has_period) + for (node = 0; node < dfa->nodes_len; ++node) + { + if (dfa->nodes[node].type == CHARACTER + && dfa->nodes[node].opr.c >= ASCII_CHARS) + dfa->nodes[node].mb_partial = 0; + else if (dfa->nodes[node].type == OP_PERIOD) + dfa->nodes[node].type = OP_UTF8_PERIOD; + } + + /* The search can be in single byte locale. */ + dfa->mb_cur_max = 1; + dfa->is_utf8 = 0; + dfa->has_mb_node = dfa->nbackref > 0 || has_period; +} +#endif + +/* Analyze the structure tree, and calculate "first", "next", "edest", + "eclosure", and "inveclosure". */ + +static reg_errcode_t +analyze (regex_t *preg) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + reg_errcode_t ret; + + /* Allocate arrays. */ + dfa->nexts = re_malloc (Idx, dfa->nodes_alloc); + dfa->org_indices = re_malloc (Idx, dfa->nodes_alloc); + dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc); + dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc); + if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL + || dfa->eclosures == NULL, 0)) + return REG_ESPACE; + + dfa->subexp_map = re_malloc (Idx, preg->re_nsub); + if (dfa->subexp_map != NULL) + { + Idx i; + for (i = 0; i < preg->re_nsub; i++) + dfa->subexp_map[i] = i; + preorder (dfa->str_tree, optimize_subexps, dfa); + for (i = 0; i < preg->re_nsub; i++) + if (dfa->subexp_map[i] != i) + break; + if (i == preg->re_nsub) + { + free (dfa->subexp_map); + dfa->subexp_map = NULL; + } + } + + ret = postorder (dfa->str_tree, lower_subexps, preg); + if (BE (ret != REG_NOERROR, 0)) + return ret; + ret = postorder (dfa->str_tree, calc_first, dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + preorder (dfa->str_tree, calc_next, dfa); + ret = preorder (dfa->str_tree, link_nfa_nodes, dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + ret = calc_eclosure (dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + /* We only need this during the prune_impossible_nodes pass in regexec.c; + skip it if p_i_n will not run, as calc_inveclosure can be quadratic. */ + if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match) + || dfa->nbackref) + { + dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len); + if (BE (dfa->inveclosures == NULL, 0)) + return REG_ESPACE; + ret = calc_inveclosure (dfa); + } + + return ret; +} + +/* Our parse trees are very unbalanced, so we cannot use a stack to + implement parse tree visits. Instead, we use parent pointers and + some hairy code in these two functions. */ +static reg_errcode_t +postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra) +{ + bin_tree_t *node, *prev; + + for (node = root; ; ) + { + /* Descend down the tree, preferably to the left (or to the right + if that's the only child). */ + while (node->left || node->right) + if (node->left) + node = node->left; + else + node = node->right; + + do + { + reg_errcode_t err = fn (extra, node); + if (BE (err != REG_NOERROR, 0)) + return err; + if (node->parent == NULL) + return REG_NOERROR; + prev = node; + node = node->parent; + } + /* Go up while we have a node that is reached from the right. */ + while (node->right == prev || node->right == NULL); + node = node->right; + } +} + +static reg_errcode_t +preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra) +{ + bin_tree_t *node; + + for (node = root; ; ) + { + reg_errcode_t err = fn (extra, node); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* Go to the left node, or up and to the right. */ + if (node->left) + node = node->left; + else + { + bin_tree_t *prev = NULL; + while (node->right == prev || node->right == NULL) + { + prev = node; + node = node->parent; + if (!node) + return REG_NOERROR; + } + node = node->right; + } + } +} + +/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell + re_search_internal to map the inner one's opr.idx to this one's. Adjust + backreferences as well. Requires a preorder visit. */ +static reg_errcode_t +optimize_subexps (void *extra, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + + if (node->token.type == OP_BACK_REF && dfa->subexp_map) + { + int idx = node->token.opr.idx; + node->token.opr.idx = dfa->subexp_map[idx]; + dfa->used_bkref_map |= 1 << node->token.opr.idx; + } + + else if (node->token.type == SUBEXP + && node->left && node->left->token.type == SUBEXP) + { + Idx other_idx = node->left->token.opr.idx; + + node->left = node->left->left; + if (node->left) + node->left->parent = node; + + dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx]; + if (other_idx < BITSET_WORD_BITS) + dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx); + } + + return REG_NOERROR; +} + +/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation + of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP. */ +static reg_errcode_t +lower_subexps (void *extra, bin_tree_t *node) +{ + regex_t *preg = (regex_t *) extra; + reg_errcode_t err = REG_NOERROR; + + if (node->left && node->left->token.type == SUBEXP) + { + node->left = lower_subexp (&err, preg, node->left); + if (node->left) + node->left->parent = node; + } + if (node->right && node->right->token.type == SUBEXP) + { + node->right = lower_subexp (&err, preg, node->right); + if (node->right) + node->right->parent = node; + } + + return err; +} + +static bin_tree_t * +lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *body = node->left; + bin_tree_t *op, *cls, *tree1, *tree; + + if (preg->no_sub + /* We do not optimize empty subexpressions, because otherwise we may + have bad CONCAT nodes with NULL children. This is obviously not + very common, so we do not lose much. An example that triggers + this case is the sed "script" /\(\)/x. */ + && node->left != NULL + && (node->token.opr.idx >= BITSET_WORD_BITS + || !(dfa->used_bkref_map + & ((bitset_word_t) 1 << node->token.opr.idx)))) + return node->left; + + /* Convert the SUBEXP node to the concatenation of an + OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP. */ + op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP); + cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP); + tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls; + tree = create_tree (dfa, op, tree1, CONCAT); + if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + + op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx; + op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp; + return tree; +} + +/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton + nodes. Requires a postorder visit. */ +static reg_errcode_t +calc_first (void *extra, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + if (node->token.type == CONCAT) + { + node->first = node->left->first; + node->node_idx = node->left->node_idx; + } + else + { + node->first = node; + node->node_idx = re_dfa_add_node (dfa, node->token); + if (BE (node->node_idx == REG_MISSING, 0)) + return REG_ESPACE; + } + return REG_NOERROR; +} + +/* Pass 2: compute NEXT on the tree. Preorder visit. */ +static reg_errcode_t +calc_next (void *extra, bin_tree_t *node) +{ + switch (node->token.type) + { + case OP_DUP_ASTERISK: + node->left->next = node; + break; + case CONCAT: + node->left->next = node->right->first; + node->right->next = node->next; + break; + default: + if (node->left) + node->left->next = node->next; + if (node->right) + node->right->next = node->next; + break; + } + return REG_NOERROR; +} + +/* Pass 3: link all DFA nodes to their NEXT node (any order will do). */ +static reg_errcode_t +link_nfa_nodes (void *extra, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + Idx idx = node->node_idx; + reg_errcode_t err = REG_NOERROR; + + switch (node->token.type) + { + case CONCAT: + break; + + case END_OF_RE: + assert (node->next == NULL); + break; + + case OP_DUP_ASTERISK: + case OP_ALT: + { + Idx left, right; + dfa->has_plural_match = 1; + if (node->left != NULL) + left = node->left->first->node_idx; + else + left = node->next->node_idx; + if (node->right != NULL) + right = node->right->first->node_idx; + else + right = node->next->node_idx; + assert (REG_VALID_INDEX (left)); + assert (REG_VALID_INDEX (right)); + err = re_node_set_init_2 (dfa->edests + idx, left, right); + } + break; + + case ANCHOR: + case OP_OPEN_SUBEXP: + case OP_CLOSE_SUBEXP: + err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx); + break; + + case OP_BACK_REF: + dfa->nexts[idx] = node->next->node_idx; + if (node->token.type == OP_BACK_REF) + re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]); + break; + + default: + assert (!IS_EPSILON_NODE (node->token.type)); + dfa->nexts[idx] = node->next->node_idx; + break; + } + + return err; +} + +/* Duplicate the epsilon closure of the node ROOT_NODE. + Note that duplicated nodes have constraint INIT_CONSTRAINT in addition + to their own constraint. */ + +static reg_errcode_t +internal_function +duplicate_node_closure (re_dfa_t *dfa, Idx top_org_node, Idx top_clone_node, + Idx root_node, unsigned int init_constraint) +{ + Idx org_node, clone_node; + bool ok; + unsigned int constraint = init_constraint; + for (org_node = top_org_node, clone_node = top_clone_node;;) + { + Idx org_dest, clone_dest; + if (dfa->nodes[org_node].type == OP_BACK_REF) + { + /* If the back reference epsilon-transit, its destination must + also have the constraint. Then duplicate the epsilon closure + of the destination of the back reference, and store it in + edests of the back reference. */ + org_dest = dfa->nexts[org_node]; + re_node_set_empty (dfa->edests + clone_node); + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == REG_MISSING, 0)) + return REG_ESPACE; + dfa->nexts[clone_node] = dfa->nexts[org_node]; + ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + } + else if (dfa->edests[org_node].nelem == 0) + { + /* In case of the node can't epsilon-transit, don't duplicate the + destination and store the original destination as the + destination of the node. */ + dfa->nexts[clone_node] = dfa->nexts[org_node]; + break; + } + else if (dfa->edests[org_node].nelem == 1) + { + /* In case of the node can epsilon-transit, and it has only one + destination. */ + org_dest = dfa->edests[org_node].elems[0]; + re_node_set_empty (dfa->edests + clone_node); + if (dfa->nodes[org_node].type == ANCHOR) + { + /* In case of the node has another constraint, append it. */ + if (org_node == root_node && clone_node != org_node) + { + /* ...but if the node is root_node itself, it means the + epsilon closure have a loop, then tie it to the + destination of the root_node. */ + ok = re_node_set_insert (dfa->edests + clone_node, org_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + break; + } + constraint |= dfa->nodes[org_node].opr.ctx_type; + } + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == REG_MISSING, 0)) + return REG_ESPACE; + ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + } + else /* dfa->edests[org_node].nelem == 2 */ + { + /* In case of the node can epsilon-transit, and it has two + destinations. In the bin_tree_t and DFA, that's '|' and '*'. */ + org_dest = dfa->edests[org_node].elems[0]; + re_node_set_empty (dfa->edests + clone_node); + /* Search for a duplicated node which satisfies the constraint. */ + clone_dest = search_duplicated_node (dfa, org_dest, constraint); + if (clone_dest == REG_MISSING) + { + /* There are no such a duplicated node, create a new one. */ + reg_errcode_t err; + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == REG_MISSING, 0)) + return REG_ESPACE; + ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + err = duplicate_node_closure (dfa, org_dest, clone_dest, + root_node, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + { + /* There are a duplicated node which satisfy the constraint, + use it to avoid infinite loop. */ + ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + } + + org_dest = dfa->edests[org_node].elems[1]; + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == REG_MISSING, 0)) + return REG_ESPACE; + ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + } + org_node = org_dest; + clone_node = clone_dest; + } + return REG_NOERROR; +} + +/* Search for a node which is duplicated from the node ORG_NODE, and + satisfies the constraint CONSTRAINT. */ + +static Idx +search_duplicated_node (const re_dfa_t *dfa, Idx org_node, + unsigned int constraint) +{ + Idx idx; + for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx) + { + if (org_node == dfa->org_indices[idx] + && constraint == dfa->nodes[idx].constraint) + return idx; /* Found. */ + } + return REG_MISSING; /* Not found. */ +} + +/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT. + Return the index of the new node, or REG_MISSING if insufficient storage is + available. */ + +static Idx +duplicate_node (re_dfa_t *dfa, Idx org_idx, unsigned int constraint) +{ + Idx dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]); + if (BE (dup_idx != REG_MISSING, 1)) + { + dfa->nodes[dup_idx].constraint = constraint; + if (dfa->nodes[org_idx].type == ANCHOR) + dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].opr.ctx_type; + dfa->nodes[dup_idx].duplicated = 1; + + /* Store the index of the original node. */ + dfa->org_indices[dup_idx] = org_idx; + } + return dup_idx; +} + +static reg_errcode_t +calc_inveclosure (re_dfa_t *dfa) +{ + Idx src, idx; + bool ok; + for (idx = 0; idx < dfa->nodes_len; ++idx) + re_node_set_init_empty (dfa->inveclosures + idx); + + for (src = 0; src < dfa->nodes_len; ++src) + { + Idx *elems = dfa->eclosures[src].elems; + for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx) + { + ok = re_node_set_insert_last (dfa->inveclosures + elems[idx], src); + if (BE (! ok, 0)) + return REG_ESPACE; + } + } + + return REG_NOERROR; +} + +/* Calculate "eclosure" for all the node in DFA. */ + +static reg_errcode_t +calc_eclosure (re_dfa_t *dfa) +{ + Idx node_idx; + bool incomplete; +#ifdef DEBUG + assert (dfa->nodes_len > 0); +#endif + incomplete = false; + /* For each nodes, calculate epsilon closure. */ + for (node_idx = 0; ; ++node_idx) + { + reg_errcode_t err; + re_node_set eclosure_elem; + if (node_idx == dfa->nodes_len) + { + if (!incomplete) + break; + incomplete = false; + node_idx = 0; + } + +#ifdef DEBUG + assert (dfa->eclosures[node_idx].nelem != REG_MISSING); +#endif + + /* If we have already calculated, skip it. */ + if (dfa->eclosures[node_idx].nelem != 0) + continue; + /* Calculate epsilon closure of `node_idx'. */ + err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, true); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (dfa->eclosures[node_idx].nelem == 0) + { + incomplete = true; + re_node_set_free (&eclosure_elem); + } + } + return REG_NOERROR; +} + +/* Calculate epsilon closure of NODE. */ + +static reg_errcode_t +calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, Idx node, bool root) +{ + reg_errcode_t err; + unsigned int constraint; + Idx i; + bool incomplete; + bool ok; + re_node_set eclosure; + incomplete = false; + err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* This indicates that we are calculating this node now. + We reference this value to avoid infinite loop. */ + dfa->eclosures[node].nelem = REG_MISSING; + + constraint = ((dfa->nodes[node].type == ANCHOR) + ? dfa->nodes[node].opr.ctx_type : 0); + /* If the current node has constraints, duplicate all nodes. + Since they must inherit the constraints. */ + if (constraint + && dfa->edests[node].nelem + && !dfa->nodes[dfa->edests[node].elems[0]].duplicated) + { + err = duplicate_node_closure (dfa, node, node, node, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Expand each epsilon destination nodes. */ + if (IS_EPSILON_NODE(dfa->nodes[node].type)) + for (i = 0; i < dfa->edests[node].nelem; ++i) + { + re_node_set eclosure_elem; + Idx edest = dfa->edests[node].elems[i]; + /* If calculating the epsilon closure of `edest' is in progress, + return intermediate result. */ + if (dfa->eclosures[edest].nelem == REG_MISSING) + { + incomplete = true; + continue; + } + /* If we haven't calculated the epsilon closure of `edest' yet, + calculate now. Otherwise use calculated epsilon closure. */ + if (dfa->eclosures[edest].nelem == 0) + { + err = calc_eclosure_iter (&eclosure_elem, dfa, edest, false); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + eclosure_elem = dfa->eclosures[edest]; + /* Merge the epsilon closure of `edest'. */ + re_node_set_merge (&eclosure, &eclosure_elem); + /* If the epsilon closure of `edest' is incomplete, + the epsilon closure of this node is also incomplete. */ + if (dfa->eclosures[edest].nelem == 0) + { + incomplete = true; + re_node_set_free (&eclosure_elem); + } + } + + /* Epsilon closures include itself. */ + ok = re_node_set_insert (&eclosure, node); + if (BE (! ok, 0)) + return REG_ESPACE; + if (incomplete && !root) + dfa->eclosures[node].nelem = 0; + else + dfa->eclosures[node] = eclosure; + *new_set = eclosure; + return REG_NOERROR; +} + +/* Functions for token which are used in the parser. */ + +/* Fetch a token from INPUT. + We must not use this function inside bracket expressions. */ + +static void +internal_function +fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax) +{ + re_string_skip_bytes (input, peek_token (result, input, syntax)); +} + +/* Peek a token from INPUT, and return the length of the token. + We must not use this function inside bracket expressions. */ + +static int +internal_function +peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax) +{ + unsigned char c; + + if (re_string_eoi (input)) + { + token->type = END_OF_RE; + return 0; + } + + c = re_string_peek_byte (input, 0); + token->opr.c = c; + + token->word_char = 0; +#ifdef RE_ENABLE_I18N + token->mb_partial = 0; + if (input->mb_cur_max > 1 && + !re_string_first_byte (input, re_string_cur_idx (input))) + { + token->type = CHARACTER; + token->mb_partial = 1; + return 1; + } +#endif + if (c == '\\') + { + unsigned char c2; + if (re_string_cur_idx (input) + 1 >= re_string_length (input)) + { + token->type = BACK_SLASH; + return 1; + } + + c2 = re_string_peek_byte_case (input, 1); + token->opr.c = c2; + token->type = CHARACTER; +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc = re_string_wchar_at (input, + re_string_cur_idx (input) + 1); + token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; + } + else +#endif + token->word_char = IS_WORD_CHAR (c2) != 0; + + switch (c2) + { + case '|': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR)) + token->type = OP_ALT; + break; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (!(syntax & RE_NO_BK_REFS)) + { + token->type = OP_BACK_REF; + token->opr.idx = c2 - '1'; + } + break; + case '<': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_FIRST; + } + break; + case '>': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_LAST; + } + break; + case 'b': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_DELIM; + } + break; + case 'B': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = NOT_WORD_DELIM; + } + break; + case 'w': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_WORD; + break; + case 'W': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_NOTWORD; + break; + case 's': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_SPACE; + break; + case 'S': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_NOTSPACE; + break; + case '`': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = BUF_FIRST; + } + break; + case '\'': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = BUF_LAST; + } + break; + case '(': + if (!(syntax & RE_NO_BK_PARENS)) + token->type = OP_OPEN_SUBEXP; + break; + case ')': + if (!(syntax & RE_NO_BK_PARENS)) + token->type = OP_CLOSE_SUBEXP; + break; + case '+': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_PLUS; + break; + case '?': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_QUESTION; + break; + case '{': + if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) + token->type = OP_OPEN_DUP_NUM; + break; + case '}': + if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) + token->type = OP_CLOSE_DUP_NUM; + break; + default: + break; + } + return 2; + } + + token->type = CHARACTER; +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input)); + token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; + } + else +#endif + token->word_char = IS_WORD_CHAR (token->opr.c); + + switch (c) + { + case '\n': + if (syntax & RE_NEWLINE_ALT) + token->type = OP_ALT; + break; + case '|': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR)) + token->type = OP_ALT; + break; + case '*': + token->type = OP_DUP_ASTERISK; + break; + case '+': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_PLUS; + break; + case '?': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_QUESTION; + break; + case '{': + if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + token->type = OP_OPEN_DUP_NUM; + break; + case '}': + if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + token->type = OP_CLOSE_DUP_NUM; + break; + case '(': + if (syntax & RE_NO_BK_PARENS) + token->type = OP_OPEN_SUBEXP; + break; + case ')': + if (syntax & RE_NO_BK_PARENS) + token->type = OP_CLOSE_SUBEXP; + break; + case '[': + token->type = OP_OPEN_BRACKET; + break; + case '.': + token->type = OP_PERIOD; + break; + case '^': + if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) && + re_string_cur_idx (input) != 0) + { + char prev = re_string_peek_byte (input, -1); + if (!(syntax & RE_NEWLINE_ALT) || prev != '\n') + break; + } + token->type = ANCHOR; + token->opr.ctx_type = LINE_FIRST; + break; + case '$': + if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) && + re_string_cur_idx (input) + 1 != re_string_length (input)) + { + re_token_t next; + re_string_skip_bytes (input, 1); + peek_token (&next, input, syntax); + re_string_skip_bytes (input, -1); + if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP) + break; + } + token->type = ANCHOR; + token->opr.ctx_type = LINE_LAST; + break; + default: + break; + } + return 1; +} + +/* Peek a token from INPUT, and return the length of the token. + We must not use this function out of bracket expressions. */ + +static int +internal_function +peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax) +{ + unsigned char c; + if (re_string_eoi (input)) + { + token->type = END_OF_RE; + return 0; + } + c = re_string_peek_byte (input, 0); + token->opr.c = c; + +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1 && + !re_string_first_byte (input, re_string_cur_idx (input))) + { + token->type = CHARACTER; + return 1; + } +#endif /* RE_ENABLE_I18N */ + + if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) + && re_string_cur_idx (input) + 1 < re_string_length (input)) + { + /* In this case, '\' escape a character. */ + unsigned char c2; + re_string_skip_bytes (input, 1); + c2 = re_string_peek_byte (input, 0); + token->opr.c = c2; + token->type = CHARACTER; + return 1; + } + if (c == '[') /* '[' is a special char in a bracket exps. */ + { + unsigned char c2; + int token_len; + if (re_string_cur_idx (input) + 1 < re_string_length (input)) + c2 = re_string_peek_byte (input, 1); + else + c2 = 0; + token->opr.c = c2; + token_len = 2; + switch (c2) + { + case '.': + token->type = OP_OPEN_COLL_ELEM; + break; + case '=': + token->type = OP_OPEN_EQUIV_CLASS; + break; + case ':': + if (syntax & RE_CHAR_CLASSES) + { + token->type = OP_OPEN_CHAR_CLASS; + break; + } + /* else fall through. */ + default: + token->type = CHARACTER; + token->opr.c = c; + token_len = 1; + break; + } + return token_len; + } + switch (c) + { + case '-': + token->type = OP_CHARSET_RANGE; + break; + case ']': + token->type = OP_CLOSE_BRACKET; + break; + case '^': + token->type = OP_NON_MATCH_LIST; + break; + default: + token->type = CHARACTER; + } + return 1; +} + +/* Functions for parser. */ + +/* Entry point of the parser. + Parse the regular expression REGEXP and return the structure tree. + If an error is occured, ERR is set by error code, and return NULL. + This function build the following tree, from regular expression : + CAT + / \ + / \ + EOR + + CAT means concatenation. + EOR means end of regular expression. */ + +static bin_tree_t * +parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax, + reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *eor, *root; + re_token_t current_token; + dfa->syntax = syntax; + fetch_token (¤t_token, regexp, syntax | RE_CARET_ANCHORS_HERE); + tree = parse_reg_exp (regexp, preg, ¤t_token, syntax, 0, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + eor = create_tree (dfa, NULL, NULL, END_OF_RE); + if (tree != NULL) + root = create_tree (dfa, tree, eor, CONCAT); + else + root = eor; + if (BE (eor == NULL || root == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + return root; +} + +/* This function build the following tree, from regular expression + |: + ALT + / \ + / \ + + + ALT means alternative, which represents the operator `|'. */ + +static bin_tree_t * +parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, Idx nest, reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *branch = NULL; + tree = parse_branch (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + + while (token->type == OP_ALT) + { + fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); + if (token->type != OP_ALT && token->type != END_OF_RE + && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) + { + branch = parse_branch (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && branch == NULL, 0)) + return NULL; + } + else + branch = NULL; + tree = create_tree (dfa, tree, branch, OP_ALT); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + return tree; +} + +/* This function build the following tree, from regular expression + : + CAT + / \ + / \ + + + CAT means concatenation. */ + +static bin_tree_t * +parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, Idx nest, reg_errcode_t *err) +{ + bin_tree_t *tree, *exp; + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + tree = parse_expression (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + + while (token->type != OP_ALT && token->type != END_OF_RE + && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) + { + exp = parse_expression (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && exp == NULL, 0)) + { + return NULL; + } + if (tree != NULL && exp != NULL) + { + tree = create_tree (dfa, tree, exp, CONCAT); + if (tree == NULL) + { + *err = REG_ESPACE; + return NULL; + } + } + else if (tree == NULL) + tree = exp; + /* Otherwise exp == NULL, we don't need to create new tree. */ + } + return tree; +} + +/* This function build the following tree, from regular expression a*: + * + | + a +*/ + +static bin_tree_t * +parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, Idx nest, reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree; + switch (token->type) + { + case CHARACTER: + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + while (!re_string_eoi (regexp) + && !re_string_first_byte (regexp, re_string_cur_idx (regexp))) + { + bin_tree_t *mbc_remain; + fetch_token (token, regexp, syntax); + mbc_remain = create_token_tree (dfa, NULL, NULL, token); + tree = create_tree (dfa, tree, mbc_remain, CONCAT); + if (BE (mbc_remain == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + } +#endif + break; + case OP_OPEN_SUBEXP: + tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_OPEN_BRACKET: + tree = parse_bracket_exp (regexp, dfa, token, syntax, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_BACK_REF: + if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1)) + { + *err = REG_ESUBREG; + return NULL; + } + dfa->used_bkref_map |= 1 << token->opr.idx; + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + ++dfa->nbackref; + dfa->has_mb_node = 1; + break; + case OP_OPEN_DUP_NUM: + if (syntax & RE_CONTEXT_INVALID_DUP) + { + *err = REG_BADRPT; + return NULL; + } + /* FALLTHROUGH */ + case OP_DUP_ASTERISK: + case OP_DUP_PLUS: + case OP_DUP_QUESTION: + if (syntax & RE_CONTEXT_INVALID_OPS) + { + *err = REG_BADRPT; + return NULL; + } + else if (syntax & RE_CONTEXT_INDEP_OPS) + { + fetch_token (token, regexp, syntax); + return parse_expression (regexp, preg, token, syntax, nest, err); + } + /* else fall through */ + case OP_CLOSE_SUBEXP: + if ((token->type == OP_CLOSE_SUBEXP) && + !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)) + { + *err = REG_ERPAREN; + return NULL; + } + /* else fall through */ + case OP_CLOSE_DUP_NUM: + /* We treat it as a normal character. */ + + /* Then we can these characters as normal characters. */ + token->type = CHARACTER; + /* mb_partial and word_char bits should be initialized already + by peek_token. */ + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + break; + case ANCHOR: + if ((token->opr.ctx_type + & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST)) + && dfa->word_ops_used == 0) + init_word_char (dfa); + if (token->opr.ctx_type == WORD_DELIM + || token->opr.ctx_type == NOT_WORD_DELIM) + { + bin_tree_t *tree_first, *tree_last; + if (token->opr.ctx_type == WORD_DELIM) + { + token->opr.ctx_type = WORD_FIRST; + tree_first = create_token_tree (dfa, NULL, NULL, token); + token->opr.ctx_type = WORD_LAST; + } + else + { + token->opr.ctx_type = INSIDE_WORD; + tree_first = create_token_tree (dfa, NULL, NULL, token); + token->opr.ctx_type = INSIDE_NOTWORD; + } + tree_last = create_token_tree (dfa, NULL, NULL, token); + tree = create_tree (dfa, tree_first, tree_last, OP_ALT); + if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + else + { + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + /* We must return here, since ANCHORs can't be followed + by repetition operators. + eg. RE"^*" is invalid or "", + it must not be "". */ + fetch_token (token, regexp, syntax); + return tree; + case OP_PERIOD: + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + if (dfa->mb_cur_max > 1) + dfa->has_mb_node = 1; + break; + case OP_WORD: + case OP_NOTWORD: + tree = build_charclass_op (dfa, regexp->trans, + (const unsigned char *) "alnum", + (const unsigned char *) "_", + token->type == OP_NOTWORD, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_SPACE: + case OP_NOTSPACE: + tree = build_charclass_op (dfa, regexp->trans, + (const unsigned char *) "space", + (const unsigned char *) "", + token->type == OP_NOTSPACE, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_ALT: + case END_OF_RE: + return NULL; + case BACK_SLASH: + *err = REG_EESCAPE; + return NULL; + default: + /* Must not happen? */ +#ifdef DEBUG + assert (0); +#endif + return NULL; + } + fetch_token (token, regexp, syntax); + + while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS + || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM) + { + tree = parse_dup_op (tree, regexp, dfa, token, syntax, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + /* In BRE consecutive duplications are not allowed. */ + if ((syntax & RE_CONTEXT_INVALID_DUP) + && (token->type == OP_DUP_ASTERISK + || token->type == OP_OPEN_DUP_NUM)) + { + *err = REG_BADRPT; + return NULL; + } + } + + return tree; +} + +/* This function build the following tree, from regular expression + (): + SUBEXP + | + +*/ + +static bin_tree_t * +parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, Idx nest, reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree; + size_t cur_nsub; + cur_nsub = preg->re_nsub++; + + fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); + + /* The subexpression may be a null string. */ + if (token->type == OP_CLOSE_SUBEXP) + tree = NULL; + else + { + tree = parse_reg_exp (regexp, preg, token, syntax, nest, err); + if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0)) + *err = REG_EPAREN; + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + + if (cur_nsub <= '9' - '1') + dfa->completed_bkref_map |= 1 << cur_nsub; + + tree = create_tree (dfa, tree, NULL, SUBEXP); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + tree->token.opr.idx = cur_nsub; + return tree; +} + +/* This function parse repetition operators like "*", "+", "{1,3}" etc. */ + +static bin_tree_t * +parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa, + re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err) +{ + bin_tree_t *tree = NULL, *old_tree = NULL; + Idx i, start, end, start_idx = re_string_cur_idx (regexp); + re_token_t start_token = *token; + + if (token->type == OP_OPEN_DUP_NUM) + { + end = 0; + start = fetch_number (regexp, token, syntax); + if (start == REG_MISSING) + { + if (token->type == CHARACTER && token->opr.c == ',') + start = 0; /* We treat "{,m}" as "{0,m}". */ + else + { + *err = REG_BADBR; /* {} is invalid. */ + return NULL; + } + } + if (BE (start != REG_ERROR, 1)) + { + /* We treat "{n}" as "{n,n}". */ + end = ((token->type == OP_CLOSE_DUP_NUM) ? start + : ((token->type == CHARACTER && token->opr.c == ',') + ? fetch_number (regexp, token, syntax) : REG_ERROR)); + } + if (BE (start == REG_ERROR || end == REG_ERROR, 0)) + { + /* Invalid sequence. */ + if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0)) + { + if (token->type == END_OF_RE) + *err = REG_EBRACE; + else + *err = REG_BADBR; + + return NULL; + } + + /* If the syntax bit is set, rollback. */ + re_string_set_index (regexp, start_idx); + *token = start_token; + token->type = CHARACTER; + /* mb_partial and word_char bits should be already initialized by + peek_token. */ + return elem; + } + + if (BE (end != REG_MISSING && start > end, 0)) + { + /* First number greater than second. */ + *err = REG_BADBR; + return NULL; + } + } + else + { + start = (token->type == OP_DUP_PLUS) ? 1 : 0; + end = (token->type == OP_DUP_QUESTION) ? 1 : REG_MISSING; + } + + fetch_token (token, regexp, syntax); + + if (BE (elem == NULL, 0)) + return NULL; + if (BE (start == 0 && end == 0, 0)) + { + postorder (elem, free_tree, NULL); + return NULL; + } + + /* Extract "{n,m}" to "...{0,}". */ + if (BE (start > 0, 0)) + { + tree = elem; + for (i = 2; i <= start; ++i) + { + elem = duplicate_tree (elem, dfa); + tree = create_tree (dfa, tree, elem, CONCAT); + if (BE (elem == NULL || tree == NULL, 0)) + goto parse_dup_op_espace; + } + + if (start == end) + return tree; + + /* Duplicate ELEM before it is marked optional. */ + elem = duplicate_tree (elem, dfa); + old_tree = tree; + } + else + old_tree = NULL; + + if (elem->token.type == SUBEXP) + postorder (elem, mark_opt_subexp, (void *) (long) elem->token.opr.idx); + + tree = create_tree (dfa, elem, NULL, + (end == REG_MISSING ? OP_DUP_ASTERISK : OP_ALT)); + if (BE (tree == NULL, 0)) + goto parse_dup_op_espace; + + /* This loop is actually executed only when end != REG_MISSING, + to rewrite {0,n} as ((...?)?)?... We have + already created the start+1-th copy. */ + if ((Idx) -1 < 0 || end != REG_MISSING) + for (i = start + 2; i <= end; ++i) + { + elem = duplicate_tree (elem, dfa); + tree = create_tree (dfa, tree, elem, CONCAT); + if (BE (elem == NULL || tree == NULL, 0)) + goto parse_dup_op_espace; + + tree = create_tree (dfa, tree, NULL, OP_ALT); + if (BE (tree == NULL, 0)) + goto parse_dup_op_espace; + } + + if (old_tree) + tree = create_tree (dfa, old_tree, tree, CONCAT); + + return tree; + + parse_dup_op_espace: + *err = REG_ESPACE; + return NULL; +} + +/* Size of the names for collating symbol/equivalence_class/character_class. + I'm not sure, but maybe enough. */ +#define BRACKET_NAME_BUF_SIZE 32 + +#ifndef _LIBC + /* Local function for parse_bracket_exp only used in case of NOT _LIBC. + Build the range expression which starts from START_ELEM, and ends + at END_ELEM. The result are written to MBCSET and SBCSET. + RANGE_ALLOC is the allocated size of mbcset->range_starts, and + mbcset->range_ends, is a pointer argument sinse we may + update it. */ + +static reg_errcode_t +internal_function +# ifdef RE_ENABLE_I18N +build_range_exp (bitset_t sbcset, re_charset_t *mbcset, Idx *range_alloc, + bracket_elem_t *start_elem, bracket_elem_t *end_elem) +# else /* not RE_ENABLE_I18N */ +build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem, + bracket_elem_t *end_elem) +# endif /* not RE_ENABLE_I18N */ +{ + unsigned int start_ch, end_ch; + /* Equivalence Classes and Character Classes can't be a range start/end. */ + if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS + || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, + 0)) + return REG_ERANGE; + + /* We can handle no multi character collating elements without libc + support. */ + if (BE ((start_elem->type == COLL_SYM + && strlen ((char *) start_elem->opr.name) > 1) + || (end_elem->type == COLL_SYM + && strlen ((char *) end_elem->opr.name) > 1), 0)) + return REG_ECOLLATE; + +# ifdef RE_ENABLE_I18N + { + wchar_t wc; + wint_t start_wc; + wint_t end_wc; + wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; + + start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch + : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] + : 0)); + end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch + : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] + : 0)); + start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM) + ? __btowc (start_ch) : start_elem->opr.wch); + end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM) + ? __btowc (end_ch) : end_elem->opr.wch); + if (start_wc == WEOF || end_wc == WEOF) + return REG_ECOLLATE; + cmp_buf[0] = start_wc; + cmp_buf[4] = end_wc; + if (wcscoll (cmp_buf, cmp_buf + 4) > 0) + return REG_ERANGE; + + /* Got valid collation sequence values, add them as a new entry. + However, for !_LIBC we have no collation elements: if the + character set is single byte, the single byte character set + that we build below suffices. parse_bracket_exp passes + no MBCSET if dfa->mb_cur_max == 1. */ + if (mbcset) + { + /* Check the space of the arrays. */ + if (BE (*range_alloc == mbcset->nranges, 0)) + { + /* There is not enough space, need realloc. */ + wchar_t *new_array_start, *new_array_end; + Idx new_nranges; + + /* +1 in case of mbcset->nranges is 0. */ + new_nranges = 2 * mbcset->nranges + 1; + /* Use realloc since mbcset->range_starts and mbcset->range_ends + are NULL if *range_alloc == 0. */ + new_array_start = re_realloc (mbcset->range_starts, wchar_t, + new_nranges); + new_array_end = re_realloc (mbcset->range_ends, wchar_t, + new_nranges); + + if (BE (new_array_start == NULL || new_array_end == NULL, 0)) + return REG_ESPACE; + + mbcset->range_starts = new_array_start; + mbcset->range_ends = new_array_end; + *range_alloc = new_nranges; + } + + mbcset->range_starts[mbcset->nranges] = start_wc; + mbcset->range_ends[mbcset->nranges++] = end_wc; + } + + /* Build the table for single byte characters. */ + for (wc = 0; wc < SBC_MAX; ++wc) + { + cmp_buf[2] = wc; + if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 + && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) + bitset_set (sbcset, wc); + } + } +# else /* not RE_ENABLE_I18N */ + { + unsigned int ch; + start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch + : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] + : 0)); + end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch + : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] + : 0)); + if (start_ch > end_ch) + return REG_ERANGE; + /* Build the table for single byte characters. */ + for (ch = 0; ch < SBC_MAX; ++ch) + if (start_ch <= ch && ch <= end_ch) + bitset_set (sbcset, ch); + } +# endif /* not RE_ENABLE_I18N */ + return REG_NOERROR; +} +#endif /* not _LIBC */ + +#ifndef _LIBC +/* Helper function for parse_bracket_exp only used in case of NOT _LIBC.. + Build the collating element which is represented by NAME. + The result are written to MBCSET and SBCSET. + COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a + pointer argument since we may update it. */ + +static reg_errcode_t +internal_function +build_collating_symbol (bitset_t sbcset, +# ifdef RE_ENABLE_I18N + re_charset_t *mbcset, Idx *coll_sym_alloc, +# endif + const unsigned char *name) +{ + size_t name_len = strlen ((const char *) name); + if (BE (name_len != 1, 0)) + return REG_ECOLLATE; + else + { + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } +} +#endif /* not _LIBC */ + +/* This function parse bracket expression like "[abc]", "[a-c]", + "[[.a-a.]]" etc. */ + +static bin_tree_t * +parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, + reg_syntax_t syntax, reg_errcode_t *err) +{ +#ifdef _LIBC + const unsigned char *collseqmb; + const char *collseqwc; + uint32_t nrules; + int32_t table_size; + const int32_t *symb_table; + const unsigned char *extra; + + /* Local function for parse_bracket_exp used in _LIBC environement. + Seek the collating symbol entry correspondings to NAME. + Return the index of the symbol in the SYMB_TABLE. */ + + auto inline int32_t + __attribute ((always_inline)) + seek_collating_symbol_entry (name, name_len) + const unsigned char *name; + size_t name_len; + { + int32_t hash = elem_hash ((const char *) name, name_len); + int32_t elem = hash % table_size; + if (symb_table[2 * elem] != 0) + { + int32_t second = hash % (table_size - 2) + 1; + + do + { + /* First compare the hashing value. */ + if (symb_table[2 * elem] == hash + /* Compare the length of the name. */ + && name_len == extra[symb_table[2 * elem + 1]] + /* Compare the name. */ + && memcmp (name, &extra[symb_table[2 * elem + 1] + 1], + name_len) == 0) + { + /* Yep, this is the entry. */ + break; + } + + /* Next entry. */ + elem += second; + } + while (symb_table[2 * elem] != 0); + } + return elem; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Look up the collation sequence value of BR_ELEM. + Return the value if succeeded, UINT_MAX otherwise. */ + + auto inline unsigned int + __attribute ((always_inline)) + lookup_collation_sequence_value (br_elem) + bracket_elem_t *br_elem; + { + if (br_elem->type == SB_CHAR) + { + /* + if (MB_CUR_MAX == 1) + */ + if (nrules == 0) + return collseqmb[br_elem->opr.ch]; + else + { + wint_t wc = __btowc (br_elem->opr.ch); + return __collseq_table_lookup (collseqwc, wc); + } + } + else if (br_elem->type == MB_CHAR) + { + return __collseq_table_lookup (collseqwc, br_elem->opr.wch); + } + else if (br_elem->type == COLL_SYM) + { + size_t sym_name_len = strlen ((char *) br_elem->opr.name); + if (nrules != 0) + { + int32_t elem, idx; + elem = seek_collating_symbol_entry (br_elem->opr.name, + sym_name_len); + if (symb_table[2 * elem] != 0) + { + /* We found the entry. */ + idx = symb_table[2 * elem + 1]; + /* Skip the name of collating element name. */ + idx += 1 + extra[idx]; + /* Skip the byte sequence of the collating element. */ + idx += 1 + extra[idx]; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + /* Skip the multibyte collation sequence value. */ + idx += sizeof (unsigned int); + /* Skip the wide char sequence of the collating element. */ + idx += sizeof (unsigned int) * + (1 + *(unsigned int *) (extra + idx)); + /* Return the collation sequence value. */ + return *(unsigned int *) (extra + idx); + } + else if (symb_table[2 * elem] == 0 && sym_name_len == 1) + { + /* No valid character. Match it as a single byte + character. */ + return collseqmb[br_elem->opr.name[0]]; + } + } + else if (sym_name_len == 1) + return collseqmb[br_elem->opr.name[0]]; + } + return UINT_MAX; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Build the range expression which starts from START_ELEM, and ends + at END_ELEM. The result are written to MBCSET and SBCSET. + RANGE_ALLOC is the allocated size of mbcset->range_starts, and + mbcset->range_ends, is a pointer argument sinse we may + update it. */ + + auto inline reg_errcode_t + __attribute ((always_inline)) + build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem) + re_charset_t *mbcset; + Idx *range_alloc; + bitset_t sbcset; + bracket_elem_t *start_elem, *end_elem; + { + unsigned int ch; + uint32_t start_collseq; + uint32_t end_collseq; + + /* Equivalence Classes and Character Classes can't be a range + start/end. */ + if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS + || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, + 0)) + return REG_ERANGE; + + start_collseq = lookup_collation_sequence_value (start_elem); + end_collseq = lookup_collation_sequence_value (end_elem); + /* Check start/end collation sequence values. */ + if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0)) + return REG_ECOLLATE; + if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0)) + return REG_ERANGE; + + /* Got valid collation sequence values, add them as a new entry. + However, if we have no collation elements, and the character set + is single byte, the single byte character set that we + build below suffices. */ + if (nrules > 0 || dfa->mb_cur_max > 1) + { + /* Check the space of the arrays. */ + if (BE (*range_alloc == mbcset->nranges, 0)) + { + /* There is not enough space, need realloc. */ + uint32_t *new_array_start; + uint32_t *new_array_end; + Idx new_nranges; + + /* +1 in case of mbcset->nranges is 0. */ + new_nranges = 2 * mbcset->nranges + 1; + new_array_start = re_realloc (mbcset->range_starts, uint32_t, + new_nranges); + new_array_end = re_realloc (mbcset->range_ends, uint32_t, + new_nranges); + + if (BE (new_array_start == NULL || new_array_end == NULL, 0)) + return REG_ESPACE; + + mbcset->range_starts = new_array_start; + mbcset->range_ends = new_array_end; + *range_alloc = new_nranges; + } + + mbcset->range_starts[mbcset->nranges] = start_collseq; + mbcset->range_ends[mbcset->nranges++] = end_collseq; + } + + /* Build the table for single byte characters. */ + for (ch = 0; ch < SBC_MAX; ch++) + { + uint32_t ch_collseq; + /* + if (MB_CUR_MAX == 1) + */ + if (nrules == 0) + ch_collseq = collseqmb[ch]; + else + ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch)); + if (start_collseq <= ch_collseq && ch_collseq <= end_collseq) + bitset_set (sbcset, ch); + } + return REG_NOERROR; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Build the collating element which is represented by NAME. + The result are written to MBCSET and SBCSET. + COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a + pointer argument sinse we may update it. */ + + auto inline reg_errcode_t + __attribute ((always_inline)) + build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name) + re_charset_t *mbcset; + Idx *coll_sym_alloc; + bitset_t sbcset; + const unsigned char *name; + { + int32_t elem, idx; + size_t name_len = strlen ((const char *) name); + if (nrules != 0) + { + elem = seek_collating_symbol_entry (name, name_len); + if (symb_table[2 * elem] != 0) + { + /* We found the entry. */ + idx = symb_table[2 * elem + 1]; + /* Skip the name of collating element name. */ + idx += 1 + extra[idx]; + } + else if (symb_table[2 * elem] == 0 && name_len == 1) + { + /* No valid character, treat it as a normal + character. */ + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } + else + return REG_ECOLLATE; + + /* Got valid collation sequence, add it as a new entry. */ + /* Check the space of the arrays. */ + if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->ncoll_syms is 0. */ + Idx new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1; + /* Use realloc since mbcset->coll_syms is NULL + if *alloc == 0. */ + int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t, + new_coll_sym_alloc); + if (BE (new_coll_syms == NULL, 0)) + return REG_ESPACE; + mbcset->coll_syms = new_coll_syms; + *coll_sym_alloc = new_coll_sym_alloc; + } + mbcset->coll_syms[mbcset->ncoll_syms++] = idx; + return REG_NOERROR; + } + else + { + if (BE (name_len != 1, 0)) + return REG_ECOLLATE; + else + { + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } + } + } +#endif + + re_token_t br_token; + re_bitset_ptr_t sbcset; +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; + Idx coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0; + Idx equiv_class_alloc = 0, char_class_alloc = 0; +#endif /* not RE_ENABLE_I18N */ + bool non_match = false; + bin_tree_t *work_tree; + int token_len; + bool first_round = true; +#ifdef _LIBC + collseqmb = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); + nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules) + { + /* + if (MB_CUR_MAX > 1) + */ + collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); + table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB); + symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_TABLEMB); + extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_EXTRAMB); + } +#endif + sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); +#ifdef RE_ENABLE_I18N + mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); +#endif /* RE_ENABLE_I18N */ +#ifdef RE_ENABLE_I18N + if (BE (sbcset == NULL || mbcset == NULL, 0)) +#else + if (BE (sbcset == NULL, 0)) +#endif /* RE_ENABLE_I18N */ + { + *err = REG_ESPACE; + return NULL; + } + + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_NON_MATCH_LIST) + { +#ifdef RE_ENABLE_I18N + mbcset->non_match = 1; +#endif /* not RE_ENABLE_I18N */ + non_match = true; + if (syntax & RE_HAT_LISTS_NOT_NEWLINE) + bitset_set (sbcset, '\0'); + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + } + + /* We treat the first ']' as a normal character. */ + if (token->type == OP_CLOSE_BRACKET) + token->type = CHARACTER; + + while (1) + { + bracket_elem_t start_elem, end_elem; + unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE]; + unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE]; + reg_errcode_t ret; + int token_len2 = 0; + bool is_range_exp = false; + re_token_t token2; + + start_elem.opr.name = start_name_buf; + ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa, + syntax, first_round); + if (BE (ret != REG_NOERROR, 0)) + { + *err = ret; + goto parse_bracket_exp_free_return; + } + first_round = false; + + /* Get information about the next token. We need it in any case. */ + token_len = peek_token_bracket (token, regexp, syntax); + + /* Do not check for ranges if we know they are not allowed. */ + if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS) + { + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_CHARSET_RANGE) + { + re_string_skip_bytes (regexp, token_len); /* Skip '-'. */ + token_len2 = peek_token_bracket (&token2, regexp, syntax); + if (BE (token2.type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token2.type == OP_CLOSE_BRACKET) + { + /* We treat the last '-' as a normal character. */ + re_string_skip_bytes (regexp, -token_len); + token->type = CHARACTER; + } + else + is_range_exp = true; + } + } + + if (is_range_exp == true) + { + end_elem.opr.name = end_name_buf; + ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2, + dfa, syntax, true); + if (BE (ret != REG_NOERROR, 0)) + { + *err = ret; + goto parse_bracket_exp_free_return; + } + + token_len = peek_token_bracket (token, regexp, syntax); + +#ifdef _LIBC + *err = build_range_exp (sbcset, mbcset, &range_alloc, + &start_elem, &end_elem); +#else +# ifdef RE_ENABLE_I18N + *err = build_range_exp (sbcset, + dfa->mb_cur_max > 1 ? mbcset : NULL, + &range_alloc, &start_elem, &end_elem); +# else + *err = build_range_exp (sbcset, &start_elem, &end_elem); +# endif +#endif /* RE_ENABLE_I18N */ + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + } + else + { + switch (start_elem.type) + { + case SB_CHAR: + bitset_set (sbcset, start_elem.opr.ch); + break; +#ifdef RE_ENABLE_I18N + case MB_CHAR: + /* Check whether the array has enough space. */ + if (BE (mbchar_alloc == mbcset->nmbchars, 0)) + { + wchar_t *new_mbchars; + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nmbchars is 0. */ + mbchar_alloc = 2 * mbcset->nmbchars + 1; + /* Use realloc since array is NULL if *alloc == 0. */ + new_mbchars = re_realloc (mbcset->mbchars, wchar_t, + mbchar_alloc); + if (BE (new_mbchars == NULL, 0)) + goto parse_bracket_exp_espace; + mbcset->mbchars = new_mbchars; + } + mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch; + break; +#endif /* RE_ENABLE_I18N */ + case EQUIV_CLASS: + *err = build_equiv_class (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &equiv_class_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + case COLL_SYM: + *err = build_collating_symbol (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &coll_sym_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + case CHAR_CLASS: + *err = build_charclass (regexp->trans, sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &char_class_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name, syntax); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + default: + assert (0); + break; + } + } + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_CLOSE_BRACKET) + break; + } + + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + + /* If it is non-matching list. */ + if (non_match) + bitset_not (sbcset); + +#ifdef RE_ENABLE_I18N + /* Ensure only single byte characters are set. */ + if (dfa->mb_cur_max > 1) + bitset_mask (sbcset, dfa->sb_char); + + if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes + || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes + || mbcset->non_match))) + { + bin_tree_t *mbc_tree; + int sbc_idx; + /* Build a tree for complex bracket. */ + dfa->has_mb_node = 1; + br_token.type = COMPLEX_BRACKET; + br_token.opr.mbcset = mbcset; + mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (mbc_tree == NULL, 0)) + goto parse_bracket_exp_espace; + for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx) + if (sbcset[sbc_idx]) + break; + /* If there are no bits set in sbcset, there is no point + of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */ + if (sbc_idx < BITSET_WORDS) + { + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + work_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + + /* Then join them by ALT node. */ + work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + } + else + { + re_free (sbcset); + work_tree = mbc_tree; + } + } + else +#endif /* not RE_ENABLE_I18N */ + { +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + work_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + } + return work_tree; + + parse_bracket_exp_espace: + *err = REG_ESPACE; + parse_bracket_exp_free_return: + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + return NULL; +} + +/* Parse an element in the bracket expression. */ + +static reg_errcode_t +parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp, + re_token_t *token, int token_len, re_dfa_t *dfa, + reg_syntax_t syntax, bool accept_hyphen) +{ +#ifdef RE_ENABLE_I18N + int cur_char_size; + cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp)); + if (cur_char_size > 1) + { + elem->type = MB_CHAR; + elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp)); + re_string_skip_bytes (regexp, cur_char_size); + return REG_NOERROR; + } +#endif /* RE_ENABLE_I18N */ + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS + || token->type == OP_OPEN_EQUIV_CLASS) + return parse_bracket_symbol (elem, regexp, token); + if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen) + { + /* A '-' must only appear as anything but a range indicator before + the closing bracket. Everything else is an error. */ + re_token_t token2; + (void) peek_token_bracket (&token2, regexp, syntax); + if (token2.type != OP_CLOSE_BRACKET) + /* The actual error value is not standardized since this whole + case is undefined. But ERANGE makes good sense. */ + return REG_ERANGE; + } + elem->type = SB_CHAR; + elem->opr.ch = token->opr.c; + return REG_NOERROR; +} + +/* Parse a bracket symbol in the bracket expression. Bracket symbols are + such as [::], [..], and + [==]. */ + +static reg_errcode_t +parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp, + re_token_t *token) +{ + unsigned char ch, delim = token->opr.c; + int i = 0; + if (re_string_eoi(regexp)) + return REG_EBRACK; + for (;; ++i) + { + if (i >= BRACKET_NAME_BUF_SIZE) + return REG_EBRACK; + if (token->type == OP_OPEN_CHAR_CLASS) + ch = re_string_fetch_byte_case (regexp); + else + ch = re_string_fetch_byte (regexp); + if (re_string_eoi(regexp)) + return REG_EBRACK; + if (ch == delim && re_string_peek_byte (regexp, 0) == ']') + break; + elem->opr.name[i] = ch; + } + re_string_skip_bytes (regexp, 1); + elem->opr.name[i] = '\0'; + switch (token->type) + { + case OP_OPEN_COLL_ELEM: + elem->type = COLL_SYM; + break; + case OP_OPEN_EQUIV_CLASS: + elem->type = EQUIV_CLASS; + break; + case OP_OPEN_CHAR_CLASS: + elem->type = CHAR_CLASS; + break; + default: + break; + } + return REG_NOERROR; +} + + /* Helper function for parse_bracket_exp. + Build the equivalence class which is represented by NAME. + The result are written to MBCSET and SBCSET. + EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes, + is a pointer argument sinse we may update it. */ + +static reg_errcode_t +#ifdef RE_ENABLE_I18N +build_equiv_class (bitset_t sbcset, re_charset_t *mbcset, + Idx *equiv_class_alloc, const unsigned char *name) +#else /* not RE_ENABLE_I18N */ +build_equiv_class (bitset_t sbcset, const unsigned char *name) +#endif /* not RE_ENABLE_I18N */ +{ +#ifdef _LIBC + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules != 0) + { + const int32_t *table, *indirect; + const unsigned char *weights, *extra, *cp; + unsigned char char_buf[2]; + int32_t idx1, idx2; + unsigned int ch; + size_t len; + /* This #include defines a local function! */ +# include + /* Calculate the index for equivalence class. */ + cp = name; + table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_INDIRECTMB); + idx1 = findidx (&cp); + if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0)) + /* This isn't a valid character. */ + return REG_ECOLLATE; + + /* Build single byte matcing table for this equivalence class. */ + char_buf[1] = (unsigned char) '\0'; + len = weights[idx1]; + for (ch = 0; ch < SBC_MAX; ++ch) + { + char_buf[0] = ch; + cp = char_buf; + idx2 = findidx (&cp); +/* + idx2 = table[ch]; +*/ + if (idx2 == 0) + /* This isn't a valid character. */ + continue; + if (len == weights[idx2]) + { + int cnt = 0; + while (cnt <= len && + weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt]) + ++cnt; + + if (cnt > len) + bitset_set (sbcset, ch); + } + } + /* Check whether the array has enough space. */ + if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nequiv_classes is 0. */ + Idx new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1; + /* Use realloc since the array is NULL if *alloc == 0. */ + int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes, + int32_t, + new_equiv_class_alloc); + if (BE (new_equiv_classes == NULL, 0)) + return REG_ESPACE; + mbcset->equiv_classes = new_equiv_classes; + *equiv_class_alloc = new_equiv_class_alloc; + } + mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1; + } + else +#endif /* _LIBC */ + { + if (BE (strlen ((const char *) name) != 1, 0)) + return REG_ECOLLATE; + bitset_set (sbcset, *name); + } + return REG_NOERROR; +} + + /* Helper function for parse_bracket_exp. + Build the character class which is represented by NAME. + The result are written to MBCSET and SBCSET. + CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes, + is a pointer argument sinse we may update it. */ + +static reg_errcode_t +#ifdef RE_ENABLE_I18N +build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, + re_charset_t *mbcset, Idx *char_class_alloc, + const unsigned char *class_name, reg_syntax_t syntax) +#else /* not RE_ENABLE_I18N */ +build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, + const unsigned char *class_name, reg_syntax_t syntax) +#endif /* not RE_ENABLE_I18N */ +{ + int i; + const char *name = (const char *) class_name; + + /* In case of REG_ICASE "upper" and "lower" match the both of + upper and lower cases. */ + if ((syntax & RE_ICASE) + && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0)) + name = "alpha"; + +#ifdef RE_ENABLE_I18N + /* Check the space of the arrays. */ + if (BE (*char_class_alloc == mbcset->nchar_classes, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nchar_classes is 0. */ + Idx new_char_class_alloc = 2 * mbcset->nchar_classes + 1; + /* Use realloc since array is NULL if *alloc == 0. */ + wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t, + new_char_class_alloc); + if (BE (new_char_classes == NULL, 0)) + return REG_ESPACE; + mbcset->char_classes = new_char_classes; + *char_class_alloc = new_char_class_alloc; + } + mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name); +#endif /* RE_ENABLE_I18N */ + +#define BUILD_CHARCLASS_LOOP(ctype_func) \ + do { \ + if (BE (trans != NULL, 0)) \ + { \ + for (i = 0; i < SBC_MAX; ++i) \ + if (ctype_func (i)) \ + bitset_set (sbcset, trans[i]); \ + } \ + else \ + { \ + for (i = 0; i < SBC_MAX; ++i) \ + if (ctype_func (i)) \ + bitset_set (sbcset, i); \ + } \ + } while (0) + + if (strcmp (name, "alnum") == 0) + BUILD_CHARCLASS_LOOP (isalnum); + else if (strcmp (name, "cntrl") == 0) + BUILD_CHARCLASS_LOOP (iscntrl); + else if (strcmp (name, "lower") == 0) + BUILD_CHARCLASS_LOOP (islower); + else if (strcmp (name, "space") == 0) + BUILD_CHARCLASS_LOOP (isspace); + else if (strcmp (name, "alpha") == 0) + BUILD_CHARCLASS_LOOP (isalpha); + else if (strcmp (name, "digit") == 0) + BUILD_CHARCLASS_LOOP (isdigit); + else if (strcmp (name, "print") == 0) + BUILD_CHARCLASS_LOOP (isprint); + else if (strcmp (name, "upper") == 0) + BUILD_CHARCLASS_LOOP (isupper); + else if (strcmp (name, "blank") == 0) + BUILD_CHARCLASS_LOOP (isblank); + else if (strcmp (name, "graph") == 0) + BUILD_CHARCLASS_LOOP (isgraph); + else if (strcmp (name, "punct") == 0) + BUILD_CHARCLASS_LOOP (ispunct); + else if (strcmp (name, "xdigit") == 0) + BUILD_CHARCLASS_LOOP (isxdigit); + else + return REG_ECTYPE; + + return REG_NOERROR; +} + +static bin_tree_t * +build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans, + const unsigned char *class_name, + const unsigned char *extra, bool non_match, + reg_errcode_t *err) +{ + re_bitset_ptr_t sbcset; +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; + Idx alloc = 0; +#endif /* not RE_ENABLE_I18N */ + reg_errcode_t ret; + re_token_t br_token; + bin_tree_t *tree; + + sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); +#ifdef RE_ENABLE_I18N + mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); +#endif /* RE_ENABLE_I18N */ + +#ifdef RE_ENABLE_I18N + if (BE (sbcset == NULL || mbcset == NULL, 0)) +#else /* not RE_ENABLE_I18N */ + if (BE (sbcset == NULL, 0)) +#endif /* not RE_ENABLE_I18N */ + { + *err = REG_ESPACE; + return NULL; + } + + if (non_match) + { +#ifdef RE_ENABLE_I18N + /* + if (syntax & RE_HAT_LISTS_NOT_NEWLINE) + bitset_set(cset->sbcset, '\0'); + */ + mbcset->non_match = 1; +#endif /* not RE_ENABLE_I18N */ + } + + /* We don't care the syntax in this case. */ + ret = build_charclass (trans, sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &alloc, +#endif /* RE_ENABLE_I18N */ + class_name, 0); + + if (BE (ret != REG_NOERROR, 0)) + { + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + *err = ret; + return NULL; + } + /* \w match '_' also. */ + for (; *extra; extra++) + bitset_set (sbcset, *extra); + + /* If it is non-matching list. */ + if (non_match) + bitset_not (sbcset); + +#ifdef RE_ENABLE_I18N + /* Ensure only single byte characters are set. */ + if (dfa->mb_cur_max > 1) + bitset_mask (sbcset, dfa->sb_char); +#endif + + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (tree == NULL, 0)) + goto build_word_op_espace; + +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + bin_tree_t *mbc_tree; + /* Build a tree for complex bracket. */ + br_token.type = COMPLEX_BRACKET; + br_token.opr.mbcset = mbcset; + dfa->has_mb_node = 1; + mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (mbc_tree == NULL, 0)) + goto build_word_op_espace; + /* Then join them by ALT node. */ + tree = create_tree (dfa, tree, mbc_tree, OP_ALT); + if (BE (mbc_tree != NULL, 1)) + return tree; + } + else + { + free_charset (mbcset); + return tree; + } +#else /* not RE_ENABLE_I18N */ + return tree; +#endif /* not RE_ENABLE_I18N */ + + build_word_op_espace: + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + *err = REG_ESPACE; + return NULL; +} + +/* This is intended for the expressions like "a{1,3}". + Fetch a number from `input', and return the number. + Return REG_MISSING if the number field is empty like "{,1}". + Return REG_ERROR if an error occurred. */ + +static Idx +fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax) +{ + Idx num = REG_MISSING; + unsigned char c; + while (1) + { + fetch_token (token, input, syntax); + c = token->opr.c; + if (BE (token->type == END_OF_RE, 0)) + return REG_ERROR; + if (token->type == OP_CLOSE_DUP_NUM || c == ',') + break; + num = ((token->type != CHARACTER || c < '0' || '9' < c + || num == REG_ERROR) + ? REG_ERROR + : ((num == REG_MISSING) ? c - '0' : num * 10 + c - '0')); + num = (num > RE_DUP_MAX) ? REG_ERROR : num; + } + return num; +} + +#ifdef RE_ENABLE_I18N +static void +free_charset (re_charset_t *cset) +{ + re_free (cset->mbchars); +# ifdef _LIBC + re_free (cset->coll_syms); + re_free (cset->equiv_classes); + re_free (cset->range_starts); + re_free (cset->range_ends); +# endif + re_free (cset->char_classes); + re_free (cset); +} +#endif /* RE_ENABLE_I18N */ + +/* Functions for binary tree operation. */ + +/* Create a tree node. */ + +static bin_tree_t * +create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, + re_token_type_t type) +{ + re_token_t t; + t.type = type; + return create_token_tree (dfa, left, right, &t); +} + +static bin_tree_t * +create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, + const re_token_t *token) +{ + bin_tree_t *tree; + if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0)) + { + bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1); + + if (storage == NULL) + return NULL; + storage->next = dfa->str_tree_storage; + dfa->str_tree_storage = storage; + dfa->str_tree_storage_idx = 0; + } + tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++]; + + tree->parent = NULL; + tree->left = left; + tree->right = right; + tree->token = *token; + tree->token.duplicated = 0; + tree->token.opt_subexp = 0; + tree->first = NULL; + tree->next = NULL; + tree->node_idx = REG_MISSING; + + if (left != NULL) + left->parent = tree; + if (right != NULL) + right->parent = tree; + return tree; +} + +/* Mark the tree SRC as an optional subexpression. + To be called from preorder or postorder. */ + +static reg_errcode_t +mark_opt_subexp (void *extra, bin_tree_t *node) +{ + Idx idx = (Idx) (long) extra; + if (node->token.type == SUBEXP && node->token.opr.idx == idx) + node->token.opt_subexp = 1; + + return REG_NOERROR; +} + +/* Free the allocated memory inside NODE. */ + +static void +free_token (re_token_t *node) +{ +#ifdef RE_ENABLE_I18N + if (node->type == COMPLEX_BRACKET && node->duplicated == 0) + free_charset (node->opr.mbcset); + else +#endif /* RE_ENABLE_I18N */ + if (node->type == SIMPLE_BRACKET && node->duplicated == 0) + re_free (node->opr.sbcset); +} + +/* Worker function for tree walking. Free the allocated memory inside NODE + and its children. */ + +static reg_errcode_t +free_tree (void *extra, bin_tree_t *node) +{ + free_token (&node->token); + return REG_NOERROR; +} + + +/* Duplicate the node SRC, and return new node. This is a preorder + visit similar to the one implemented by the generic visitor, but + we need more infrastructure to maintain two parallel trees --- so, + it's easier to duplicate. */ + +static bin_tree_t * +duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa) +{ + const bin_tree_t *node; + bin_tree_t *dup_root; + bin_tree_t **p_new = &dup_root, *dup_node = root->parent; + + for (node = root; ; ) + { + /* Create a new tree and link it back to the current parent. */ + *p_new = create_token_tree (dfa, NULL, NULL, &node->token); + if (*p_new == NULL) + return NULL; + (*p_new)->parent = dup_node; + (*p_new)->token.duplicated = 1; + dup_node = *p_new; + + /* Go to the left node, or up and to the right. */ + if (node->left) + { + node = node->left; + p_new = &dup_node->left; + } + else + { + const bin_tree_t *prev = NULL; + while (node->right == prev || node->right == NULL) + { + prev = node; + node = node->parent; + dup_node = dup_node->parent; + if (!node) + return dup_root; + } + node = node->right; + p_new = &dup_node->right; + } + } +} diff --git a/src/bin/m4/lib/regex.c b/src/bin/m4/lib/regex.c new file mode 100644 index 0000000000..d4eb726b29 --- /dev/null +++ b/src/bin/m4/lib/regex.c @@ -0,0 +1,71 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Make sure noone compiles this code with a C++ compiler. */ +#if defined __cplusplus && defined _LIBC +# error "This is C code, use a C compiler" +#endif + +#ifdef _LIBC +/* We have to keep the namespace clean. */ +# define regfree(preg) __regfree (preg) +# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef) +# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags) +# define regerror(errcode, preg, errbuf, errbuf_size) \ + __regerror(errcode, preg, errbuf, errbuf_size) +# define re_set_registers(bu, re, nu, st, en) \ + __re_set_registers (bu, re, nu, st, en) +# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \ + __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) +# define re_match(bufp, string, size, pos, regs) \ + __re_match (bufp, string, size, pos, regs) +# define re_search(bufp, string, size, startpos, range, regs) \ + __re_search (bufp, string, size, startpos, range, regs) +# define re_compile_pattern(pattern, length, bufp) \ + __re_compile_pattern (pattern, length, bufp) +# define re_set_syntax(syntax) __re_set_syntax (syntax) +# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \ + __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop) +# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp) + +# include "../locale/localeinfo.h" +#endif + +/* On some systems, limits.h sets RE_DUP_MAX to a lower value than + GNU regex allows. Include it before , which correctly + #undefs RE_DUP_MAX and sets it to the right value. */ +#include + +#include +#include "regex_internal.h" + +#include "regex_internal.c" +#include "regcomp.c" +#include "regexec.c" + +/* Binary backward compatibility. */ +#if _LIBC +# include +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3) +link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.") +int re_max_failures = 2000; +# endif +#endif diff --git a/src/bin/m4/lib/regex.h b/src/bin/m4/lib/regex.h new file mode 100644 index 0000000000..e8748e195b --- /dev/null +++ b/src/bin/m4/lib/regex.h @@ -0,0 +1,668 @@ +/* Definitions for data structures and routines for the regular + expression library. + Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _REGEX_H +#define _REGEX_H 1 + +#include + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Define __USE_GNU_REGEX to declare GNU extensions that violate the + POSIX name space rules. */ +#undef __USE_GNU_REGEX +#if (defined _GNU_SOURCE \ + || (!defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE \ + && !defined _XOPEN_SOURCE)) +# define __USE_GNU_REGEX 1 +#endif + +#ifdef _REGEX_LARGE_OFFSETS + +/* Use types and values that are wide enough to represent signed and + unsigned byte offsets in memory. This currently works only when + the regex code is used outside of the GNU C library; it is not yet + supported within glibc itself, and glibc users should not define + _REGEX_LARGE_OFFSETS. */ + +/* The type of the offset of a byte within a string. + For historical reasons POSIX 1003.1-2004 requires that regoff_t be + at least as wide as off_t. However, many common POSIX platforms set + regoff_t to the more-sensible ssize_t and the Open Group has + signalled its intention to change the requirement to be that + regoff_t be at least as wide as ptrdiff_t and ssize_t; see XBD ERN + 60 (2005-08-25). We don't know of any hosts where ssize_t or + ptrdiff_t is wider than ssize_t, so ssize_t is safe. */ +typedef ssize_t regoff_t; + +/* The type of nonnegative object indexes. Traditionally, GNU regex + uses 'int' for these. Code that uses __re_idx_t should work + regardless of whether the type is signed. */ +typedef size_t __re_idx_t; + +/* The type of object sizes. */ +typedef size_t __re_size_t; + +/* The type of object sizes, in places where the traditional code + uses unsigned long int. */ +typedef size_t __re_long_size_t; + +#else + +/* Use types that are binary-compatible with the traditional GNU regex + implementation, which mishandles strings longer than INT_MAX. */ + +typedef int regoff_t; +typedef int __re_idx_t; +typedef unsigned int __re_size_t; +typedef unsigned long int __re_long_size_t; + +#endif + +/* The following two types have to be signed and unsigned integer type + wide enough to hold a value of a pointer. For most ANSI compilers + ptrdiff_t and size_t should be likely OK. Still size of these two + types is 2 for Microsoft C. Ugh... */ +typedef long int s_reg_t; +typedef unsigned long int active_reg_t; + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned long int reg_syntax_t; + +#ifdef __USE_GNU_REGEX + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +# define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +# define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +# define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +# define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +# define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +# define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +# define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +# define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +# define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +# define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +# define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +# define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +# define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +# define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \ matches . + If not set, then \ is a back-reference. */ +# define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +# define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +# define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +# define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* If this bit is set, succeed as soon as we match the whole pattern, + without further backtracking. */ +# define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) + +/* If this bit is set, do not process the GNU regex operators. + If not set, then the GNU regex operators are recognized. */ +# define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1) + +/* If this bit is set, turn on internal regex debugging. + If not set, and debugging was on, turn it off. + This only works if regex.c is compiled -DDEBUG. + We define this bit always, so that all that's needed to turn on + debugging is to recompile regex.c; the calling code can always have + this bit set, and it won't affect anything in the normal case. */ +# define RE_DEBUG (RE_NO_GNU_OPS << 1) + +/* If this bit is set, a syntactically invalid interval is treated as + a string of ordinary characters. For example, the ERE 'a{1' is + treated as 'a\{1'. */ +# define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1) + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +# define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1) + +/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only + for ^, because it is difficult to scan the regex backwards to find + whether ^ should be special. */ +# define RE_CARET_ANCHORS_HERE (RE_ICASE << 1) + +/* If this bit is set, then \{ cannot be first in an bre or + immediately after an alternation or begin-group operator. */ +# define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1) + +/* If this bit is set, then no_sub will be set to 1 during + re_compile_pattern. */ +# define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1) + +#endif /* defined __USE_GNU_REGEX */ + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + stored in the pattern buffer, so changing this does not affect + already-compiled regexps. */ +extern reg_syntax_t re_syntax_options; + +#ifdef __USE_GNU_REGEX +/* Define combinations of the above bits for the standard possibilities. + (The [[[ comments delimit what gets put into the Texinfo file, so + don't delete them!) */ +/* [[[begin syntaxes]]] */ +# define RE_SYNTAX_EMACS 0 + +# define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \ + | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) + +# define RE_SYNTAX_GNU_AWK \ + ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \ + & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS \ + | RE_CONTEXT_INVALID_OPS )) + +# define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ + | RE_INTERVALS | RE_NO_GNU_OPS) + +# define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +# define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +# define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \ + | RE_INVALID_INTERVAL_ORD) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +# define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +# define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +# define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +# define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +# define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +# define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is + removed and RE_NO_BK_REFS is added. */ +# define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +/* [[[end syntaxes]]] */ + +#endif /* defined __USE_GNU_REGEX */ + +#ifdef __USE_GNU_REGEX + +/* Maximum number of duplicates an interval can allow. POSIX-conforming + systems might define this in , but we want our + value, so remove any previous define. */ +# ifdef RE_DUP_MAX +# undef RE_DUP_MAX +# endif + +/* RE_DUP_MAX is 2**15 - 1 because an earlier implementation stored + the counter as a 2-byte signed integer. This is no longer true, so + RE_DUP_MAX could be increased to (INT_MAX / 10 - 1), or to + ((SIZE_MAX - 2) / 10 - 1) if _REGEX_LARGE_OFFSETS is defined. + However, there would be a huge performance problem if someone + actually used a pattern like a\{214748363\}, so RE_DUP_MAX retains + its historical value. */ +# define RE_DUP_MAX (0x7fff) + +#endif /* defined __USE_GNU_REGEX */ + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (1 << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (1 << 2) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (1 << 3) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + +/* Use PMATCH[0] to delimit the start and end of the search in the + buffer. */ +#define REG_STARTEND (1 << 2) + + +/* If any error codes are removed, changed, or added, update the + `__re_error_msgid' table in regcomp.c. */ + +typedef enum +{ + _REG_ENOSYS = -1, /* This will never happen for this implementation. */ + _REG_NOERROR = 0, /* Success. */ + _REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + _REG_BADPAT, /* Invalid pattern. */ + _REG_ECOLLATE, /* Invalid collating element. */ + _REG_ECTYPE, /* Invalid character class name. */ + _REG_EESCAPE, /* Trailing backslash. */ + _REG_ESUBREG, /* Invalid back reference. */ + _REG_EBRACK, /* Unmatched left bracket. */ + _REG_EPAREN, /* Parenthesis imbalance. */ + _REG_EBRACE, /* Unmatched \{. */ + _REG_BADBR, /* Invalid contents of \{\}. */ + _REG_ERANGE, /* Invalid range end. */ + _REG_ESPACE, /* Ran out of memory. */ + _REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + _REG_EEND, /* Premature end. */ + _REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ + _REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ +} reg_errcode_t; + +#ifdef _XOPEN_SOURCE +# define REG_ENOSYS _REG_ENOSYS +#endif +#define REG_NOERROR _REG_NOERROR +#define REG_NOMATCH _REG_NOMATCH +#define REG_BADPAT _REG_BADPAT +#define REG_ECOLLATE _REG_ECOLLATE +#define REG_ECTYPE _REG_ECTYPE +#define REG_EESCAPE _REG_EESCAPE +#define REG_ESUBREG _REG_ESUBREG +#define REG_EBRACK _REG_EBRACK +#define REG_EPAREN _REG_EPAREN +#define REG_EBRACE _REG_EBRACE +#define REG_BADBR _REG_BADBR +#define REG_ERANGE _REG_ERANGE +#define REG_ESPACE _REG_ESPACE +#define REG_BADRPT _REG_BADRPT +#define REG_EEND _REG_EEND +#define REG_ESIZE _REG_ESIZE +#define REG_ERPAREN _REG_ERPAREN + +/* struct re_pattern_buffer normally uses member names like `buffer' + that POSIX does not allow. In POSIX mode these members have names + with leading `re_' (e.g., `re_buffer'). */ +#ifdef __USE_GNU_REGEX +# define _REG_RE_NAME(id) id +# define _REG_RM_NAME(id) id +#else +# define _REG_RE_NAME(id) re_##id +# define _REG_RM_NAME(id) rm_##id +#endif + +/* The user can specify the type of the re_translate member by + defining the macro RE_TRANSLATE_TYPE, which defaults to unsigned + char *. This pollutes the POSIX name space, so in POSIX mode just + use unsigned char *. */ +#ifdef __USE_GNU_REGEX +# ifndef RE_TRANSLATE_TYPE +# define RE_TRANSLATE_TYPE unsigned char * +# endif +# define REG_TRANSLATE_TYPE RE_TRANSLATE_TYPE +#else +# define REG_TRANSLATE_TYPE unsigned char * +#endif + +/* This data structure represents a compiled pattern. Before calling + the pattern compiler, the fields `buffer', `allocated', `fastmap', + `translate', and `no_sub' can be set. After the pattern has been + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ + +struct re_pattern_buffer +{ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are sometimes used as + array indexes. */ + unsigned char *_REG_RE_NAME (buffer); + + /* Number of bytes to which `buffer' points. */ + __re_long_size_t _REG_RE_NAME (allocated); + + /* Number of bytes actually used in `buffer'. */ + __re_long_size_t _REG_RE_NAME (used); + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t _REG_RE_NAME (syntax); + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses the + fastmap, if there is one, to skip over impossible starting points + for matches. */ + char *_REG_RE_NAME (fastmap); + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation is + applied to a pattern when it is compiled and to a string when it + is matched. */ + REG_TRANSLATE_TYPE _REG_RE_NAME (translate); + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see whether or + not we should use the fastmap, so we don't set this absolutely + perfectly; see `re_compile_fastmap' (the `duplicate' case). */ + unsigned int _REG_RE_NAME (can_be_null) : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#ifdef __USE_GNU_REGEX +# define REGS_UNALLOCATED 0 +# define REGS_REALLOCATE 1 +# define REGS_FIXED 2 +#endif + unsigned int _REG_RE_NAME (regs_allocated) : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned int _REG_RE_NAME (fastmap_accurate) : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned int _REG_RE_NAME (no_sub) : 1; + + /* If set, a beginning-of-line anchor doesn't match at the beginning + of the string. */ + unsigned int _REG_RE_NAME (not_bol) : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned int _REG_RE_NAME (not_eol) : 1; + + /* If true, an anchor at a newline matches. */ + unsigned int _REG_RE_NAME (newline_anchor) : 1; + +/* [[[end pattern_buffer]]] */ +}; + +typedef struct re_pattern_buffer regex_t; + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + __re_size_t _REG_RM_NAME (num_regs); + regoff_t *_REG_RM_NAME (start); + regoff_t *_REG_RM_NAME (end); +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#if !defined RE_NREGS && defined __USE_GNU_REGEX +# define RE_NREGS 30 +#endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +typedef struct +{ + regoff_t rm_so; /* Byte offset from string's start to substring's start. */ + regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ +} regmatch_t; + +/* Declarations for routines. */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern (const char *__pattern, size_t __length, + struct re_pattern_buffer *__buffer); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap (struct re_pattern_buffer *__buffer); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern regoff_t re_search (struct re_pattern_buffer *__buffer, + const char *__string, __re_idx_t __length, + __re_idx_t __start, regoff_t __range, + struct re_registers *__regs); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern regoff_t re_search_2 (struct re_pattern_buffer *__buffer, + const char *__string1, __re_idx_t __length1, + const char *__string2, __re_idx_t __length2, + __re_idx_t __start, regoff_t __range, + struct re_registers *__regs, + __re_idx_t __stop); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern regoff_t re_match (struct re_pattern_buffer *__buffer, + const char *__string, __re_idx_t __length, + __re_idx_t __start, struct re_registers *__regs); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern regoff_t re_match_2 (struct re_pattern_buffer *__buffer, + const char *__string1, __re_idx_t __length1, + const char *__string2, __re_idx_t __length2, + __re_idx_t __start, struct re_registers *__regs, + __re_idx_t __stop); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers (struct re_pattern_buffer *__buffer, + struct re_registers *__regs, + __re_size_t __num_regs, + regoff_t *__starts, regoff_t *__ends); + +#if defined _REGEX_RE_COMP || defined _LIBC +# ifndef _CRAY +/* 4.2 bsd compatibility. */ +extern char *re_comp (const char *); +extern int re_exec (const char *); +# endif +#endif + +/* GCC 2.95 and later have "__restrict"; C99 compilers have + "restrict", and "configure" may have defined "restrict". */ +#ifndef __restrict +# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__)) +# if defined restrict || 199901L <= __STDC_VERSION__ +# define __restrict restrict +# else +# define __restrict +# endif +# endif +#endif +/* gcc 3.1 and up support the [restrict] syntax. */ +#ifndef __restrict_arr +# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) \ + && !defined __GNUG__ +# define __restrict_arr __restrict +# else +# define __restrict_arr +# endif +#endif + +/* POSIX compatibility. */ +extern int regcomp (regex_t *__restrict __preg, + const char *__restrict __pattern, + int __cflags); + +extern int regexec (const regex_t *__restrict __preg, + const char *__restrict __string, size_t __nmatch, + regmatch_t __pmatch[__restrict_arr], + int __eflags); + +extern size_t regerror (int __errcode, const regex_t *__restrict __preg, + char *__restrict __errbuf, size_t __errbuf_size); + +extern void regfree (regex_t *__preg); + + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* regex.h */ diff --git a/src/bin/m4/lib/regex_internal.c b/src/bin/m4/lib/regex_internal.c new file mode 100644 index 0000000000..78e16f3301 --- /dev/null +++ b/src/bin/m4/lib/regex_internal.c @@ -0,0 +1,1742 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +static void re_string_construct_common (const char *str, Idx len, + re_string_t *pstr, + RE_TRANSLATE_TYPE trans, bool icase, + const re_dfa_t *dfa) internal_function; +static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa, + const re_node_set *nodes, + re_hashval_t hash) internal_function; +static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int context, + re_hashval_t hash) internal_function; + +/* Functions for string operation. */ + +/* This function allocate the buffers. It is necessary to call + re_string_reconstruct before using the object. */ + +static reg_errcode_t +internal_function +re_string_allocate (re_string_t *pstr, const char *str, Idx len, Idx init_len, + RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa) +{ + reg_errcode_t ret; + Idx init_buf_len; + + /* Ensure at least one character fits into the buffers. */ + if (init_len < dfa->mb_cur_max) + init_len = dfa->mb_cur_max; + init_buf_len = (len + 1 < init_len) ? len + 1: init_len; + re_string_construct_common (str, len, pstr, trans, icase, dfa); + + ret = re_string_realloc_buffers (pstr, init_buf_len); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + pstr->word_char = dfa->word_char; + pstr->word_ops_used = dfa->word_ops_used; + pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; + pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len; + pstr->valid_raw_len = pstr->valid_len; + return REG_NOERROR; +} + +/* This function allocate the buffers, and initialize them. */ + +static reg_errcode_t +internal_function +re_string_construct (re_string_t *pstr, const char *str, Idx len, + RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa) +{ + reg_errcode_t ret; + memset (pstr, '\0', sizeof (re_string_t)); + re_string_construct_common (str, len, pstr, trans, icase, dfa); + + if (len > 0) + { + ret = re_string_realloc_buffers (pstr, len + 1); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; + + if (icase) + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + while (1) + { + ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + if (pstr->valid_raw_len >= len) + break; + if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max) + break; + ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + } + else +#endif /* RE_ENABLE_I18N */ + build_upper_buffer (pstr); + } + else + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + build_wcs_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + { + if (trans != NULL) + re_string_translate_buffer (pstr); + else + { + pstr->valid_len = pstr->bufs_len; + pstr->valid_raw_len = pstr->bufs_len; + } + } + } + + return REG_NOERROR; +} + +/* Helper functions for re_string_allocate, and re_string_construct. */ + +static reg_errcode_t +internal_function +re_string_realloc_buffers (re_string_t *pstr, Idx new_buf_len) +{ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + wint_t *new_wcs; + + /* Avoid overflow. */ + size_t max_object_size = MAX (sizeof (wint_t), sizeof (Idx)); + if (BE (SIZE_MAX / max_object_size < new_buf_len, 0)) + return REG_ESPACE; + + new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len); + if (BE (new_wcs == NULL, 0)) + return REG_ESPACE; + pstr->wcs = new_wcs; + if (pstr->offsets != NULL) + { + Idx *new_offsets = re_realloc (pstr->offsets, Idx, new_buf_len); + if (BE (new_offsets == NULL, 0)) + return REG_ESPACE; + pstr->offsets = new_offsets; + } + } +#endif /* RE_ENABLE_I18N */ + if (pstr->mbs_allocated) + { + unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char, + new_buf_len); + if (BE (new_mbs == NULL, 0)) + return REG_ESPACE; + pstr->mbs = new_mbs; + } + pstr->bufs_len = new_buf_len; + return REG_NOERROR; +} + + +static void +internal_function +re_string_construct_common (const char *str, Idx len, re_string_t *pstr, + RE_TRANSLATE_TYPE trans, bool icase, + const re_dfa_t *dfa) +{ + pstr->raw_mbs = (const unsigned char *) str; + pstr->len = len; + pstr->raw_len = len; + pstr->trans = trans; + pstr->icase = icase; + pstr->mbs_allocated = (trans != NULL || icase); + pstr->mb_cur_max = dfa->mb_cur_max; + pstr->is_utf8 = dfa->is_utf8; + pstr->map_notascii = dfa->map_notascii; + pstr->stop = pstr->len; + pstr->raw_stop = pstr->stop; +} + +#ifdef RE_ENABLE_I18N + +/* Build wide character buffer PSTR->WCS. + If the byte sequence of the string are: + (0), (1), (0), (1), + Then wide character buffer will be: + , WEOF , , WEOF , + We use WEOF for padding, they indicate that the position isn't + a first byte of a multibyte character. + + Note that this function assumes PSTR->VALID_LEN elements are already + built and starts from PSTR->VALID_LEN. */ + +static void +internal_function +build_wcs_buffer (re_string_t *pstr) +{ +#ifdef _LIBC + unsigned char buf[MB_LEN_MAX]; + assert (MB_LEN_MAX >= pstr->mb_cur_max); +#else + unsigned char buf[64]; +#endif + mbstate_t prev_st; + Idx byte_idx, end_idx, remain_len; + size_t mbclen; + + /* Build the buffers from pstr->valid_len to either pstr->len or + pstr->bufs_len. */ + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + for (byte_idx = pstr->valid_len; byte_idx < end_idx;) + { + wchar_t wc; + const char *p; + + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + /* Apply the translation if we need. */ + if (BE (pstr->trans != NULL, 0)) + { + int i, ch; + + for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) + { + ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i]; + buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch]; + } + p = (const char *) buf; + } + else + p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx; + mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2, 0)) + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0)) + { + /* We treat these cases as a singlebyte character. */ + mbclen = 1; + wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; + if (BE (pstr->trans != NULL, 0)) + wc = pstr->trans[wc]; + pstr->cur_state = prev_st; + } + + /* Write wide character and padding. */ + pstr->wcs[byte_idx++] = wc; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = byte_idx; +} + +/* Build wide character buffer PSTR->WCS like build_wcs_buffer, + but for REG_ICASE. */ + +static reg_errcode_t +internal_function +build_wcs_upper_buffer (re_string_t *pstr) +{ + mbstate_t prev_st; + Idx src_idx, byte_idx, end_idx, remain_len; + size_t mbclen; +#ifdef _LIBC + char buf[MB_LEN_MAX]; + assert (MB_LEN_MAX >= pstr->mb_cur_max); +#else + char buf[64]; +#endif + + byte_idx = pstr->valid_len; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + /* The following optimization assumes that ASCII characters can be + mapped to wide characters with a simple cast. */ + if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed) + { + while (byte_idx < end_idx) + { + wchar_t wc; + + if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]) + && mbsinit (&pstr->cur_state)) + { + /* In case of a singlebyte character. */ + pstr->mbs[byte_idx] + = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]); + /* The next step uses the assumption that wchar_t is encoded + ASCII-safe: all ASCII values can be converted like this. */ + pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx]; + ++byte_idx; + continue; + } + + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + mbclen = mbrtowc (&wc, + ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx + + byte_idx), remain_len, &pstr->cur_state); + if (BE (mbclen < (size_t) -2, 1)) + { + wchar_t wcu = wc; + if (iswlower (wc)) + { + size_t mbcdlen; + + wcu = towupper (wc); + mbcdlen = wcrtomb (buf, wcu, &prev_st); + if (BE (mbclen == mbcdlen, 1)) + memcpy (pstr->mbs + byte_idx, buf, mbclen); + else + { + src_idx = byte_idx; + goto offsets_needed; + } + } + else + memcpy (pstr->mbs + byte_idx, + pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen); + pstr->wcs[byte_idx++] = wcu; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + else if (mbclen == (size_t) -1 || mbclen == 0) + { + /* It is an invalid character or '\0'. Just use the byte. */ + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; + pstr->mbs[byte_idx] = ch; + /* And also cast it to wide char. */ + pstr->wcs[byte_idx++] = (wchar_t) ch; + if (BE (mbclen == (size_t) -1, 0)) + pstr->cur_state = prev_st; + } + else + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = byte_idx; + return REG_NOERROR; + } + else + for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;) + { + wchar_t wc; + const char *p; + offsets_needed: + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + if (BE (pstr->trans != NULL, 0)) + { + int i, ch; + + for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) + { + ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i]; + buf[i] = pstr->trans[ch]; + } + p = (const char *) buf; + } + else + p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx; + mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state); + if (BE (mbclen < (size_t) -2, 1)) + { + wchar_t wcu = wc; + if (iswlower (wc)) + { + size_t mbcdlen; + + wcu = towupper (wc); + mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st); + if (BE (mbclen == mbcdlen, 1)) + memcpy (pstr->mbs + byte_idx, buf, mbclen); + else if (mbcdlen != (size_t) -1) + { + size_t i; + + if (byte_idx + mbcdlen > pstr->bufs_len) + { + pstr->cur_state = prev_st; + break; + } + + if (pstr->offsets == NULL) + { + pstr->offsets = re_malloc (Idx, pstr->bufs_len); + + if (pstr->offsets == NULL) + return REG_ESPACE; + } + if (!pstr->offsets_needed) + { + for (i = 0; i < (size_t) byte_idx; ++i) + pstr->offsets[i] = i; + pstr->offsets_needed = 1; + } + + memcpy (pstr->mbs + byte_idx, buf, mbcdlen); + pstr->wcs[byte_idx] = wcu; + pstr->offsets[byte_idx] = src_idx; + for (i = 1; i < mbcdlen; ++i) + { + pstr->offsets[byte_idx + i] + = src_idx + (i < mbclen ? i : mbclen - 1); + pstr->wcs[byte_idx + i] = WEOF; + } + pstr->len += mbcdlen - mbclen; + if (pstr->raw_stop > src_idx) + pstr->stop += mbcdlen - mbclen; + end_idx = (pstr->bufs_len > pstr->len) + ? pstr->len : pstr->bufs_len; + byte_idx += mbcdlen; + src_idx += mbclen; + continue; + } + else + memcpy (pstr->mbs + byte_idx, p, mbclen); + } + else + memcpy (pstr->mbs + byte_idx, p, mbclen); + + if (BE (pstr->offsets_needed != 0, 0)) + { + size_t i; + for (i = 0; i < mbclen; ++i) + pstr->offsets[byte_idx + i] = src_idx + i; + } + src_idx += mbclen; + + pstr->wcs[byte_idx++] = wcu; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + else if (mbclen == (size_t) -1 || mbclen == 0) + { + /* It is an invalid character or '\0'. Just use the byte. */ + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx]; + + if (BE (pstr->trans != NULL, 0)) + ch = pstr->trans [ch]; + pstr->mbs[byte_idx] = ch; + + if (BE (pstr->offsets_needed != 0, 0)) + pstr->offsets[byte_idx] = src_idx; + ++src_idx; + + /* And also cast it to wide char. */ + pstr->wcs[byte_idx++] = (wchar_t) ch; + if (BE (mbclen == (size_t) -1, 0)) + pstr->cur_state = prev_st; + } + else + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = src_idx; + return REG_NOERROR; +} + +/* Skip characters until the index becomes greater than NEW_RAW_IDX. + Return the index. */ + +static Idx +internal_function +re_string_skip_chars (re_string_t *pstr, Idx new_raw_idx, wint_t *last_wc) +{ + mbstate_t prev_st; + Idx rawbuf_idx; + size_t mbclen; + wint_t wc = WEOF; + + /* Skip the characters which are not necessary to check. */ + for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len; + rawbuf_idx < new_raw_idx;) + { + wchar_t wc2; + Idx remain_len; + remain_len = pstr->len - rawbuf_idx; + prev_st = pstr->cur_state; + mbclen = mbrtowc (&wc2, (const char *) pstr->raw_mbs + rawbuf_idx, + remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0)) + { + /* We treat these cases as a single byte character. */ + if (mbclen == 0 || remain_len == 0) + wc = L'\0'; + else + wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx); + mbclen = 1; + pstr->cur_state = prev_st; + } + else + wc = wc2; + /* Then proceed the next character. */ + rawbuf_idx += mbclen; + } + *last_wc = wc; + return rawbuf_idx; +} +#endif /* RE_ENABLE_I18N */ + +/* Build the buffer PSTR->MBS, and apply the translation if we need. + This function is used in case of REG_ICASE. */ + +static void +internal_function +build_upper_buffer (re_string_t *pstr) +{ + Idx char_idx, end_idx; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx) + { + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx]; + if (BE (pstr->trans != NULL, 0)) + ch = pstr->trans[ch]; + if (islower (ch)) + pstr->mbs[char_idx] = toupper (ch); + else + pstr->mbs[char_idx] = ch; + } + pstr->valid_len = char_idx; + pstr->valid_raw_len = char_idx; +} + +/* Apply TRANS to the buffer in PSTR. */ + +static void +internal_function +re_string_translate_buffer (re_string_t *pstr) +{ + Idx buf_idx, end_idx; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx) + { + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx]; + pstr->mbs[buf_idx] = pstr->trans[ch]; + } + + pstr->valid_len = buf_idx; + pstr->valid_raw_len = buf_idx; +} + +/* This function re-construct the buffers. + Concretely, convert to wide character in case of pstr->mb_cur_max > 1, + convert to upper case in case of REG_ICASE, apply translation. */ + +static reg_errcode_t +internal_function +re_string_reconstruct (re_string_t *pstr, Idx idx, int eflags) +{ + Idx offset; + + if (BE (pstr->raw_mbs_idx <= idx, 0)) + offset = idx - pstr->raw_mbs_idx; + else + { + /* Reset buffer. */ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); +#endif /* RE_ENABLE_I18N */ + pstr->len = pstr->raw_len; + pstr->stop = pstr->raw_stop; + pstr->valid_len = 0; + pstr->raw_mbs_idx = 0; + pstr->valid_raw_len = 0; + pstr->offsets_needed = 0; + pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF + : CONTEXT_NEWLINE | CONTEXT_BEGBUF); + if (!pstr->mbs_allocated) + pstr->mbs = (unsigned char *) pstr->raw_mbs; + offset = idx; + } + + if (BE (offset != 0, 1)) + { + /* Should the already checked characters be kept? */ + if (BE (offset < pstr->valid_raw_len, 1)) + { + /* Yes, move them to the front of the buffer. */ +#ifdef RE_ENABLE_I18N + if (BE (pstr->offsets_needed, 0)) + { + Idx low = 0, high = pstr->valid_len, mid; + do + { + mid = (high + low) / 2; + if (pstr->offsets[mid] > offset) + high = mid; + else if (pstr->offsets[mid] < offset) + low = mid + 1; + else + break; + } + while (low < high); + if (pstr->offsets[mid] < offset) + ++mid; + pstr->tip_context = re_string_context_at (pstr, mid - 1, + eflags); + /* This can be quite complicated, so handle specially + only the common and easy case where the character with + different length representation of lower and upper + case is present at or after offset. */ + if (pstr->valid_len > offset + && mid == offset && pstr->offsets[mid] == offset) + { + memmove (pstr->wcs, pstr->wcs + offset, + (pstr->valid_len - offset) * sizeof (wint_t)); + memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset); + pstr->valid_len -= offset; + pstr->valid_raw_len -= offset; + for (low = 0; low < pstr->valid_len; low++) + pstr->offsets[low] = pstr->offsets[low + offset] - offset; + } + else + { + /* Otherwise, just find out how long the partial multibyte + character at offset is and fill it with WEOF/255. */ + pstr->len = pstr->raw_len - idx + offset; + pstr->stop = pstr->raw_stop - idx + offset; + pstr->offsets_needed = 0; + while (mid > 0 && pstr->offsets[mid - 1] == offset) + --mid; + while (mid < pstr->valid_len) + if (pstr->wcs[mid] != WEOF) + break; + else + ++mid; + if (mid == pstr->valid_len) + pstr->valid_len = 0; + else + { + pstr->valid_len = pstr->offsets[mid] - offset; + if (pstr->valid_len) + { + for (low = 0; low < pstr->valid_len; ++low) + pstr->wcs[low] = WEOF; + memset (pstr->mbs, 255, pstr->valid_len); + } + } + pstr->valid_raw_len = pstr->valid_len; + } + } + else +#endif + { + pstr->tip_context = re_string_context_at (pstr, offset - 1, + eflags); +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + memmove (pstr->wcs, pstr->wcs + offset, + (pstr->valid_len - offset) * sizeof (wint_t)); +#endif /* RE_ENABLE_I18N */ + if (BE (pstr->mbs_allocated, 0)) + memmove (pstr->mbs, pstr->mbs + offset, + pstr->valid_len - offset); + pstr->valid_len -= offset; + pstr->valid_raw_len -= offset; +#if DEBUG + assert (pstr->valid_len > 0); +#endif + } + } + else + { + /* No, skip all characters until IDX. */ + Idx prev_valid_len = pstr->valid_len; + +#ifdef RE_ENABLE_I18N + if (BE (pstr->offsets_needed, 0)) + { + pstr->len = pstr->raw_len - idx + offset; + pstr->stop = pstr->raw_stop - idx + offset; + pstr->offsets_needed = 0; + } +#endif + pstr->valid_len = 0; +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + Idx wcs_idx; + wint_t wc = WEOF; + + if (pstr->is_utf8) + { + const unsigned char *raw, *p, *q, *end; + + /* Special case UTF-8. Multi-byte chars start with any + byte other than 0x80 - 0xbf. */ + raw = pstr->raw_mbs + pstr->raw_mbs_idx; + end = raw + (offset - pstr->mb_cur_max); + if (end < pstr->raw_mbs) + end = pstr->raw_mbs; + p = raw + offset - 1; +#ifdef _LIBC + /* We know the wchar_t encoding is UCS4, so for the simple + case, ASCII characters, skip the conversion step. */ + if (isascii (*p) && BE (pstr->trans == NULL, 1)) + { + memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); + /* pstr->valid_len = 0; */ + wc = (wchar_t) *p; + } + else +#endif + for (; p >= end; --p) + if ((*p & 0xc0) != 0x80) + { + mbstate_t cur_state; + wchar_t wc2; + Idx mlen = raw + pstr->len - p; + unsigned char buf[6]; + size_t mbclen; + + q = p; + if (BE (pstr->trans != NULL, 0)) + { + int i = mlen < 6 ? mlen : 6; + while (--i >= 0) + buf[i] = pstr->trans[p[i]]; + q = buf; + } + /* XXX Don't use mbrtowc, we know which conversion + to use (UTF-8 -> UCS4). */ + memset (&cur_state, 0, sizeof (cur_state)); + mbclen = mbrtowc (&wc2, (const char *) p, mlen, + &cur_state); + if (raw + offset - p <= mbclen + && mbclen < (size_t) -2) + { + memset (&pstr->cur_state, '\0', + sizeof (mbstate_t)); + pstr->valid_len = mbclen - (raw + offset - p); + wc = wc2; + } + break; + } + } + + if (wc == WEOF) + pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx; + if (wc == WEOF) + pstr->tip_context + = re_string_context_at (pstr, prev_valid_len - 1, eflags); + else + pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0) + && IS_WIDE_WORD_CHAR (wc)) + ? CONTEXT_WORD + : ((IS_WIDE_NEWLINE (wc) + && pstr->newline_anchor) + ? CONTEXT_NEWLINE : 0)); + if (BE (pstr->valid_len, 0)) + { + for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx) + pstr->wcs[wcs_idx] = WEOF; + if (pstr->mbs_allocated) + memset (pstr->mbs, 255, pstr->valid_len); + } + pstr->valid_raw_len = pstr->valid_len; + } + else +#endif /* RE_ENABLE_I18N */ + { + int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1]; + pstr->valid_raw_len = 0; + if (pstr->trans) + c = pstr->trans[c]; + pstr->tip_context = (bitset_contain (pstr->word_char, c) + ? CONTEXT_WORD + : ((IS_NEWLINE (c) && pstr->newline_anchor) + ? CONTEXT_NEWLINE : 0)); + } + } + if (!BE (pstr->mbs_allocated, 0)) + pstr->mbs += offset; + } + pstr->raw_mbs_idx = idx; + pstr->len -= offset; + pstr->stop -= offset; + + /* Then build the buffers. */ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + if (pstr->icase) + { + reg_errcode_t ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + else + build_wcs_buffer (pstr); + } + else +#endif /* RE_ENABLE_I18N */ + if (BE (pstr->mbs_allocated, 0)) + { + if (pstr->icase) + build_upper_buffer (pstr); + else if (pstr->trans != NULL) + re_string_translate_buffer (pstr); + } + else + pstr->valid_len = pstr->len; + + pstr->cur_idx = 0; + return REG_NOERROR; +} + +static unsigned char +internal_function __attribute ((pure)) +re_string_peek_byte_case (const re_string_t *pstr, Idx idx) +{ + int ch; + Idx off; + + /* Handle the common (easiest) cases first. */ + if (BE (!pstr->mbs_allocated, 1)) + return re_string_peek_byte (pstr, idx); + +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1 + && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx)) + return re_string_peek_byte (pstr, idx); +#endif + + off = pstr->cur_idx + idx; +#ifdef RE_ENABLE_I18N + if (pstr->offsets_needed) + off = pstr->offsets[off]; +#endif + + ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; + +#ifdef RE_ENABLE_I18N + /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I + this function returns CAPITAL LETTER I instead of first byte of + DOTLESS SMALL LETTER I. The latter would confuse the parser, + since peek_byte_case doesn't advance cur_idx in any way. */ + if (pstr->offsets_needed && !isascii (ch)) + return re_string_peek_byte (pstr, idx); +#endif + + return ch; +} + +static unsigned char +internal_function __attribute ((pure)) +re_string_fetch_byte_case (re_string_t *pstr) +{ + if (BE (!pstr->mbs_allocated, 1)) + return re_string_fetch_byte (pstr); + +#ifdef RE_ENABLE_I18N + if (pstr->offsets_needed) + { + Idx off; + int ch; + + /* For tr_TR.UTF-8 [[:islower:]] there is + [[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip + in that case the whole multi-byte character and return + the original letter. On the other side, with + [[: DOTLESS SMALL LETTER I return [[:I, as doing + anything else would complicate things too much. */ + + if (!re_string_first_byte (pstr, pstr->cur_idx)) + return re_string_fetch_byte (pstr); + + off = pstr->offsets[pstr->cur_idx]; + ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; + + if (! isascii (ch)) + return re_string_fetch_byte (pstr); + + re_string_skip_bytes (pstr, + re_string_char_size_at (pstr, pstr->cur_idx)); + return ch; + } +#endif + + return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++]; +} + +static void +internal_function +re_string_destruct (re_string_t *pstr) +{ +#ifdef RE_ENABLE_I18N + re_free (pstr->wcs); + re_free (pstr->offsets); +#endif /* RE_ENABLE_I18N */ + if (pstr->mbs_allocated) + re_free (pstr->mbs); +} + +/* Return the context at IDX in INPUT. */ + +static unsigned int +internal_function +re_string_context_at (const re_string_t *input, Idx idx, int eflags) +{ + int c; + if (BE (! REG_VALID_INDEX (idx), 0)) + /* In this case, we use the value stored in input->tip_context, + since we can't know the character in input->mbs[-1] here. */ + return input->tip_context; + if (BE (idx == input->len, 0)) + return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF + : CONTEXT_NEWLINE | CONTEXT_ENDBUF); +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc; + Idx wc_idx = idx; + while(input->wcs[wc_idx] == WEOF) + { +#ifdef DEBUG + /* It must not happen. */ + assert (REG_VALID_INDEX (wc_idx)); +#endif + --wc_idx; + if (! REG_VALID_INDEX (wc_idx)) + return input->tip_context; + } + wc = input->wcs[wc_idx]; + if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc)) + return CONTEXT_WORD; + return (IS_WIDE_NEWLINE (wc) && input->newline_anchor + ? CONTEXT_NEWLINE : 0); + } + else +#endif + { + c = re_string_byte_at (input, idx); + if (bitset_contain (input->word_char, c)) + return CONTEXT_WORD; + return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0; + } +} + +/* Functions for set operation. */ + +static reg_errcode_t +internal_function +re_node_set_alloc (re_node_set *set, Idx size) +{ + set->alloc = size; + set->nelem = 0; + set->elems = re_malloc (Idx, size); + if (BE (set->elems == NULL, 0)) + return REG_ESPACE; + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +re_node_set_init_1 (re_node_set *set, Idx elem) +{ + set->alloc = 1; + set->nelem = 1; + set->elems = re_malloc (Idx, 1); + if (BE (set->elems == NULL, 0)) + { + set->alloc = set->nelem = 0; + return REG_ESPACE; + } + set->elems[0] = elem; + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +re_node_set_init_2 (re_node_set *set, Idx elem1, Idx elem2) +{ + set->alloc = 2; + set->elems = re_malloc (Idx, 2); + if (BE (set->elems == NULL, 0)) + return REG_ESPACE; + if (elem1 == elem2) + { + set->nelem = 1; + set->elems[0] = elem1; + } + else + { + set->nelem = 2; + if (elem1 < elem2) + { + set->elems[0] = elem1; + set->elems[1] = elem2; + } + else + { + set->elems[0] = elem2; + set->elems[1] = elem1; + } + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +re_node_set_init_copy (re_node_set *dest, const re_node_set *src) +{ + dest->nelem = src->nelem; + if (src->nelem > 0) + { + dest->alloc = dest->nelem; + dest->elems = re_malloc (Idx, dest->alloc); + if (BE (dest->elems == NULL, 0)) + { + dest->alloc = dest->nelem = 0; + return REG_ESPACE; + } + memcpy (dest->elems, src->elems, src->nelem * sizeof (Idx)); + } + else + re_node_set_init_empty (dest); + return REG_NOERROR; +} + +/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. + Note: We assume dest->elems is NULL, when dest->alloc is 0. */ + +static reg_errcode_t +internal_function +re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1, + const re_node_set *src2) +{ + Idx i1, i2, is, id, delta, sbase; + if (src1->nelem == 0 || src2->nelem == 0) + return REG_NOERROR; + + /* We need dest->nelem + 2 * elems_in_intersection; this is a + conservative estimate. */ + if (src1->nelem + src2->nelem + dest->nelem > dest->alloc) + { + Idx new_alloc = src1->nelem + src2->nelem + dest->alloc; + Idx *new_elems = re_realloc (dest->elems, Idx, new_alloc); + if (BE (new_elems == NULL, 0)) + return REG_ESPACE; + dest->elems = new_elems; + dest->alloc = new_alloc; + } + + /* Find the items in the intersection of SRC1 and SRC2, and copy + into the top of DEST those that are not already in DEST itself. */ + sbase = dest->nelem + src1->nelem + src2->nelem; + i1 = src1->nelem - 1; + i2 = src2->nelem - 1; + id = dest->nelem - 1; + for (;;) + { + if (src1->elems[i1] == src2->elems[i2]) + { + /* Try to find the item in DEST. Maybe we could binary search? */ + while (REG_VALID_INDEX (id) && dest->elems[id] > src1->elems[i1]) + --id; + + if (! REG_VALID_INDEX (id) || dest->elems[id] != src1->elems[i1]) + dest->elems[--sbase] = src1->elems[i1]; + + if (! REG_VALID_INDEX (--i1) || ! REG_VALID_INDEX (--i2)) + break; + } + + /* Lower the highest of the two items. */ + else if (src1->elems[i1] < src2->elems[i2]) + { + if (! REG_VALID_INDEX (--i2)) + break; + } + else + { + if (! REG_VALID_INDEX (--i1)) + break; + } + } + + id = dest->nelem - 1; + is = dest->nelem + src1->nelem + src2->nelem - 1; + delta = is - sbase + 1; + + /* Now copy. When DELTA becomes zero, the remaining + DEST elements are already in place; this is more or + less the same loop that is in re_node_set_merge. */ + dest->nelem += delta; + if (delta > 0 && REG_VALID_INDEX (id)) + for (;;) + { + if (dest->elems[is] > dest->elems[id]) + { + /* Copy from the top. */ + dest->elems[id + delta--] = dest->elems[is--]; + if (delta == 0) + break; + } + else + { + /* Slide from the bottom. */ + dest->elems[id + delta] = dest->elems[id]; + if (! REG_VALID_INDEX (--id)) + break; + } + } + + /* Copy remaining SRC elements. */ + memcpy (dest->elems, dest->elems + sbase, delta * sizeof (Idx)); + + return REG_NOERROR; +} + +/* Calculate the union set of the sets SRC1 and SRC2. And store it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ + +static reg_errcode_t +internal_function +re_node_set_init_union (re_node_set *dest, const re_node_set *src1, + const re_node_set *src2) +{ + Idx i1, i2, id; + if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0) + { + dest->alloc = src1->nelem + src2->nelem; + dest->elems = re_malloc (Idx, dest->alloc); + if (BE (dest->elems == NULL, 0)) + return REG_ESPACE; + } + else + { + if (src1 != NULL && src1->nelem > 0) + return re_node_set_init_copy (dest, src1); + else if (src2 != NULL && src2->nelem > 0) + return re_node_set_init_copy (dest, src2); + else + re_node_set_init_empty (dest); + return REG_NOERROR; + } + for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;) + { + if (src1->elems[i1] > src2->elems[i2]) + { + dest->elems[id++] = src2->elems[i2++]; + continue; + } + if (src1->elems[i1] == src2->elems[i2]) + ++i2; + dest->elems[id++] = src1->elems[i1++]; + } + if (i1 < src1->nelem) + { + memcpy (dest->elems + id, src1->elems + i1, + (src1->nelem - i1) * sizeof (Idx)); + id += src1->nelem - i1; + } + else if (i2 < src2->nelem) + { + memcpy (dest->elems + id, src2->elems + i2, + (src2->nelem - i2) * sizeof (Idx)); + id += src2->nelem - i2; + } + dest->nelem = id; + return REG_NOERROR; +} + +/* Calculate the union set of the sets DEST and SRC. And store it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ + +static reg_errcode_t +internal_function +re_node_set_merge (re_node_set *dest, const re_node_set *src) +{ + Idx is, id, sbase, delta; + if (src == NULL || src->nelem == 0) + return REG_NOERROR; + if (dest->alloc < 2 * src->nelem + dest->nelem) + { + Idx new_alloc = 2 * (src->nelem + dest->alloc); + Idx *new_buffer = re_realloc (dest->elems, Idx, new_alloc); + if (BE (new_buffer == NULL, 0)) + return REG_ESPACE; + dest->elems = new_buffer; + dest->alloc = new_alloc; + } + + if (BE (dest->nelem == 0, 0)) + { + dest->nelem = src->nelem; + memcpy (dest->elems, src->elems, src->nelem * sizeof (Idx)); + return REG_NOERROR; + } + + /* Copy into the top of DEST the items of SRC that are not + found in DEST. Maybe we could binary search in DEST? */ + for (sbase = dest->nelem + 2 * src->nelem, + is = src->nelem - 1, id = dest->nelem - 1; + REG_VALID_INDEX (is) && REG_VALID_INDEX (id); ) + { + if (dest->elems[id] == src->elems[is]) + is--, id--; + else if (dest->elems[id] < src->elems[is]) + dest->elems[--sbase] = src->elems[is--]; + else /* if (dest->elems[id] > src->elems[is]) */ + --id; + } + + if (REG_VALID_INDEX (is)) + { + /* If DEST is exhausted, the remaining items of SRC must be unique. */ + sbase -= is + 1; + memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (Idx)); + } + + id = dest->nelem - 1; + is = dest->nelem + 2 * src->nelem - 1; + delta = is - sbase + 1; + if (delta == 0) + return REG_NOERROR; + + /* Now copy. When DELTA becomes zero, the remaining + DEST elements are already in place. */ + dest->nelem += delta; + for (;;) + { + if (dest->elems[is] > dest->elems[id]) + { + /* Copy from the top. */ + dest->elems[id + delta--] = dest->elems[is--]; + if (delta == 0) + break; + } + else + { + /* Slide from the bottom. */ + dest->elems[id + delta] = dest->elems[id]; + if (! REG_VALID_INDEX (--id)) + { + /* Copy remaining SRC elements. */ + memcpy (dest->elems, dest->elems + sbase, + delta * sizeof (Idx)); + break; + } + } + } + + return REG_NOERROR; +} + +/* Insert the new element ELEM to the re_node_set* SET. + SET should not already have ELEM. + Return true if successful. */ + +static bool +internal_function +re_node_set_insert (re_node_set *set, Idx elem) +{ + Idx idx; + /* In case the set is empty. */ + if (set->alloc == 0) + return BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1); + + if (BE (set->nelem, 0) == 0) + { + /* We already guaranteed above that set->alloc != 0. */ + set->elems[0] = elem; + ++set->nelem; + return true; + } + + /* Realloc if we need. */ + if (set->alloc == set->nelem) + { + Idx *new_elems; + set->alloc = set->alloc * 2; + new_elems = re_realloc (set->elems, Idx, set->alloc); + if (BE (new_elems == NULL, 0)) + return false; + set->elems = new_elems; + } + + /* Move the elements which follows the new element. Test the + first element separately to skip a check in the inner loop. */ + if (elem < set->elems[0]) + { + idx = 0; + for (idx = set->nelem; idx > 0; idx--) + set->elems[idx] = set->elems[idx - 1]; + } + else + { + for (idx = set->nelem; set->elems[idx - 1] > elem; idx--) + set->elems[idx] = set->elems[idx - 1]; + } + + /* Insert the new element. */ + set->elems[idx] = elem; + ++set->nelem; + return true; +} + +/* Insert the new element ELEM to the re_node_set* SET. + SET should not already have any element greater than or equal to ELEM. + Return true if successful. */ + +static bool +internal_function +re_node_set_insert_last (re_node_set *set, Idx elem) +{ + /* Realloc if we need. */ + if (set->alloc == set->nelem) + { + Idx *new_elems; + set->alloc = (set->alloc + 1) * 2; + new_elems = re_realloc (set->elems, Idx, set->alloc); + if (BE (new_elems == NULL, 0)) + return false; + set->elems = new_elems; + } + + /* Insert the new element. */ + set->elems[set->nelem++] = elem; + return true; +} + +/* Compare two node sets SET1 and SET2. + Return true if SET1 and SET2 are equivalent. */ + +static bool +internal_function __attribute ((pure)) +re_node_set_compare (const re_node_set *set1, const re_node_set *set2) +{ + Idx i; + if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem) + return false; + for (i = set1->nelem ; REG_VALID_INDEX (--i) ; ) + if (set1->elems[i] != set2->elems[i]) + return false; + return true; +} + +/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */ + +static Idx +internal_function __attribute ((pure)) +re_node_set_contains (const re_node_set *set, Idx elem) +{ + __re_size_t idx, right, mid; + if (! REG_VALID_NONZERO_INDEX (set->nelem)) + return 0; + + /* Binary search the element. */ + idx = 0; + right = set->nelem - 1; + while (idx < right) + { + mid = (idx + right) / 2; + if (set->elems[mid] < elem) + idx = mid + 1; + else + right = mid; + } + return set->elems[idx] == elem ? idx + 1 : 0; +} + +static void +internal_function +re_node_set_remove_at (re_node_set *set, Idx idx) +{ + if (idx < 0 || idx >= set->nelem) + return; + --set->nelem; + for (; idx < set->nelem; idx++) + set->elems[idx] = set->elems[idx + 1]; +} + + +/* Add the token TOKEN to dfa->nodes, and return the index of the token. + Or return REG_MISSING if an error occurred. */ + +static Idx +internal_function +re_dfa_add_node (re_dfa_t *dfa, re_token_t token) +{ + if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0)) + { + size_t new_nodes_alloc = dfa->nodes_alloc * 2; + Idx *new_nexts, *new_indices; + re_node_set *new_edests, *new_eclosures; + re_token_t *new_nodes; + size_t max_object_size = + MAX (sizeof (re_token_t), + MAX (sizeof (re_node_set), + sizeof (Idx))); + + /* Avoid overflows. */ + if (BE (SIZE_MAX / 2 / max_object_size < dfa->nodes_alloc, 0)) + return REG_MISSING; + + new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc); + if (BE (new_nodes == NULL, 0)) + return REG_MISSING; + dfa->nodes = new_nodes; + new_nexts = re_realloc (dfa->nexts, Idx, new_nodes_alloc); + new_indices = re_realloc (dfa->org_indices, Idx, new_nodes_alloc); + new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc); + new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc); + if (BE (new_nexts == NULL || new_indices == NULL + || new_edests == NULL || new_eclosures == NULL, 0)) + return REG_MISSING; + dfa->nexts = new_nexts; + dfa->org_indices = new_indices; + dfa->edests = new_edests; + dfa->eclosures = new_eclosures; + dfa->nodes_alloc = new_nodes_alloc; + } + dfa->nodes[dfa->nodes_len] = token; + dfa->nodes[dfa->nodes_len].constraint = 0; +#ifdef RE_ENABLE_I18N + { + int type = token.type; + dfa->nodes[dfa->nodes_len].accept_mb = + (type == OP_PERIOD && dfa->mb_cur_max > 1) || type == COMPLEX_BRACKET; + } +#endif + dfa->nexts[dfa->nodes_len] = REG_MISSING; + re_node_set_init_empty (dfa->edests + dfa->nodes_len); + re_node_set_init_empty (dfa->eclosures + dfa->nodes_len); + return dfa->nodes_len++; +} + +static inline re_hashval_t +internal_function +calc_state_hash (const re_node_set *nodes, unsigned int context) +{ + re_hashval_t hash = nodes->nelem + context; + Idx i; + for (i = 0 ; i < nodes->nelem ; i++) + hash += nodes->elems[i]; + return hash; +} + +/* Search for the state whose node_set is equivalent to NODES. + Return the pointer to the state, if we found it in the DFA. + Otherwise create the new one and return it. In case of an error + return NULL and set the error code in ERR. + Note: - We assume NULL as the invalid state, then it is possible that + return value is NULL and ERR is REG_NOERROR. + - We never return non-NULL value in case of any errors, it is for + optimization. */ + +static re_dfastate_t * +internal_function +re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa, + const re_node_set *nodes) +{ + re_hashval_t hash; + re_dfastate_t *new_state; + struct re_state_table_entry *spot; + Idx i; +#ifdef lint + /* Suppress bogus uninitialized-variable warnings. */ + *err = REG_NOERROR; +#endif + if (BE (nodes->nelem == 0, 0)) + { + *err = REG_NOERROR; + return NULL; + } + hash = calc_state_hash (nodes, 0); + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + for (i = 0 ; i < spot->num ; i++) + { + re_dfastate_t *state = spot->array[i]; + if (hash != state->hash) + continue; + if (re_node_set_compare (&state->nodes, nodes)) + return state; + } + + /* There are no appropriate state in the dfa, create the new one. */ + new_state = create_ci_newstate (dfa, nodes, hash); + if (BE (new_state == NULL, 0)) + *err = REG_ESPACE; + + return new_state; +} + +/* Search for the state whose node_set is equivalent to NODES and + whose context is equivalent to CONTEXT. + Return the pointer to the state, if we found it in the DFA. + Otherwise create the new one and return it. In case of an error + return NULL and set the error code in ERR. + Note: - We assume NULL as the invalid state, then it is possible that + return value is NULL and ERR is REG_NOERROR. + - We never return non-NULL value in case of any errors, it is for + optimization. */ + +static re_dfastate_t * +internal_function +re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa, + const re_node_set *nodes, unsigned int context) +{ + re_hashval_t hash; + re_dfastate_t *new_state; + struct re_state_table_entry *spot; + Idx i; +#ifdef lint + /* Suppress bogus uninitialized-variable warnings. */ + *err = REG_NOERROR; +#endif + if (nodes->nelem == 0) + { + *err = REG_NOERROR; + return NULL; + } + hash = calc_state_hash (nodes, context); + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + for (i = 0 ; i < spot->num ; i++) + { + re_dfastate_t *state = spot->array[i]; + if (state->hash == hash + && state->context == context + && re_node_set_compare (state->entrance_nodes, nodes)) + return state; + } + /* There are no appropriate state in `dfa', create the new one. */ + new_state = create_cd_newstate (dfa, nodes, context, hash); + if (BE (new_state == NULL, 0)) + *err = REG_ESPACE; + + return new_state; +} + +/* Finish initialization of the new state NEWSTATE, and using its hash value + HASH put in the appropriate bucket of DFA's state table. Return value + indicates the error code if failed. */ + +static reg_errcode_t +register_state (const re_dfa_t *dfa, re_dfastate_t *newstate, + re_hashval_t hash) +{ + struct re_state_table_entry *spot; + reg_errcode_t err; + Idx i; + + newstate->hash = hash; + err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem); + if (BE (err != REG_NOERROR, 0)) + return REG_ESPACE; + for (i = 0; i < newstate->nodes.nelem; i++) + { + Idx elem = newstate->nodes.elems[i]; + if (!IS_EPSILON_NODE (dfa->nodes[elem].type)) + if (BE (! re_node_set_insert_last (&newstate->non_eps_nodes, elem), 0)) + return REG_ESPACE; + } + + spot = dfa->state_table + (hash & dfa->state_hash_mask); + if (BE (spot->alloc <= spot->num, 0)) + { + Idx new_alloc = 2 * spot->num + 2; + re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *, + new_alloc); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + spot->array = new_array; + spot->alloc = new_alloc; + } + spot->array[spot->num++] = newstate; + return REG_NOERROR; +} + +static void +free_state (re_dfastate_t *state) +{ + re_node_set_free (&state->non_eps_nodes); + re_node_set_free (&state->inveclosure); + if (state->entrance_nodes != &state->nodes) + { + re_node_set_free (state->entrance_nodes); + re_free (state->entrance_nodes); + } + re_node_set_free (&state->nodes); + re_free (state->word_trtable); + re_free (state->trtable); + re_free (state); +} + +/* Create the new state which is independ of contexts. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +internal_function +create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes, + re_hashval_t hash) +{ + Idx i; + reg_errcode_t err; + re_dfastate_t *newstate; + + newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); + if (BE (newstate == NULL, 0)) + return NULL; + err = re_node_set_init_copy (&newstate->nodes, nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_free (newstate); + return NULL; + } + + newstate->entrance_nodes = &newstate->nodes; + for (i = 0 ; i < nodes->nelem ; i++) + { + re_token_t *node = dfa->nodes + nodes->elems[i]; + re_token_type_t type = node->type; + if (type == CHARACTER && !node->constraint) + continue; +#ifdef RE_ENABLE_I18N + newstate->accept_mb |= node->accept_mb; +#endif /* RE_ENABLE_I18N */ + + /* If the state has the halt node, the state is a halt state. */ + if (type == END_OF_RE) + newstate->halt = 1; + else if (type == OP_BACK_REF) + newstate->has_backref = 1; + else if (type == ANCHOR || node->constraint) + newstate->has_constraint = 1; + } + err = register_state (dfa, newstate, hash); + if (BE (err != REG_NOERROR, 0)) + { + free_state (newstate); + newstate = NULL; + } + return newstate; +} + +/* Create the new state which is depend on the context CONTEXT. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +internal_function +create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes, + unsigned int context, re_hashval_t hash) +{ + Idx i, nctx_nodes = 0; + reg_errcode_t err; + re_dfastate_t *newstate; + + newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); + if (BE (newstate == NULL, 0)) + return NULL; + err = re_node_set_init_copy (&newstate->nodes, nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_free (newstate); + return NULL; + } + + newstate->context = context; + newstate->entrance_nodes = &newstate->nodes; + + for (i = 0 ; i < nodes->nelem ; i++) + { + unsigned int constraint = 0; + re_token_t *node = dfa->nodes + nodes->elems[i]; + re_token_type_t type = node->type; + if (node->constraint) + constraint = node->constraint; + + if (type == CHARACTER && !constraint) + continue; +#ifdef RE_ENABLE_I18N + newstate->accept_mb |= node->accept_mb; +#endif /* RE_ENABLE_I18N */ + + /* If the state has the halt node, the state is a halt state. */ + if (type == END_OF_RE) + newstate->halt = 1; + else if (type == OP_BACK_REF) + newstate->has_backref = 1; + else if (type == ANCHOR) + constraint = node->opr.ctx_type; + + if (constraint) + { + if (newstate->entrance_nodes == &newstate->nodes) + { + newstate->entrance_nodes = re_malloc (re_node_set, 1); + if (BE (newstate->entrance_nodes == NULL, 0)) + { + free_state (newstate); + return NULL; + } + re_node_set_init_copy (newstate->entrance_nodes, nodes); + nctx_nodes = 0; + newstate->has_constraint = 1; + } + + if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context)) + { + re_node_set_remove_at (&newstate->nodes, i - nctx_nodes); + ++nctx_nodes; + } + } + } + err = register_state (dfa, newstate, hash); + if (BE (err != REG_NOERROR, 0)) + { + free_state (newstate); + newstate = NULL; + } + return newstate; +} diff --git a/src/bin/m4/lib/regex_internal.h b/src/bin/m4/lib/regex_internal.h new file mode 100644 index 0000000000..cc9b5b79c2 --- /dev/null +++ b/src/bin/m4/lib/regex_internal.h @@ -0,0 +1,865 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _REGEX_INTERNAL_H +#define _REGEX_INTERNAL_H 1 + +#include +#include +#include +#include +#include +#include + +#ifndef _LIBC +# include "strcase.h" +#endif + +#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC +# include +#endif +#if defined HAVE_LOCALE_H || defined _LIBC +# include +#endif +#if defined HAVE_WCHAR_H || defined _LIBC +# include +#endif /* HAVE_WCHAR_H || _LIBC */ +#if defined HAVE_WCTYPE_H || defined _LIBC +# include +#endif /* HAVE_WCTYPE_H || _LIBC */ +#include +#if defined _LIBC +# include +#else +# define __libc_lock_init(NAME) do { } while (0) +# define __libc_lock_lock(NAME) do { } while (0) +# define __libc_lock_unlock(NAME) do { } while (0) +#endif + +/* In case that the system doesn't have isblank(). */ +#if !defined _LIBC && !HAVE_DECL_ISBLANK && !defined isblank +# define isblank(ch) ((ch) == ' ' || (ch) == '\t') +#endif + +#ifdef _LIBC +# ifndef _RE_DEFINE_LOCALE_FUNCTIONS +# define _RE_DEFINE_LOCALE_FUNCTIONS 1 +# include +# include +# include +# endif +#endif + +/* This is for other GNU distributions with internationalized messages. */ +#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC +# include +# ifdef _LIBC +# undef gettext +# define gettext(msgid) \ + INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES) +# endif +#else +# define gettext(msgid) (msgid) +#endif + +#ifndef gettext_noop +/* This define is so xgettext can find the internationalizable + strings. */ +# define gettext_noop(String) String +#endif + +/* For loser systems without the definition. */ +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +#if (defined MB_CUR_MAX && HAVE_LOCALE_H && HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_WCRTOMB && HAVE_MBRTOWC && HAVE_WCSCOLL) || _LIBC +# define RE_ENABLE_I18N +#endif + +#if __GNUC__ >= 3 +# define BE(expr, val) __builtin_expect (expr, val) +#else +# define BE(expr, val) (expr) +# ifdef _LIBC +# define inline +# endif +#endif + +/* Number of ASCII characters. */ +#define ASCII_CHARS 0x80 + +/* Number of single byte characters. */ +#define SBC_MAX (UCHAR_MAX + 1) + +#define COLL_ELEM_LEN_MAX 8 + +/* The character which represents newline. */ +#define NEWLINE_CHAR '\n' +#define WIDE_NEWLINE_CHAR L'\n' + +/* Rename to standard API for using out of glibc. */ +#ifndef _LIBC +# define __wctype wctype +# define __iswctype iswctype +# define __btowc btowc +# ifndef __mempcpy +# define __mempcpy mempcpy +# endif +# define __wcrtomb wcrtomb +# define __regfree regfree +# define attribute_hidden +#endif /* not _LIBC */ + +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define __attribute(arg) __attribute__ (arg) +#else +# define __attribute(arg) +#endif + +typedef __re_idx_t Idx; + +/* Special return value for failure to match. */ +#define REG_MISSING ((Idx) -1) + +/* Special return value for internal error. */ +#define REG_ERROR ((Idx) -2) + +/* Test whether N is a valid index, and is not one of the above. */ +#ifdef _REGEX_LARGE_OFFSETS +# define REG_VALID_INDEX(n) ((Idx) (n) < REG_ERROR) +#else +# define REG_VALID_INDEX(n) (0 <= (n)) +#endif + +/* Test whether N is a valid nonzero index. */ +#ifdef _REGEX_LARGE_OFFSETS +# define REG_VALID_NONZERO_INDEX(n) ((Idx) ((n) - 1) < (Idx) (REG_ERROR - 1)) +#else +# define REG_VALID_NONZERO_INDEX(n) (0 < (n)) +#endif + +/* A hash value, suitable for computing hash tables. */ +typedef __re_size_t re_hashval_t; + +/* An integer used to represent a set of bits. It must be unsigned, + and must be at least as wide as unsigned int. */ +typedef unsigned long int bitset_word_t; +/* All bits set in a bitset_word_t. */ +#define BITSET_WORD_MAX ULONG_MAX + +/* Number of bits in a bitset_word_t. For portability to hosts with + padding bits, do not use '(sizeof (bitset_word_t) * CHAR_BIT)'; + instead, deduce it directly from BITSET_WORD_MAX. Avoid + greater-than-32-bit integers and unconditional shifts by more than + 31 bits, as they're not portable. */ +#if BITSET_WORD_MAX == 0xffffffff +# define BITSET_WORD_BITS 32 +#elif BITSET_WORD_MAX >> 31 >> 5 == 1 +# define BITSET_WORD_BITS 36 +#elif BITSET_WORD_MAX >> 31 >> 16 == 1 +# define BITSET_WORD_BITS 48 +#elif BITSET_WORD_MAX >> 31 >> 28 == 1 +# define BITSET_WORD_BITS 60 +#elif BITSET_WORD_MAX >> 31 >> 31 >> 1 == 1 +# define BITSET_WORD_BITS 64 +#elif BITSET_WORD_MAX >> 31 >> 31 >> 9 == 1 +# define BITSET_WORD_BITS 72 +#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 3 == 1 +# define BITSET_WORD_BITS 128 +#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 7 == 1 +# define BITSET_WORD_BITS 256 +#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 7 > 1 +# define BITSET_WORD_BITS 257 /* any value > SBC_MAX will do here */ +# if BITSET_WORD_BITS <= SBC_MAX +# error "Invalid SBC_MAX" +# endif +#elif BITSET_WORD_MAX == (0xffffffff + 2) * 0xffffffff +/* Work around a bug in 64-bit PGC (before version 6.1-2), where the + preprocessor mishandles large unsigned values as if they were signed. */ +# define BITSET_WORD_BITS 64 +#else +# error "Add case for new bitset_word_t size" +#endif + +/* Number of bitset_word_t values in a bitset_t. */ +#define BITSET_WORDS ((SBC_MAX + BITSET_WORD_BITS - 1) / BITSET_WORD_BITS) + +typedef bitset_word_t bitset_t[BITSET_WORDS]; +typedef bitset_word_t *re_bitset_ptr_t; +typedef const bitset_word_t *re_const_bitset_ptr_t; + +#define PREV_WORD_CONSTRAINT 0x0001 +#define PREV_NOTWORD_CONSTRAINT 0x0002 +#define NEXT_WORD_CONSTRAINT 0x0004 +#define NEXT_NOTWORD_CONSTRAINT 0x0008 +#define PREV_NEWLINE_CONSTRAINT 0x0010 +#define NEXT_NEWLINE_CONSTRAINT 0x0020 +#define PREV_BEGBUF_CONSTRAINT 0x0040 +#define NEXT_ENDBUF_CONSTRAINT 0x0080 +#define WORD_DELIM_CONSTRAINT 0x0100 +#define NOT_WORD_DELIM_CONSTRAINT 0x0200 + +typedef enum +{ + INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, + WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, + WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, + INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, + LINE_FIRST = PREV_NEWLINE_CONSTRAINT, + LINE_LAST = NEXT_NEWLINE_CONSTRAINT, + BUF_FIRST = PREV_BEGBUF_CONSTRAINT, + BUF_LAST = NEXT_ENDBUF_CONSTRAINT, + WORD_DELIM = WORD_DELIM_CONSTRAINT, + NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT +} re_context_type; + +typedef struct +{ + Idx alloc; + Idx nelem; + Idx *elems; +} re_node_set; + +typedef enum +{ + NON_TYPE = 0, + + /* Node type, These are used by token, node, tree. */ + CHARACTER = 1, + END_OF_RE = 2, + SIMPLE_BRACKET = 3, + OP_BACK_REF = 4, + OP_PERIOD = 5, +#ifdef RE_ENABLE_I18N + COMPLEX_BRACKET = 6, + OP_UTF8_PERIOD = 7, +#endif /* RE_ENABLE_I18N */ + + /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used + when the debugger shows values of this enum type. */ +#define EPSILON_BIT 8 + OP_OPEN_SUBEXP = EPSILON_BIT | 0, + OP_CLOSE_SUBEXP = EPSILON_BIT | 1, + OP_ALT = EPSILON_BIT | 2, + OP_DUP_ASTERISK = EPSILON_BIT | 3, + ANCHOR = EPSILON_BIT | 4, + + /* Tree type, these are used only by tree. */ + CONCAT = 16, + SUBEXP = 17, + + /* Token type, these are used only by token. */ + OP_DUP_PLUS = 18, + OP_DUP_QUESTION, + OP_OPEN_BRACKET, + OP_CLOSE_BRACKET, + OP_CHARSET_RANGE, + OP_OPEN_DUP_NUM, + OP_CLOSE_DUP_NUM, + OP_NON_MATCH_LIST, + OP_OPEN_COLL_ELEM, + OP_CLOSE_COLL_ELEM, + OP_OPEN_EQUIV_CLASS, + OP_CLOSE_EQUIV_CLASS, + OP_OPEN_CHAR_CLASS, + OP_CLOSE_CHAR_CLASS, + OP_WORD, + OP_NOTWORD, + OP_SPACE, + OP_NOTSPACE, + BACK_SLASH + +} re_token_type_t; + +#ifdef RE_ENABLE_I18N +typedef struct +{ + /* Multibyte characters. */ + wchar_t *mbchars; + + /* Collating symbols. */ +# ifdef _LIBC + int32_t *coll_syms; +# endif + + /* Equivalence classes. */ +# ifdef _LIBC + int32_t *equiv_classes; +# endif + + /* Range expressions. */ +# ifdef _LIBC + uint32_t *range_starts; + uint32_t *range_ends; +# else /* not _LIBC */ + wchar_t *range_starts; + wchar_t *range_ends; +# endif /* not _LIBC */ + + /* Character classes. */ + wctype_t *char_classes; + + /* If this character set is the non-matching list. */ + unsigned int non_match : 1; + + /* # of multibyte characters. */ + Idx nmbchars; + + /* # of collating symbols. */ + Idx ncoll_syms; + + /* # of equivalence classes. */ + Idx nequiv_classes; + + /* # of range expressions. */ + Idx nranges; + + /* # of character classes. */ + Idx nchar_classes; +} re_charset_t; +#endif /* RE_ENABLE_I18N */ + +typedef struct +{ + union + { + unsigned char c; /* for CHARACTER */ + re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */ +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; /* for COMPLEX_BRACKET */ +#endif /* RE_ENABLE_I18N */ + Idx idx; /* for BACK_REF */ + re_context_type ctx_type; /* for ANCHOR */ + } opr; +#if __GNUC__ >= 2 + re_token_type_t type : 8; +#else + re_token_type_t type; +#endif + unsigned int constraint : 10; /* context constraint */ + unsigned int duplicated : 1; + unsigned int opt_subexp : 1; +#ifdef RE_ENABLE_I18N + unsigned int accept_mb : 1; + /* These 2 bits can be moved into the union if needed (e.g. if running out + of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */ + unsigned int mb_partial : 1; +#endif + unsigned int word_char : 1; +} re_token_t; + +#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT) + +struct re_string_t +{ + /* Indicate the raw buffer which is the original string passed as an + argument of regexec(), re_search(), etc.. */ + const unsigned char *raw_mbs; + /* Store the multibyte string. In case of "case insensitive mode" like + REG_ICASE, upper cases of the string are stored, otherwise MBS points + the same address that RAW_MBS points. */ + unsigned char *mbs; +#ifdef RE_ENABLE_I18N + /* Store the wide character string which is corresponding to MBS. */ + wint_t *wcs; + Idx *offsets; + mbstate_t cur_state; +#endif + /* Index in RAW_MBS. Each character mbs[i] corresponds to + raw_mbs[raw_mbs_idx + i]. */ + Idx raw_mbs_idx; + /* The length of the valid characters in the buffers. */ + Idx valid_len; + /* The corresponding number of bytes in raw_mbs array. */ + Idx valid_raw_len; + /* The length of the buffers MBS and WCS. */ + Idx bufs_len; + /* The index in MBS, which is updated by re_string_fetch_byte. */ + Idx cur_idx; + /* length of RAW_MBS array. */ + Idx raw_len; + /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */ + Idx len; + /* End of the buffer may be shorter than its length in the cases such + as re_match_2, re_search_2. Then, we use STOP for end of the buffer + instead of LEN. */ + Idx raw_stop; + /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */ + Idx stop; + + /* The context of mbs[0]. We store the context independently, since + the context of mbs[0] may be different from raw_mbs[0], which is + the beginning of the input string. */ + unsigned int tip_context; + /* The translation passed as a part of an argument of re_compile_pattern. */ + RE_TRANSLATE_TYPE trans; + /* Copy of re_dfa_t's word_char. */ + re_const_bitset_ptr_t word_char; + /* true if REG_ICASE. */ + unsigned char icase; + unsigned char is_utf8; + unsigned char map_notascii; + unsigned char mbs_allocated; + unsigned char offsets_needed; + unsigned char newline_anchor; + unsigned char word_ops_used; + int mb_cur_max; +}; +typedef struct re_string_t re_string_t; + + +struct re_dfa_t; +typedef struct re_dfa_t re_dfa_t; + +#ifndef _LIBC +# ifdef __i386__ +# define internal_function __attribute ((regparm (3), stdcall)) +# else +# define internal_function +# endif +#endif + +static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr, + Idx new_buf_len) + internal_function; +#ifdef RE_ENABLE_I18N +static void build_wcs_buffer (re_string_t *pstr) internal_function; +static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr) + internal_function; +#endif /* RE_ENABLE_I18N */ +static void build_upper_buffer (re_string_t *pstr) internal_function; +static void re_string_translate_buffer (re_string_t *pstr) internal_function; +static unsigned int re_string_context_at (const re_string_t *input, Idx idx, + int eflags) + internal_function __attribute ((pure)); +#define re_string_peek_byte(pstr, offset) \ + ((pstr)->mbs[(pstr)->cur_idx + offset]) +#define re_string_fetch_byte(pstr) \ + ((pstr)->mbs[(pstr)->cur_idx++]) +#define re_string_first_byte(pstr, idx) \ + ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF) +#define re_string_is_single_byte_char(pstr, idx) \ + ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \ + || (pstr)->wcs[(idx) + 1] != WEOF)) +#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx) +#define re_string_cur_idx(pstr) ((pstr)->cur_idx) +#define re_string_get_buffer(pstr) ((pstr)->mbs) +#define re_string_length(pstr) ((pstr)->len) +#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx]) +#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx)) +#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx)) + +#include + +#ifndef _LIBC +# if HAVE_ALLOCA +/* The OS usually guarantees only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + allocate anything larger than 4096 bytes. Also care for the possibility + of a few compiler-allocated temporary stack slots. */ +# define __libc_use_alloca(n) ((n) < 4032) +# else +/* alloca is implemented with malloc, so just use malloc. */ +# define __libc_use_alloca(n) 0 +# endif +#endif + +#ifndef MAX +# define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif + +#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t))) +#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t))) +#define re_free(p) free (p) + +struct bin_tree_t +{ + struct bin_tree_t *parent; + struct bin_tree_t *left; + struct bin_tree_t *right; + struct bin_tree_t *first; + struct bin_tree_t *next; + + re_token_t token; + + /* `node_idx' is the index in dfa->nodes, if `type' == 0. + Otherwise `type' indicate the type of this node. */ + Idx node_idx; +}; +typedef struct bin_tree_t bin_tree_t; + +#define BIN_TREE_STORAGE_SIZE \ + ((1024 - sizeof (void *)) / sizeof (bin_tree_t)) + +struct bin_tree_storage_t +{ + struct bin_tree_storage_t *next; + bin_tree_t data[BIN_TREE_STORAGE_SIZE]; +}; +typedef struct bin_tree_storage_t bin_tree_storage_t; + +#define CONTEXT_WORD 1 +#define CONTEXT_NEWLINE (CONTEXT_WORD << 1) +#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1) +#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1) + +#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD) +#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE) +#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF) +#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF) +#define IS_ORDINARY_CONTEXT(c) ((c) == 0) + +#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_') +#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR) +#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_') +#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR) + +#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \ + ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ + || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ + || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\ + || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context))) + +#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \ + ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ + || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ + || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \ + || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context))) + +struct re_dfastate_t +{ + re_hashval_t hash; + re_node_set nodes; + re_node_set non_eps_nodes; + re_node_set inveclosure; + re_node_set *entrance_nodes; + struct re_dfastate_t **trtable, **word_trtable; + unsigned int context : 4; + unsigned int halt : 1; + /* If this state can accept `multi byte'. + Note that we refer to multibyte characters, and multi character + collating elements as `multi byte'. */ + unsigned int accept_mb : 1; + /* If this state has backreference node(s). */ + unsigned int has_backref : 1; + unsigned int has_constraint : 1; +}; +typedef struct re_dfastate_t re_dfastate_t; + +struct re_state_table_entry +{ + Idx num; + Idx alloc; + re_dfastate_t **array; +}; + +/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */ + +typedef struct +{ + Idx next_idx; + Idx alloc; + re_dfastate_t **array; +} state_array_t; + +/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */ + +typedef struct +{ + Idx node; + Idx str_idx; /* The position NODE match at. */ + state_array_t path; +} re_sub_match_last_t; + +/* Store information about the node NODE whose type is OP_OPEN_SUBEXP. + And information about the node, whose type is OP_CLOSE_SUBEXP, + corresponding to NODE is stored in LASTS. */ + +typedef struct +{ + Idx str_idx; + Idx node; + state_array_t *path; + Idx alasts; /* Allocation size of LASTS. */ + Idx nlasts; /* The number of LASTS. */ + re_sub_match_last_t **lasts; +} re_sub_match_top_t; + +struct re_backref_cache_entry +{ + Idx node; + Idx str_idx; + Idx subexp_from; + Idx subexp_to; + char more; + char unused; + unsigned short int eps_reachable_subexps_map; +}; + +typedef struct +{ + /* The string object corresponding to the input string. */ + re_string_t input; +#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) + const re_dfa_t *const dfa; +#else + const re_dfa_t *dfa; +#endif + /* EFLAGS of the argument of regexec. */ + int eflags; + /* Where the matching ends. */ + Idx match_last; + Idx last_node; + /* The state log used by the matcher. */ + re_dfastate_t **state_log; + Idx state_log_top; + /* Back reference cache. */ + Idx nbkref_ents; + Idx abkref_ents; + struct re_backref_cache_entry *bkref_ents; + int max_mb_elem_len; + Idx nsub_tops; + Idx asub_tops; + re_sub_match_top_t **sub_tops; +} re_match_context_t; + +typedef struct +{ + re_dfastate_t **sifted_states; + re_dfastate_t **limited_states; + Idx last_node; + Idx last_str_idx; + re_node_set limits; +} re_sift_context_t; + +struct re_fail_stack_ent_t +{ + Idx idx; + Idx node; + regmatch_t *regs; + re_node_set eps_via_nodes; +}; + +struct re_fail_stack_t +{ + Idx num; + Idx alloc; + struct re_fail_stack_ent_t *stack; +}; + +struct re_dfa_t +{ + re_token_t *nodes; + size_t nodes_alloc; + size_t nodes_len; + Idx *nexts; + Idx *org_indices; + re_node_set *edests; + re_node_set *eclosures; + re_node_set *inveclosures; + struct re_state_table_entry *state_table; + re_dfastate_t *init_state; + re_dfastate_t *init_state_word; + re_dfastate_t *init_state_nl; + re_dfastate_t *init_state_begbuf; + bin_tree_t *str_tree; + bin_tree_storage_t *str_tree_storage; + re_bitset_ptr_t sb_char; + int str_tree_storage_idx; + + /* number of subexpressions `re_nsub' is in regex_t. */ + re_hashval_t state_hash_mask; + Idx init_node; + Idx nbackref; /* The number of backreference in this dfa. */ + + /* Bitmap expressing which backreference is used. */ + bitset_word_t used_bkref_map; + bitset_word_t completed_bkref_map; + + unsigned int has_plural_match : 1; + /* If this dfa has "multibyte node", which is a backreference or + a node which can accept multibyte character or multi character + collating element. */ + unsigned int has_mb_node : 1; + unsigned int is_utf8 : 1; + unsigned int map_notascii : 1; + unsigned int word_ops_used : 1; + int mb_cur_max; + bitset_t word_char; + reg_syntax_t syntax; + Idx *subexp_map; +#ifdef DEBUG + char* re_str; +#endif +#ifdef _LIBC + __libc_lock_define (, lock) +#endif +}; + +#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set)) +#define re_node_set_remove(set,id) \ + (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1)) +#define re_node_set_empty(p) ((p)->nelem = 0) +#define re_node_set_free(set) re_free ((set)->elems) + + +typedef enum +{ + SB_CHAR, + MB_CHAR, + EQUIV_CLASS, + COLL_SYM, + CHAR_CLASS +} bracket_elem_type; + +typedef struct +{ + bracket_elem_type type; + union + { + unsigned char ch; + unsigned char *name; + wchar_t wch; + } opr; +} bracket_elem_t; + + +/* Inline functions for bitset_t operation. */ + +static inline void +bitset_set (bitset_t set, Idx i) +{ + set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS; +} + +static inline void +bitset_clear (bitset_t set, Idx i) +{ + set[i / BITSET_WORD_BITS] &= ~ ((bitset_word_t) 1 << i % BITSET_WORD_BITS); +} + +static inline bool +bitset_contain (const bitset_t set, Idx i) +{ + return (set[i / BITSET_WORD_BITS] >> i % BITSET_WORD_BITS) & 1; +} + +static inline void +bitset_empty (bitset_t set) +{ + memset (set, '\0', sizeof (bitset_t)); +} + +static inline void +bitset_set_all (bitset_t set) +{ + memset (set, -1, sizeof (bitset_word_t) * (SBC_MAX / BITSET_WORD_BITS)); + if (SBC_MAX % BITSET_WORD_BITS != 0) + set[BITSET_WORDS - 1] = + ((bitset_word_t) 1 << SBC_MAX % BITSET_WORD_BITS) - 1; +} + +static inline void +bitset_copy (bitset_t dest, const bitset_t src) +{ + memcpy (dest, src, sizeof (bitset_t)); +} + +static inline void +bitset_not (bitset_t set) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < SBC_MAX / BITSET_WORD_BITS; ++bitset_i) + set[bitset_i] = ~set[bitset_i]; + if (SBC_MAX % BITSET_WORD_BITS != 0) + set[BITSET_WORDS - 1] = + ((((bitset_word_t) 1 << SBC_MAX % BITSET_WORD_BITS) - 1) + & ~set[BITSET_WORDS - 1]); +} + +static inline void +bitset_merge (bitset_t dest, const bitset_t src) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) + dest[bitset_i] |= src[bitset_i]; +} + +static inline void +bitset_mask (bitset_t dest, const bitset_t src) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) + dest[bitset_i] &= src[bitset_i]; +} + +#ifdef RE_ENABLE_I18N +/* Inline functions for re_string. */ +static inline int +internal_function __attribute ((pure)) +re_string_char_size_at (const re_string_t *pstr, Idx idx) +{ + int byte_idx; + if (pstr->mb_cur_max == 1) + return 1; + for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx) + if (pstr->wcs[idx + byte_idx] != WEOF) + break; + return byte_idx; +} + +static inline wint_t +internal_function __attribute ((pure)) +re_string_wchar_at (const re_string_t *pstr, Idx idx) +{ + if (pstr->mb_cur_max == 1) + return (wint_t) pstr->mbs[idx]; + return (wint_t) pstr->wcs[idx]; +} + +static int +internal_function __attribute ((pure)) +re_string_elem_size_at (const re_string_t *pstr, Idx idx) +{ +# ifdef _LIBC + const unsigned char *p, *extra; + const int32_t *table, *indirect; + int32_t tmp; +# include + uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + + if (nrules != 0) + { + table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_INDIRECTMB); + p = pstr->mbs + idx; + tmp = findidx (&p); + return p - pstr->mbs - idx; + } + else +# endif /* _LIBC */ + return 1; +} +#endif /* RE_ENABLE_I18N */ + +#endif /* _REGEX_INTERNAL_H */ diff --git a/src/bin/m4/lib/regexec.c b/src/bin/m4/lib/regexec.c new file mode 100644 index 0000000000..84c3aed634 --- /dev/null +++ b/src/bin/m4/lib/regexec.c @@ -0,0 +1,4398 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, + Idx n) internal_function; +static void match_ctx_clean (re_match_context_t *mctx) internal_function; +static void match_ctx_free (re_match_context_t *cache) internal_function; +static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, Idx node, + Idx str_idx, Idx from, Idx to) + internal_function; +static Idx search_cur_bkref_entry (const re_match_context_t *mctx, Idx str_idx) + internal_function; +static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, Idx node, + Idx str_idx) internal_function; +static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop, + Idx node, Idx str_idx) + internal_function; +static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, + re_dfastate_t **limited_sts, Idx last_node, + Idx last_str_idx) + internal_function; +static reg_errcode_t re_search_internal (const regex_t *preg, + const char *string, Idx length, + Idx start, Idx last_start, Idx stop, + size_t nmatch, regmatch_t pmatch[], + int eflags) internal_function; +static regoff_t re_search_2_stub (struct re_pattern_buffer *bufp, + const char *string1, Idx length1, + const char *string2, Idx length2, + Idx start, regoff_t range, + struct re_registers *regs, + Idx stop, bool ret_len) internal_function; +static regoff_t re_search_stub (struct re_pattern_buffer *bufp, + const char *string, Idx length, Idx start, + regoff_t range, Idx stop, + struct re_registers *regs, + bool ret_len) internal_function; +static unsigned int re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, + Idx nregs, int regs_allocated) + internal_function; +static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx) + internal_function; +static Idx check_matching (re_match_context_t *mctx, bool fl_longest_match, + Idx *p_match_first) internal_function; +static Idx check_halt_state_context (const re_match_context_t *mctx, + const re_dfastate_t *state, Idx idx) + internal_function; +static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, + regmatch_t *prev_idx_match, Idx cur_node, + Idx cur_idx, Idx nmatch) internal_function; +static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs, + Idx str_idx, Idx dest_node, Idx nregs, + regmatch_t *regs, + re_node_set *eps_via_nodes) + internal_function; +static reg_errcode_t set_regs (const regex_t *preg, + const re_match_context_t *mctx, + size_t nmatch, regmatch_t *pmatch, + bool fl_backtrack) internal_function; +static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs) + internal_function; + +#ifdef RE_ENABLE_I18N +static int sift_states_iter_mb (const re_match_context_t *mctx, + re_sift_context_t *sctx, + Idx node_idx, Idx str_idx, Idx max_str_idx) + internal_function; +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t sift_states_backward (const re_match_context_t *mctx, + re_sift_context_t *sctx) + internal_function; +static reg_errcode_t build_sifted_states (const re_match_context_t *mctx, + re_sift_context_t *sctx, Idx str_idx, + re_node_set *cur_dest) + internal_function; +static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx, + re_sift_context_t *sctx, + Idx str_idx, + re_node_set *dest_nodes) + internal_function; +static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates) + internal_function; +static bool check_dst_limits (const re_match_context_t *mctx, + const re_node_set *limits, + Idx dst_node, Idx dst_idx, Idx src_node, + Idx src_idx) internal_function; +static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, + int boundaries, Idx subexp_idx, + Idx from_node, Idx bkref_idx) + internal_function; +static int check_dst_limits_calc_pos (const re_match_context_t *mctx, + Idx limit, Idx subexp_idx, + Idx node, Idx str_idx, + Idx bkref_idx) internal_function; +static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates, + re_node_set *limits, + struct re_backref_cache_entry *bkref_ents, + Idx str_idx) internal_function; +static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx, + re_sift_context_t *sctx, + Idx str_idx, const re_node_set *candidates) + internal_function; +static reg_errcode_t merge_state_array (const re_dfa_t *dfa, + re_dfastate_t **dst, + re_dfastate_t **src, Idx num) + internal_function; +static re_dfastate_t *find_recover_state (reg_errcode_t *err, + re_match_context_t *mctx) internal_function; +static re_dfastate_t *transit_state (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *state) internal_function; +static re_dfastate_t *merge_state_with_log (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *next_state) + internal_function; +static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx, + re_node_set *cur_nodes, + Idx str_idx) internal_function; +#if 0 +static re_dfastate_t *transit_state_sb (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *pstate) + internal_function; +#endif +#ifdef RE_ENABLE_I18N +static reg_errcode_t transit_state_mb (re_match_context_t *mctx, + re_dfastate_t *pstate) + internal_function; +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t transit_state_bkref (re_match_context_t *mctx, + const re_node_set *nodes) + internal_function; +static reg_errcode_t get_subexp (re_match_context_t *mctx, + Idx bkref_node, Idx bkref_str_idx) + internal_function; +static reg_errcode_t get_subexp_sub (re_match_context_t *mctx, + const re_sub_match_top_t *sub_top, + re_sub_match_last_t *sub_last, + Idx bkref_node, Idx bkref_str) + internal_function; +static Idx find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, + Idx subexp_idx, int type) internal_function; +static reg_errcode_t check_arrival (re_match_context_t *mctx, + state_array_t *path, Idx top_node, + Idx top_str, Idx last_node, Idx last_str, + int type) internal_function; +static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx, + Idx str_idx, + re_node_set *cur_nodes, + re_node_set *next_nodes) + internal_function; +static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa, + re_node_set *cur_nodes, + Idx ex_subexp, int type) + internal_function; +static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa, + re_node_set *dst_nodes, + Idx target, Idx ex_subexp, + int type) internal_function; +static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx, + re_node_set *cur_nodes, Idx cur_str, + Idx subexp_num, int type) + internal_function; +static bool build_trtable (const re_dfa_t *dfa, + re_dfastate_t *state) internal_function; +#ifdef RE_ENABLE_I18N +static int check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx, + const re_string_t *input, Idx idx) + internal_function; +# ifdef _LIBC +static unsigned int find_collation_sequence_value (const unsigned char *mbs, + size_t name_len) + internal_function; +# endif /* _LIBC */ +#endif /* RE_ENABLE_I18N */ +static Idx group_nodes_into_DFAstates (const re_dfa_t *dfa, + const re_dfastate_t *state, + re_node_set *states_node, + bitset_t *states_ch) internal_function; +static bool check_node_accept (const re_match_context_t *mctx, + const re_token_t *node, Idx idx) + internal_function; +static reg_errcode_t extend_buffers (re_match_context_t *mctx) + internal_function; + +/* Entry point for POSIX code. */ + +/* regexec searches for a given pattern, specified by PREG, in the + string STRING. + + If NMATCH is zero or REG_NOSUB was set in the cflags argument to + `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at + least NMATCH elements, and we set them to the offsets of the + corresponding matched substrings. + + EFLAGS specifies `execution flags' which affect matching: if + REG_NOTBOL is set, then ^ does not match at the beginning of the + string; if REG_NOTEOL is set, then $ does not match at the end. + + We return 0 if we find a match and REG_NOMATCH if not. */ + +int +regexec (preg, string, nmatch, pmatch, eflags) + const regex_t *__restrict preg; + const char *__restrict string; + size_t nmatch; + regmatch_t pmatch[]; + int eflags; +{ + reg_errcode_t err; + Idx start, length; +#ifdef _LIBC + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; +#endif + + if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND)) + return REG_BADPAT; + + if (eflags & REG_STARTEND) + { + start = pmatch[0].rm_so; + length = pmatch[0].rm_eo; + } + else + { + start = 0; + length = strlen (string); + } + + __libc_lock_lock (dfa->lock); + if (preg->no_sub) + err = re_search_internal (preg, string, length, start, length, + length, 0, NULL, eflags); + else + err = re_search_internal (preg, string, length, start, length, + length, nmatch, pmatch, eflags); + __libc_lock_unlock (dfa->lock); + return err != REG_NOERROR; +} + +#ifdef _LIBC +# include +versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4); + +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4) +__typeof__ (__regexec) __compat_regexec; + +int +attribute_compat_text_section +__compat_regexec (const regex_t *__restrict preg, + const char *__restrict string, size_t nmatch, + regmatch_t pmatch[], int eflags) +{ + return regexec (preg, string, nmatch, pmatch, + eflags & (REG_NOTBOL | REG_NOTEOL)); +} +compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0); +# endif +#endif + +/* Entry points for GNU code. */ + +/* re_match, re_search, re_match_2, re_search_2 + + The former two functions operate on STRING with length LENGTH, + while the later two operate on concatenation of STRING1 and STRING2 + with lengths LENGTH1 and LENGTH2, respectively. + + re_match() matches the compiled pattern in BUFP against the string, + starting at index START. + + re_search() first tries matching at index START, then it tries to match + starting from index START + 1, and so on. The last start position tried + is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same + way as re_match().) + + The parameter STOP of re_{match,search}_2 specifies that no match exceeding + the first STOP characters of the concatenation of the strings should be + concerned. + + If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match + and all groups is stored in REGS. (For the "_2" variants, the offsets are + computed relative to the concatenation, not relative to the individual + strings.) + + On success, re_match* functions return the length of the match, re_search* + return the position of the start of the match. Return value -1 means no + match was found and -2 indicates an internal error. */ + +regoff_t +re_match (bufp, string, length, start, regs) + struct re_pattern_buffer *bufp; + const char *string; + Idx length, start; + struct re_registers *regs; +{ + return re_search_stub (bufp, string, length, start, 0, length, regs, true); +} +#ifdef _LIBC +weak_alias (__re_match, re_match) +#endif + +regoff_t +re_search (bufp, string, length, start, range, regs) + struct re_pattern_buffer *bufp; + const char *string; + Idx length, start; + regoff_t range; + struct re_registers *regs; +{ + return re_search_stub (bufp, string, length, start, range, length, regs, + false); +} +#ifdef _LIBC +weak_alias (__re_search, re_search) +#endif + +regoff_t +re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + Idx length1, length2, start, stop; + struct re_registers *regs; +{ + return re_search_2_stub (bufp, string1, length1, string2, length2, + start, 0, regs, stop, true); +} +#ifdef _LIBC +weak_alias (__re_match_2, re_match_2) +#endif + +regoff_t +re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + Idx length1, length2, start, stop; + regoff_t range; + struct re_registers *regs; +{ + return re_search_2_stub (bufp, string1, length1, string2, length2, + start, range, regs, stop, false); +} +#ifdef _LIBC +weak_alias (__re_search_2, re_search_2) +#endif + +static regoff_t +internal_function +re_search_2_stub (struct re_pattern_buffer *bufp, + const char *string1, Idx length1, + const char *string2, Idx length2, + Idx start, regoff_t range, struct re_registers *regs, + Idx stop, bool ret_len) +{ + const char *str; + regoff_t rval; + Idx len = length1 + length2; + char *s = NULL; + + if (BE (length1 < 0 || length2 < 0 || stop < 0 || len < length1, 0)) + return -2; + + /* Concatenate the strings. */ + if (length2 > 0) + if (length1 > 0) + { + s = re_malloc (char, len); + + if (BE (s == NULL, 0)) + return -2; +#ifdef _LIBC + memcpy (__mempcpy (s, string1, length1), string2, length2); +#else + memcpy (s, string1, length1); + memcpy (s + length1, string2, length2); +#endif + str = s; + } + else + str = string2; + else + str = string1; + + rval = re_search_stub (bufp, str, len, start, range, stop, regs, + ret_len); + re_free (s); + return rval; +} + +/* The parameters have the same meaning as those of re_search. + Additional parameters: + If RET_LEN is true the length of the match is returned (re_match style); + otherwise the position of the match is returned. */ + +static regoff_t +internal_function +re_search_stub (struct re_pattern_buffer *bufp, + const char *string, Idx length, + Idx start, regoff_t range, Idx stop, struct re_registers *regs, + bool ret_len) +{ + reg_errcode_t result; + regmatch_t *pmatch; + Idx nregs; + regoff_t rval; + int eflags = 0; +#ifdef _LIBC + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; +#endif + Idx last_start = start + range; + + /* Check for out-of-range. */ + if (BE (start < 0 || start > length, 0)) + return -1; + if (BE (length < last_start || (0 <= range && last_start < start), 0)) + last_start = length; + else if (BE (last_start < 0 || (range < 0 && start <= last_start), 0)) + last_start = 0; + + __libc_lock_lock (dfa->lock); + + eflags |= (bufp->not_bol) ? REG_NOTBOL : 0; + eflags |= (bufp->not_eol) ? REG_NOTEOL : 0; + + /* Compile fastmap if we haven't yet. */ + if (start < last_start && bufp->fastmap != NULL && !bufp->fastmap_accurate) + re_compile_fastmap (bufp); + + if (BE (bufp->no_sub, 0)) + regs = NULL; + + /* We need at least 1 register. */ + if (regs == NULL) + nregs = 1; + else if (BE (bufp->regs_allocated == REGS_FIXED + && regs->num_regs <= bufp->re_nsub, 0)) + { + nregs = regs->num_regs; + if (BE (nregs < 1, 0)) + { + /* Nothing can be copied to regs. */ + regs = NULL; + nregs = 1; + } + } + else + nregs = bufp->re_nsub + 1; + pmatch = re_malloc (regmatch_t, nregs); + if (BE (pmatch == NULL, 0)) + { + rval = -2; + goto out; + } + + result = re_search_internal (bufp, string, length, start, last_start, stop, + nregs, pmatch, eflags); + + rval = 0; + + /* I hope we needn't fill ther regs with -1's when no match was found. */ + if (result != REG_NOERROR) + rval = -1; + else if (regs != NULL) + { + /* If caller wants register contents data back, copy them. */ + bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs, + bufp->regs_allocated); + if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0)) + rval = -2; + } + + if (BE (rval == 0, 1)) + { + if (ret_len) + { + assert (pmatch[0].rm_so == start); + rval = pmatch[0].rm_eo - start; + } + else + rval = pmatch[0].rm_so; + } + re_free (pmatch); + out: + __libc_lock_unlock (dfa->lock); + return rval; +} + +static unsigned int +internal_function +re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, Idx nregs, + int regs_allocated) +{ + int rval = REGS_REALLOCATE; + Idx i; + Idx need_regs = nregs + 1; + /* We need one extra element beyond `num_regs' for the `-1' marker GNU code + uses. */ + + /* Have the register data arrays been allocated? */ + if (regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. */ + regs->start = re_malloc (regoff_t, need_regs); + if (BE (regs->start == NULL, 0)) + return REGS_UNALLOCATED; + regs->end = re_malloc (regoff_t, need_regs); + if (BE (regs->end == NULL, 0)) + { + re_free (regs->start); + return REGS_UNALLOCATED; + } + regs->num_regs = need_regs; + } + else if (regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (BE (need_regs > regs->num_regs, 0)) + { + regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs); + regoff_t *new_end; + if (BE (new_start == NULL, 0)) + return REGS_UNALLOCATED; + new_end = re_realloc (regs->end, regoff_t, need_regs); + if (BE (new_end == NULL, 0)) + { + re_free (new_start); + return REGS_UNALLOCATED; + } + regs->start = new_start; + regs->end = new_end; + regs->num_regs = need_regs; + } + } + else + { + assert (regs_allocated == REGS_FIXED); + /* This function may not be called with REGS_FIXED and nregs too big. */ + assert (regs->num_regs >= nregs); + rval = REGS_FIXED; + } + + /* Copy the regs. */ + for (i = 0; i < nregs; ++i) + { + regs->start[i] = pmatch[i].rm_so; + regs->end[i] = pmatch[i].rm_eo; + } + for ( ; i < regs->num_regs; ++i) + regs->start[i] = regs->end[i] = -1; + + return rval; +} + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use + this memory for recording register information. STARTS and ENDS + must be allocated using the malloc library routine, and must each + be at least NUM_REGS * sizeof (regoff_t) bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ + +void +re_set_registers (bufp, regs, num_regs, starts, ends) + struct re_pattern_buffer *bufp; + struct re_registers *regs; + __re_size_t num_regs; + regoff_t *starts, *ends; +{ + if (num_regs) + { + bufp->regs_allocated = REGS_REALLOCATE; + regs->num_regs = num_regs; + regs->start = starts; + regs->end = ends; + } + else + { + bufp->regs_allocated = REGS_UNALLOCATED; + regs->num_regs = 0; + regs->start = regs->end = NULL; + } +} +#ifdef _LIBC +weak_alias (__re_set_registers, re_set_registers) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC +int +# ifdef _LIBC +weak_function +# endif +re_exec (s) + const char *s; +{ + return 0 == regexec (&re_comp_buf, s, 0, NULL, 0); +} +#endif /* _REGEX_RE_COMP */ + +/* Internal entry point. */ + +/* Searches for a compiled pattern PREG in the string STRING, whose + length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same + meaning as with regexec. LAST_START is START + RANGE, where + START and RANGE have the same meaning as with re_search. + Return REG_NOERROR if we find a match, and REG_NOMATCH if not, + otherwise return the error code. + Note: We assume front end functions already check ranges. + (0 <= LAST_START && LAST_START <= LENGTH) */ + +static reg_errcode_t +internal_function +re_search_internal (const regex_t *preg, + const char *string, Idx length, + Idx start, Idx last_start, Idx stop, + size_t nmatch, regmatch_t pmatch[], + int eflags) +{ + reg_errcode_t err; + const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer; + Idx left_lim, right_lim; + int incr; + bool fl_longest_match; + int match_kind; + Idx match_first; + Idx match_last = REG_MISSING; + Idx extra_nmatch; + bool sb; + int ch; +#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) + re_match_context_t mctx = { .dfa = dfa }; +#else + re_match_context_t mctx; +#endif + char *fastmap = ((preg->fastmap != NULL && preg->fastmap_accurate + && start != last_start && !preg->can_be_null) + ? preg->fastmap : NULL); + RE_TRANSLATE_TYPE t = preg->translate; + +#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)) + memset (&mctx, '\0', sizeof (re_match_context_t)); + mctx.dfa = dfa; +#endif + + extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0; + nmatch -= extra_nmatch; + + /* Check if the DFA haven't been compiled. */ + if (BE (preg->used == 0 || dfa->init_state == NULL + || dfa->init_state_word == NULL || dfa->init_state_nl == NULL + || dfa->init_state_begbuf == NULL, 0)) + return REG_NOMATCH; + +#ifdef DEBUG + /* We assume front-end functions already check them. */ + assert (0 <= last_start && last_start <= length); +#endif + + /* If initial states with non-begbuf contexts have no elements, + the regex must be anchored. If preg->newline_anchor is set, + we'll never use init_state_nl, so do not check it. */ + if (dfa->init_state->nodes.nelem == 0 + && dfa->init_state_word->nodes.nelem == 0 + && (dfa->init_state_nl->nodes.nelem == 0 + || !preg->newline_anchor)) + { + if (start != 0 && last_start != 0) + return REG_NOMATCH; + start = last_start = 0; + } + + /* We must check the longest matching, if nmatch > 0. */ + fl_longest_match = (nmatch != 0 || dfa->nbackref); + + err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1, + preg->translate, preg->syntax & RE_ICASE, dfa); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + mctx.input.stop = stop; + mctx.input.raw_stop = stop; + mctx.input.newline_anchor = preg->newline_anchor; + + err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* We will log all the DFA states through which the dfa pass, + if nmatch > 1, or this dfa has "multibyte node", which is a + back-reference or a node which can accept multibyte character or + multi character collating element. */ + if (nmatch > 1 || dfa->has_mb_node) + { + /* Avoid overflow. */ + if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= mctx.input.bufs_len, 0)) + { + err = REG_ESPACE; + goto free_return; + } + + mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1); + if (BE (mctx.state_log == NULL, 0)) + { + err = REG_ESPACE; + goto free_return; + } + } + else + mctx.state_log = NULL; + + match_first = start; + mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF + : CONTEXT_NEWLINE | CONTEXT_BEGBUF; + + /* Check incrementally whether of not the input string match. */ + incr = (last_start < start) ? -1 : 1; + left_lim = (last_start < start) ? last_start : start; + right_lim = (last_start < start) ? start : last_start; + sb = dfa->mb_cur_max == 1; + match_kind = + (fastmap + ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0) + | (start <= last_start ? 2 : 0) + | (t != NULL ? 1 : 0)) + : 8); + + for (;; match_first += incr) + { + err = REG_NOMATCH; + if (match_first < left_lim || right_lim < match_first) + goto free_return; + + /* Advance as rapidly as possible through the string, until we + find a plausible place to start matching. This may be done + with varying efficiency, so there are various possibilities: + only the most common of them are specialized, in order to + save on code size. We use a switch statement for speed. */ + switch (match_kind) + { + case 8: + /* No fastmap. */ + break; + + case 7: + /* Fastmap with single-byte translation, match forward. */ + while (BE (match_first < right_lim, 1) + && !fastmap[t[(unsigned char) string[match_first]]]) + ++match_first; + goto forward_match_found_start_or_reached_end; + + case 6: + /* Fastmap without translation, match forward. */ + while (BE (match_first < right_lim, 1) + && !fastmap[(unsigned char) string[match_first]]) + ++match_first; + + forward_match_found_start_or_reached_end: + if (BE (match_first == right_lim, 0)) + { + ch = match_first >= length + ? 0 : (unsigned char) string[match_first]; + if (!fastmap[t ? t[ch] : ch]) + goto free_return; + } + break; + + case 4: + case 5: + /* Fastmap without multi-byte translation, match backwards. */ + while (match_first >= left_lim) + { + ch = match_first >= length + ? 0 : (unsigned char) string[match_first]; + if (fastmap[t ? t[ch] : ch]) + break; + --match_first; + } + if (match_first < left_lim) + goto free_return; + break; + + default: + /* In this case, we can't determine easily the current byte, + since it might be a component byte of a multibyte + character. Then we use the constructed buffer instead. */ + for (;;) + { + /* If MATCH_FIRST is out of the valid range, reconstruct the + buffers. */ + __re_size_t offset = match_first - mctx.input.raw_mbs_idx; + if (BE (offset >= (__re_size_t) mctx.input.valid_raw_len, 0)) + { + err = re_string_reconstruct (&mctx.input, match_first, + eflags); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + offset = match_first - mctx.input.raw_mbs_idx; + } + /* If MATCH_FIRST is out of the buffer, leave it as '\0'. + Note that MATCH_FIRST must not be smaller than 0. */ + ch = (match_first >= length + ? 0 : re_string_byte_at (&mctx.input, offset)); + if (fastmap[ch]) + break; + match_first += incr; + if (match_first < left_lim || match_first > right_lim) + { + err = REG_NOMATCH; + goto free_return; + } + } + break; + } + + /* Reconstruct the buffers so that the matcher can assume that + the matching starts from the beginning of the buffer. */ + err = re_string_reconstruct (&mctx.input, match_first, eflags); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + +#ifdef RE_ENABLE_I18N + /* Don't consider this char as a possible match start if it part, + yet isn't the head, of a multibyte character. */ + if (!sb && !re_string_first_byte (&mctx.input, 0)) + continue; +#endif + + /* It seems to be appropriate one, then use the matcher. */ + /* We assume that the matching starts from 0. */ + mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0; + match_last = check_matching (&mctx, fl_longest_match, + start <= last_start ? &match_first : NULL); + if (match_last != REG_MISSING) + { + if (BE (match_last == REG_ERROR, 0)) + { + err = REG_ESPACE; + goto free_return; + } + else + { + mctx.match_last = match_last; + if ((!preg->no_sub && nmatch > 1) || dfa->nbackref) + { + re_dfastate_t *pstate = mctx.state_log[match_last]; + mctx.last_node = check_halt_state_context (&mctx, pstate, + match_last); + } + if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match) + || dfa->nbackref) + { + err = prune_impossible_nodes (&mctx); + if (err == REG_NOERROR) + break; + if (BE (err != REG_NOMATCH, 0)) + goto free_return; + match_last = REG_MISSING; + } + else + break; /* We found a match. */ + } + } + + match_ctx_clean (&mctx); + } + +#ifdef DEBUG + assert (match_last != REG_MISSING); + assert (err == REG_NOERROR); +#endif + + /* Set pmatch[] if we need. */ + if (nmatch > 0) + { + Idx reg_idx; + + /* Initialize registers. */ + for (reg_idx = 1; reg_idx < nmatch; ++reg_idx) + pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1; + + /* Set the points where matching start/end. */ + pmatch[0].rm_so = 0; + pmatch[0].rm_eo = mctx.match_last; + /* FIXME: This function should fail if mctx.match_last exceeds + the maximum possible regoff_t value. We need a new error + code REG_OVERFLOW. */ + + if (!preg->no_sub && nmatch > 1) + { + err = set_regs (preg, &mctx, nmatch, pmatch, + dfa->has_plural_match && dfa->nbackref > 0); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + + /* At last, add the offset to the each registers, since we slided + the buffers so that we could assume that the matching starts + from 0. */ + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + if (pmatch[reg_idx].rm_so != -1) + { +#ifdef RE_ENABLE_I18N + if (BE (mctx.input.offsets_needed != 0, 0)) + { + pmatch[reg_idx].rm_so = + (pmatch[reg_idx].rm_so == mctx.input.valid_len + ? mctx.input.valid_raw_len + : mctx.input.offsets[pmatch[reg_idx].rm_so]); + pmatch[reg_idx].rm_eo = + (pmatch[reg_idx].rm_eo == mctx.input.valid_len + ? mctx.input.valid_raw_len + : mctx.input.offsets[pmatch[reg_idx].rm_eo]); + } +#else + assert (mctx.input.offsets_needed == 0); +#endif + pmatch[reg_idx].rm_so += match_first; + pmatch[reg_idx].rm_eo += match_first; + } + for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx) + { + pmatch[nmatch + reg_idx].rm_so = -1; + pmatch[nmatch + reg_idx].rm_eo = -1; + } + + if (dfa->subexp_map) + for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++) + if (dfa->subexp_map[reg_idx] != reg_idx) + { + pmatch[reg_idx + 1].rm_so + = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so; + pmatch[reg_idx + 1].rm_eo + = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo; + } + } + + free_return: + re_free (mctx.state_log); + if (dfa->nbackref) + match_ctx_free (&mctx); + re_string_destruct (&mctx.input); + return err; +} + +static reg_errcode_t +internal_function +prune_impossible_nodes (re_match_context_t *mctx) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx halt_node, match_last; + reg_errcode_t ret; + re_dfastate_t **sifted_states; + re_dfastate_t **lim_states = NULL; + re_sift_context_t sctx; +#ifdef DEBUG + assert (mctx->state_log != NULL); +#endif + match_last = mctx->match_last; + halt_node = mctx->last_node; + + /* Avoid overflow. */ + if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= match_last, 0)) + return REG_ESPACE; + + sifted_states = re_malloc (re_dfastate_t *, match_last + 1); + if (BE (sifted_states == NULL, 0)) + { + ret = REG_ESPACE; + goto free_return; + } + if (dfa->nbackref) + { + lim_states = re_malloc (re_dfastate_t *, match_last + 1); + if (BE (lim_states == NULL, 0)) + { + ret = REG_ESPACE; + goto free_return; + } + while (1) + { + memset (lim_states, '\0', + sizeof (re_dfastate_t *) * (match_last + 1)); + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, + match_last); + ret = sift_states_backward (mctx, &sctx); + re_node_set_free (&sctx.limits); + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + if (sifted_states[0] != NULL || lim_states[0] != NULL) + break; + do + { + --match_last; + if (! REG_VALID_INDEX (match_last)) + { + ret = REG_NOMATCH; + goto free_return; + } + } while (mctx->state_log[match_last] == NULL + || !mctx->state_log[match_last]->halt); + halt_node = check_halt_state_context (mctx, + mctx->state_log[match_last], + match_last); + } + ret = merge_state_array (dfa, sifted_states, lim_states, + match_last + 1); + re_free (lim_states); + lim_states = NULL; + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + } + else + { + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last); + ret = sift_states_backward (mctx, &sctx); + re_node_set_free (&sctx.limits); + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + } + re_free (mctx->state_log); + mctx->state_log = sifted_states; + sifted_states = NULL; + mctx->last_node = halt_node; + mctx->match_last = match_last; + ret = REG_NOERROR; + free_return: + re_free (sifted_states); + re_free (lim_states); + return ret; +} + +/* Acquire an initial state and return it. + We must select appropriate initial state depending on the context, + since initial states may have constraints like "\<", "^", etc.. */ + +static inline re_dfastate_t * +__attribute ((always_inline)) internal_function +acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx, + Idx idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + if (dfa->init_state->has_constraint) + { + unsigned int context; + context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags); + if (IS_WORD_CONTEXT (context)) + return dfa->init_state_word; + else if (IS_ORDINARY_CONTEXT (context)) + return dfa->init_state; + else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context)) + return dfa->init_state_begbuf; + else if (IS_NEWLINE_CONTEXT (context)) + return dfa->init_state_nl; + else if (IS_BEGBUF_CONTEXT (context)) + { + /* It is relatively rare case, then calculate on demand. */ + return re_acquire_state_context (err, dfa, + dfa->init_state->entrance_nodes, + context); + } + else + /* Must not happen? */ + return dfa->init_state; + } + else + return dfa->init_state; +} + +/* Check whether the regular expression match input string INPUT or not, + and return the index where the matching end. Return REG_MISSING if + there is no match, and return REG_ERROR in case of an error. + FL_LONGEST_MATCH means we want the POSIX longest matching. + If P_MATCH_FIRST is not NULL, and the match fails, it is set to the + next place where we may want to try matching. + Note that the matcher assume that the maching starts from the current + index of the buffer. */ + +static Idx +internal_function +check_matching (re_match_context_t *mctx, bool fl_longest_match, + Idx *p_match_first) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx match = 0; + Idx match_last = REG_MISSING; + Idx cur_str_idx = re_string_cur_idx (&mctx->input); + re_dfastate_t *cur_state; + bool at_init_state = p_match_first != NULL; + Idx next_start_idx = cur_str_idx; + + err = REG_NOERROR; + cur_state = acquire_init_state_context (&err, mctx, cur_str_idx); + /* An initial state must not be NULL (invalid). */ + if (BE (cur_state == NULL, 0)) + { + assert (err == REG_ESPACE); + return REG_ERROR; + } + + if (mctx->state_log != NULL) + { + mctx->state_log[cur_str_idx] = cur_state; + + /* Check OP_OPEN_SUBEXP in the initial state in case that we use them + later. E.g. Processing back references. */ + if (BE (dfa->nbackref, 0)) + { + at_init_state = false; + err = check_subexp_matching_top (mctx, &cur_state->nodes, 0); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (cur_state->has_backref) + { + err = transit_state_bkref (mctx, &cur_state->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + } + + /* If the RE accepts NULL string. */ + if (BE (cur_state->halt, 0)) + { + if (!cur_state->has_constraint + || check_halt_state_context (mctx, cur_state, cur_str_idx)) + { + if (!fl_longest_match) + return cur_str_idx; + else + { + match_last = cur_str_idx; + match = 1; + } + } + } + + while (!re_string_eoi (&mctx->input)) + { + re_dfastate_t *old_state = cur_state; + Idx next_char_idx = re_string_cur_idx (&mctx->input) + 1; + + if (BE (next_char_idx >= mctx->input.bufs_len, 0) + || (BE (next_char_idx >= mctx->input.valid_len, 0) + && mctx->input.valid_len < mctx->input.len)) + { + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + { + assert (err == REG_ESPACE); + return REG_ERROR; + } + } + + cur_state = transit_state (&err, mctx, cur_state); + if (mctx->state_log != NULL) + cur_state = merge_state_with_log (&err, mctx, cur_state); + + if (cur_state == NULL) + { + /* Reached the invalid state or an error. Try to recover a valid + state using the state log, if available and if we have not + already found a valid (even if not the longest) match. */ + if (BE (err != REG_NOERROR, 0)) + return REG_ERROR; + + if (mctx->state_log == NULL + || (match && !fl_longest_match) + || (cur_state = find_recover_state (&err, mctx)) == NULL) + break; + } + + if (BE (at_init_state, 0)) + { + if (old_state == cur_state) + next_start_idx = next_char_idx; + else + at_init_state = false; + } + + if (cur_state->halt) + { + /* Reached a halt state. + Check the halt state can satisfy the current context. */ + if (!cur_state->has_constraint + || check_halt_state_context (mctx, cur_state, + re_string_cur_idx (&mctx->input))) + { + /* We found an appropriate halt state. */ + match_last = re_string_cur_idx (&mctx->input); + match = 1; + + /* We found a match, do not modify match_first below. */ + p_match_first = NULL; + if (!fl_longest_match) + break; + } + } + } + + if (p_match_first) + *p_match_first += next_start_idx; + + return match_last; +} + +/* Check NODE match the current context. */ + +static bool +internal_function +check_halt_node_context (const re_dfa_t *dfa, Idx node, unsigned int context) +{ + re_token_type_t type = dfa->nodes[node].type; + unsigned int constraint = dfa->nodes[node].constraint; + if (type != END_OF_RE) + return false; + if (!constraint) + return true; + if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context)) + return false; + return true; +} + +/* Check the halt state STATE match the current context. + Return 0 if not match, if the node, STATE has, is a halt node and + match the context, return the node. */ + +static Idx +internal_function +check_halt_state_context (const re_match_context_t *mctx, + const re_dfastate_t *state, Idx idx) +{ + Idx i; + unsigned int context; +#ifdef DEBUG + assert (state->halt); +#endif + context = re_string_context_at (&mctx->input, idx, mctx->eflags); + for (i = 0; i < state->nodes.nelem; ++i) + if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context)) + return state->nodes.elems[i]; + return 0; +} + +/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA + corresponding to the DFA). + Return the destination node, and update EPS_VIA_NODES; + return REG_MISSING in case of errors. */ + +static Idx +internal_function +proceed_next_node (const re_match_context_t *mctx, Idx nregs, regmatch_t *regs, + Idx *pidx, Idx node, re_node_set *eps_via_nodes, + struct re_fail_stack_t *fs) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx i; + bool ok; + if (IS_EPSILON_NODE (dfa->nodes[node].type)) + { + re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes; + re_node_set *edests = &dfa->edests[node]; + Idx dest_node; + ok = re_node_set_insert (eps_via_nodes, node); + if (BE (! ok, 0)) + return REG_ERROR; + /* Pick up a valid destination, or return REG_MISSING if none + is found. */ + for (dest_node = REG_MISSING, i = 0; i < edests->nelem; ++i) + { + Idx candidate = edests->elems[i]; + if (!re_node_set_contains (cur_nodes, candidate)) + continue; + if (dest_node == REG_MISSING) + dest_node = candidate; + + else + { + /* In order to avoid infinite loop like "(a*)*", return the second + epsilon-transition if the first was already considered. */ + if (re_node_set_contains (eps_via_nodes, dest_node)) + return candidate; + + /* Otherwise, push the second epsilon-transition on the fail stack. */ + else if (fs != NULL + && push_fail_stack (fs, *pidx, candidate, nregs, regs, + eps_via_nodes)) + return REG_ERROR; + + /* We know we are going to exit. */ + break; + } + } + return dest_node; + } + else + { + Idx naccepted = 0; + re_token_type_t type = dfa->nodes[node].type; + +#ifdef RE_ENABLE_I18N + if (dfa->nodes[node].accept_mb) + naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx); + else +#endif /* RE_ENABLE_I18N */ + if (type == OP_BACK_REF) + { + Idx subexp_idx = dfa->nodes[node].opr.idx + 1; + naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so; + if (fs != NULL) + { + if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1) + return REG_MISSING; + else if (naccepted) + { + char *buf = (char *) re_string_get_buffer (&mctx->input); + if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx, + naccepted) != 0) + return REG_MISSING; + } + } + + if (naccepted == 0) + { + Idx dest_node; + ok = re_node_set_insert (eps_via_nodes, node); + if (BE (! ok, 0)) + return REG_ERROR; + dest_node = dfa->edests[node].elems[0]; + if (re_node_set_contains (&mctx->state_log[*pidx]->nodes, + dest_node)) + return dest_node; + } + } + + if (naccepted != 0 + || check_node_accept (mctx, dfa->nodes + node, *pidx)) + { + Idx dest_node = dfa->nexts[node]; + *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted; + if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL + || !re_node_set_contains (&mctx->state_log[*pidx]->nodes, + dest_node))) + return REG_MISSING; + re_node_set_empty (eps_via_nodes); + return dest_node; + } + } + return REG_MISSING; +} + +static reg_errcode_t +internal_function +push_fail_stack (struct re_fail_stack_t *fs, Idx str_idx, Idx dest_node, + Idx nregs, regmatch_t *regs, re_node_set *eps_via_nodes) +{ + reg_errcode_t err; + Idx num = fs->num++; + if (fs->num == fs->alloc) + { + struct re_fail_stack_ent_t *new_array; + new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t) + * fs->alloc * 2)); + if (new_array == NULL) + return REG_ESPACE; + fs->alloc *= 2; + fs->stack = new_array; + } + fs->stack[num].idx = str_idx; + fs->stack[num].node = dest_node; + fs->stack[num].regs = re_malloc (regmatch_t, nregs); + if (fs->stack[num].regs == NULL) + return REG_ESPACE; + memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs); + err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes); + return err; +} + +static Idx +internal_function +pop_fail_stack (struct re_fail_stack_t *fs, Idx *pidx, Idx nregs, + regmatch_t *regs, re_node_set *eps_via_nodes) +{ + Idx num = --fs->num; + assert (REG_VALID_INDEX (num)); + *pidx = fs->stack[num].idx; + memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs); + re_node_set_free (eps_via_nodes); + re_free (fs->stack[num].regs); + *eps_via_nodes = fs->stack[num].eps_via_nodes; + return fs->stack[num].node; +} + +/* Set the positions where the subexpressions are starts/ends to registers + PMATCH. + Note: We assume that pmatch[0] is already set, and + pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch. */ + +static reg_errcode_t +internal_function +set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch, + regmatch_t *pmatch, bool fl_backtrack) +{ + const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer; + Idx idx, cur_node; + re_node_set eps_via_nodes; + struct re_fail_stack_t *fs; + struct re_fail_stack_t fs_body = { 0, 2, NULL }; + regmatch_t *prev_idx_match; + bool prev_idx_match_malloced = false; + +#ifdef DEBUG + assert (nmatch > 1); + assert (mctx->state_log != NULL); +#endif + if (fl_backtrack) + { + fs = &fs_body; + fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc); + if (fs->stack == NULL) + return REG_ESPACE; + } + else + fs = NULL; + + cur_node = dfa->init_node; + re_node_set_init_empty (&eps_via_nodes); + + if (__libc_use_alloca (nmatch * sizeof (regmatch_t))) + prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t)); + else + { + prev_idx_match = re_malloc (regmatch_t, nmatch); + if (prev_idx_match == NULL) + { + free_fail_stack_return (fs); + return REG_ESPACE; + } + prev_idx_match_malloced = true; + } + memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); + + for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;) + { + update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch); + + if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node) + { + Idx reg_idx; + if (fs) + { + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1) + break; + if (reg_idx == nmatch) + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return free_fail_stack_return (fs); + } + cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, + &eps_via_nodes); + } + else + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return REG_NOERROR; + } + } + + /* Proceed to next node. */ + cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node, + &eps_via_nodes, fs); + + if (BE (! REG_VALID_INDEX (cur_node), 0)) + { + if (BE (cur_node == REG_ERROR, 0)) + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + free_fail_stack_return (fs); + return REG_ESPACE; + } + if (fs) + cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, + &eps_via_nodes); + else + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return REG_NOMATCH; + } + } + } + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return free_fail_stack_return (fs); +} + +static reg_errcode_t +internal_function +free_fail_stack_return (struct re_fail_stack_t *fs) +{ + if (fs) + { + Idx fs_idx; + for (fs_idx = 0; fs_idx < fs->num; ++fs_idx) + { + re_node_set_free (&fs->stack[fs_idx].eps_via_nodes); + re_free (fs->stack[fs_idx].regs); + } + re_free (fs->stack); + } + return REG_NOERROR; +} + +static void +internal_function +update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, + regmatch_t *prev_idx_match, Idx cur_node, Idx cur_idx, Idx nmatch) +{ + int type = dfa->nodes[cur_node].type; + if (type == OP_OPEN_SUBEXP) + { + Idx reg_num = dfa->nodes[cur_node].opr.idx + 1; + + /* We are at the first node of this sub expression. */ + if (reg_num < nmatch) + { + pmatch[reg_num].rm_so = cur_idx; + pmatch[reg_num].rm_eo = -1; + } + } + else if (type == OP_CLOSE_SUBEXP) + { + Idx reg_num = dfa->nodes[cur_node].opr.idx + 1; + if (reg_num < nmatch) + { + /* We are at the last node of this sub expression. */ + if (pmatch[reg_num].rm_so < cur_idx) + { + pmatch[reg_num].rm_eo = cur_idx; + /* This is a non-empty match or we are not inside an optional + subexpression. Accept this right away. */ + memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); + } + else + { + if (dfa->nodes[cur_node].opt_subexp + && prev_idx_match[reg_num].rm_so != -1) + /* We transited through an empty match for an optional + subexpression, like (a?)*, and this is not the subexp's + first match. Copy back the old content of the registers + so that matches of an inner subexpression are undone as + well, like in ((a?))*. */ + memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch); + else + /* We completed a subexpression, but it may be part of + an optional one, so do not update PREV_IDX_MATCH. */ + pmatch[reg_num].rm_eo = cur_idx; + } + } + } +} + +/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0 + and sift the nodes in each states according to the following rules. + Updated state_log will be wrote to STATE_LOG. + + Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if... + 1. When STR_IDX == MATCH_LAST(the last index in the state_log): + If `a' isn't the LAST_NODE and `a' can't epsilon transit to + the LAST_NODE, we throw away the node `a'. + 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts + string `s' and transit to `b': + i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw + away the node `a'. + ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is + thrown away, we throw away the node `a'. + 3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b': + i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the + node `a'. + ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away, + we throw away the node `a'. */ + +#define STATE_NODE_CONTAINS(state,node) \ + ((state) != NULL && re_node_set_contains (&(state)->nodes, node)) + +static reg_errcode_t +internal_function +sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx) +{ + reg_errcode_t err; + int null_cnt = 0; + Idx str_idx = sctx->last_str_idx; + re_node_set cur_dest; + +#ifdef DEBUG + assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL); +#endif + + /* Build sifted state_log[str_idx]. It has the nodes which can epsilon + transit to the last_node and the last_node itself. */ + err = re_node_set_init_1 (&cur_dest, sctx->last_node); + if (BE (err != REG_NOERROR, 0)) + return err; + err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* Then check each states in the state_log. */ + while (str_idx > 0) + { + /* Update counters. */ + null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0; + if (null_cnt > mctx->max_mb_elem_len) + { + memset (sctx->sifted_states, '\0', + sizeof (re_dfastate_t *) * str_idx); + re_node_set_free (&cur_dest); + return REG_NOERROR; + } + re_node_set_empty (&cur_dest); + --str_idx; + + if (mctx->state_log[str_idx]) + { + err = build_sifted_states (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + + /* Add all the nodes which satisfy the following conditions: + - It can epsilon transit to a node in CUR_DEST. + - It is in CUR_SRC. + And update state_log. */ + err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + err = REG_NOERROR; + free_return: + re_node_set_free (&cur_dest); + return err; +} + +static reg_errcode_t +internal_function +build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx, + Idx str_idx, re_node_set *cur_dest) +{ + const re_dfa_t *const dfa = mctx->dfa; + const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes; + Idx i; + + /* Then build the next sifted state. + We build the next sifted state on `cur_dest', and update + `sifted_states[str_idx]' with `cur_dest'. + Note: + `cur_dest' is the sifted state from `state_log[str_idx + 1]'. + `cur_src' points the node_set of the old `state_log[str_idx]' + (with the epsilon nodes pre-filtered out). */ + for (i = 0; i < cur_src->nelem; i++) + { + Idx prev_node = cur_src->elems[i]; + int naccepted = 0; + bool ok; + +#ifdef DEBUG + re_token_type_t type = dfa->nodes[prev_node].type; + assert (!IS_EPSILON_NODE (type)); +#endif +#ifdef RE_ENABLE_I18N + /* If the node may accept `multi byte'. */ + if (dfa->nodes[prev_node].accept_mb) + naccepted = sift_states_iter_mb (mctx, sctx, prev_node, + str_idx, sctx->last_str_idx); +#endif /* RE_ENABLE_I18N */ + + /* We don't check backreferences here. + See update_cur_sifted_state(). */ + if (!naccepted + && check_node_accept (mctx, dfa->nodes + prev_node, str_idx) + && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1], + dfa->nexts[prev_node])) + naccepted = 1; + + if (naccepted == 0) + continue; + + if (sctx->limits.nelem) + { + Idx to_idx = str_idx + naccepted; + if (check_dst_limits (mctx, &sctx->limits, + dfa->nexts[prev_node], to_idx, + prev_node, str_idx)) + continue; + } + ok = re_node_set_insert (cur_dest, prev_node); + if (BE (! ok, 0)) + return REG_ESPACE; + } + + return REG_NOERROR; +} + +/* Helper functions. */ + +static reg_errcode_t +internal_function +clean_state_log_if_needed (re_match_context_t *mctx, Idx next_state_log_idx) +{ + Idx top = mctx->state_log_top; + + if (next_state_log_idx >= mctx->input.bufs_len + || (next_state_log_idx >= mctx->input.valid_len + && mctx->input.valid_len < mctx->input.len)) + { + reg_errcode_t err; + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (top < next_state_log_idx) + { + memset (mctx->state_log + top + 1, '\0', + sizeof (re_dfastate_t *) * (next_state_log_idx - top)); + mctx->state_log_top = next_state_log_idx; + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst, + re_dfastate_t **src, Idx num) +{ + Idx st_idx; + reg_errcode_t err; + for (st_idx = 0; st_idx < num; ++st_idx) + { + if (dst[st_idx] == NULL) + dst[st_idx] = src[st_idx]; + else if (src[st_idx] != NULL) + { + re_node_set merged_set; + err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes, + &src[st_idx]->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + dst[st_idx] = re_acquire_state (&err, dfa, &merged_set); + re_node_set_free (&merged_set); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +update_cur_sifted_state (const re_match_context_t *mctx, + re_sift_context_t *sctx, Idx str_idx, + re_node_set *dest_nodes) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err = REG_NOERROR; + const re_node_set *candidates; + candidates = ((mctx->state_log[str_idx] == NULL) ? NULL + : &mctx->state_log[str_idx]->nodes); + + if (dest_nodes->nelem == 0) + sctx->sifted_states[str_idx] = NULL; + else + { + if (candidates) + { + /* At first, add the nodes which can epsilon transit to a node in + DEST_NODE. */ + err = add_epsilon_src_nodes (dfa, dest_nodes, candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* Then, check the limitations in the current sift_context. */ + if (sctx->limits.nelem) + { + err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits, + mctx->bkref_ents, str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + + sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (candidates && mctx->state_log[str_idx]->has_backref) + { + err = sift_states_bkref (mctx, sctx, str_idx, candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes, + const re_node_set *candidates) +{ + reg_errcode_t err = REG_NOERROR; + Idx i; + + re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (!state->inveclosure.alloc) + { + err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem); + if (BE (err != REG_NOERROR, 0)) + return REG_ESPACE; + for (i = 0; i < dest_nodes->nelem; i++) + re_node_set_merge (&state->inveclosure, + dfa->inveclosures + dest_nodes->elems[i]); + } + return re_node_set_add_intersect (dest_nodes, candidates, + &state->inveclosure); +} + +static reg_errcode_t +internal_function +sub_epsilon_src_nodes (const re_dfa_t *dfa, Idx node, re_node_set *dest_nodes, + const re_node_set *candidates) +{ + Idx ecl_idx; + reg_errcode_t err; + re_node_set *inv_eclosure = dfa->inveclosures + node; + re_node_set except_nodes; + re_node_set_init_empty (&except_nodes); + for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) + { + Idx cur_node = inv_eclosure->elems[ecl_idx]; + if (cur_node == node) + continue; + if (IS_EPSILON_NODE (dfa->nodes[cur_node].type)) + { + Idx edst1 = dfa->edests[cur_node].elems[0]; + Idx edst2 = ((dfa->edests[cur_node].nelem > 1) + ? dfa->edests[cur_node].elems[1] : REG_MISSING); + if ((!re_node_set_contains (inv_eclosure, edst1) + && re_node_set_contains (dest_nodes, edst1)) + || (REG_VALID_NONZERO_INDEX (edst2) + && !re_node_set_contains (inv_eclosure, edst2) + && re_node_set_contains (dest_nodes, edst2))) + { + err = re_node_set_add_intersect (&except_nodes, candidates, + dfa->inveclosures + cur_node); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&except_nodes); + return err; + } + } + } + } + for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) + { + Idx cur_node = inv_eclosure->elems[ecl_idx]; + if (!re_node_set_contains (&except_nodes, cur_node)) + { + Idx idx = re_node_set_contains (dest_nodes, cur_node) - 1; + re_node_set_remove_at (dest_nodes, idx); + } + } + re_node_set_free (&except_nodes); + return REG_NOERROR; +} + +static bool +internal_function +check_dst_limits (const re_match_context_t *mctx, const re_node_set *limits, + Idx dst_node, Idx dst_idx, Idx src_node, Idx src_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx lim_idx, src_pos, dst_pos; + + Idx dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx); + Idx src_bkref_idx = search_cur_bkref_entry (mctx, src_idx); + for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) + { + Idx subexp_idx; + struct re_backref_cache_entry *ent; + ent = mctx->bkref_ents + limits->elems[lim_idx]; + subexp_idx = dfa->nodes[ent->node].opr.idx; + + dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], + subexp_idx, dst_node, dst_idx, + dst_bkref_idx); + src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], + subexp_idx, src_node, src_idx, + src_bkref_idx); + + /* In case of: + ( ) + ( ) + ( ) */ + if (src_pos == dst_pos) + continue; /* This is unrelated limitation. */ + else + return true; + } + return false; +} + +static int +internal_function +check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries, + Idx subexp_idx, Idx from_node, Idx bkref_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + const re_node_set *eclosures = dfa->eclosures + from_node; + Idx node_idx; + + /* Else, we are on the boundary: examine the nodes on the epsilon + closure. */ + for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx) + { + Idx node = eclosures->elems[node_idx]; + switch (dfa->nodes[node].type) + { + case OP_BACK_REF: + if (bkref_idx != REG_MISSING) + { + struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx; + do + { + Idx dst; + int cpos; + + if (ent->node != node) + continue; + + if (subexp_idx < BITSET_WORD_BITS + && !(ent->eps_reachable_subexps_map + & ((bitset_word_t) 1 << subexp_idx))) + continue; + + /* Recurse trying to reach the OP_OPEN_SUBEXP and + OP_CLOSE_SUBEXP cases below. But, if the + destination node is the same node as the source + node, don't recurse because it would cause an + infinite loop: a regex that exhibits this behavior + is ()\1*\1* */ + dst = dfa->edests[node].elems[0]; + if (dst == from_node) + { + if (boundaries & 1) + return -1; + else /* if (boundaries & 2) */ + return 0; + } + + cpos = + check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, + dst, bkref_idx); + if (cpos == -1 /* && (boundaries & 1) */) + return -1; + if (cpos == 0 && (boundaries & 2)) + return 0; + + if (subexp_idx < BITSET_WORD_BITS) + ent->eps_reachable_subexps_map + &= ~((bitset_word_t) 1 << subexp_idx); + } + while (ent++->more); + } + break; + + case OP_OPEN_SUBEXP: + if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx) + return -1; + break; + + case OP_CLOSE_SUBEXP: + if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx) + return 0; + break; + + default: + break; + } + } + + return (boundaries & 2) ? 1 : 0; +} + +static int +internal_function +check_dst_limits_calc_pos (const re_match_context_t *mctx, Idx limit, + Idx subexp_idx, Idx from_node, Idx str_idx, + Idx bkref_idx) +{ + struct re_backref_cache_entry *lim = mctx->bkref_ents + limit; + int boundaries; + + /* If we are outside the range of the subexpression, return -1 or 1. */ + if (str_idx < lim->subexp_from) + return -1; + + if (lim->subexp_to < str_idx) + return 1; + + /* If we are within the subexpression, return 0. */ + boundaries = (str_idx == lim->subexp_from); + boundaries |= (str_idx == lim->subexp_to) << 1; + if (boundaries == 0) + return 0; + + /* Else, examine epsilon closure. */ + return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, + from_node, bkref_idx); +} + +/* Check the limitations of sub expressions LIMITS, and remove the nodes + which are against limitations from DEST_NODES. */ + +static reg_errcode_t +internal_function +check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes, + const re_node_set *candidates, re_node_set *limits, + struct re_backref_cache_entry *bkref_ents, Idx str_idx) +{ + reg_errcode_t err; + Idx node_idx, lim_idx; + + for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) + { + Idx subexp_idx; + struct re_backref_cache_entry *ent; + ent = bkref_ents + limits->elems[lim_idx]; + + if (str_idx <= ent->subexp_from || ent->str_idx < str_idx) + continue; /* This is unrelated limitation. */ + + subexp_idx = dfa->nodes[ent->node].opr.idx; + if (ent->subexp_to == str_idx) + { + Idx ops_node = REG_MISSING; + Idx cls_node = REG_MISSING; + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + Idx node = dest_nodes->elems[node_idx]; + re_token_type_t type = dfa->nodes[node].type; + if (type == OP_OPEN_SUBEXP + && subexp_idx == dfa->nodes[node].opr.idx) + ops_node = node; + else if (type == OP_CLOSE_SUBEXP + && subexp_idx == dfa->nodes[node].opr.idx) + cls_node = node; + } + + /* Check the limitation of the open subexpression. */ + /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */ + if (REG_VALID_INDEX (ops_node)) + { + err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Check the limitation of the close subexpression. */ + if (REG_VALID_INDEX (cls_node)) + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + Idx node = dest_nodes->elems[node_idx]; + if (!re_node_set_contains (dfa->inveclosures + node, + cls_node) + && !re_node_set_contains (dfa->eclosures + node, + cls_node)) + { + /* It is against this limitation. + Remove it form the current sifted state. */ + err = sub_epsilon_src_nodes (dfa, node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + --node_idx; + } + } + } + else /* (ent->subexp_to != str_idx) */ + { + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + Idx node = dest_nodes->elems[node_idx]; + re_token_type_t type = dfa->nodes[node].type; + if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP) + { + if (subexp_idx != dfa->nodes[node].opr.idx) + continue; + /* It is against this limitation. + Remove it form the current sifted state. */ + err = sub_epsilon_src_nodes (dfa, node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + } + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx, + Idx str_idx, const re_node_set *candidates) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx node_idx, node; + re_sift_context_t local_sctx; + Idx first_idx = search_cur_bkref_entry (mctx, str_idx); + + if (first_idx == REG_MISSING) + return REG_NOERROR; + + local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */ + + for (node_idx = 0; node_idx < candidates->nelem; ++node_idx) + { + Idx enabled_idx; + re_token_type_t type; + struct re_backref_cache_entry *entry; + node = candidates->elems[node_idx]; + type = dfa->nodes[node].type; + /* Avoid infinite loop for the REs like "()\1+". */ + if (node == sctx->last_node && str_idx == sctx->last_str_idx) + continue; + if (type != OP_BACK_REF) + continue; + + entry = mctx->bkref_ents + first_idx; + enabled_idx = first_idx; + do + { + Idx subexp_len; + Idx to_idx; + Idx dst_node; + bool ok; + re_dfastate_t *cur_state; + + if (entry->node != node) + continue; + subexp_len = entry->subexp_to - entry->subexp_from; + to_idx = str_idx + subexp_len; + dst_node = (subexp_len ? dfa->nexts[node] + : dfa->edests[node].elems[0]); + + if (to_idx > sctx->last_str_idx + || sctx->sifted_states[to_idx] == NULL + || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node) + || check_dst_limits (mctx, &sctx->limits, node, + str_idx, dst_node, to_idx)) + continue; + + if (local_sctx.sifted_states == NULL) + { + local_sctx = *sctx; + err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + local_sctx.last_node = node; + local_sctx.last_str_idx = str_idx; + ok = re_node_set_insert (&local_sctx.limits, enabled_idx); + if (BE (! ok, 0)) + { + err = REG_ESPACE; + goto free_return; + } + cur_state = local_sctx.sifted_states[str_idx]; + err = sift_states_backward (mctx, &local_sctx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + if (sctx->limited_states != NULL) + { + err = merge_state_array (dfa, sctx->limited_states, + local_sctx.sifted_states, + str_idx + 1); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + local_sctx.sifted_states[str_idx] = cur_state; + re_node_set_remove (&local_sctx.limits, enabled_idx); + + /* mctx->bkref_ents may have changed, reload the pointer. */ + entry = mctx->bkref_ents + enabled_idx; + } + while (enabled_idx++, entry++->more); + } + err = REG_NOERROR; + free_return: + if (local_sctx.sifted_states != NULL) + { + re_node_set_free (&local_sctx.limits); + } + + return err; +} + + +#ifdef RE_ENABLE_I18N +static int +internal_function +sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx, + Idx node_idx, Idx str_idx, Idx max_str_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + int naccepted; + /* Check the node can accept `multi byte'. */ + naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx); + if (naccepted > 0 && str_idx + naccepted <= max_str_idx && + !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted], + dfa->nexts[node_idx])) + /* The node can't accept the `multi byte', or the + destination was already thrown away, then the node + could't accept the current input `multi byte'. */ + naccepted = 0; + /* Otherwise, it is sure that the node could accept + `naccepted' bytes input. */ + return naccepted; +} +#endif /* RE_ENABLE_I18N */ + + +/* Functions for state transition. */ + +/* Return the next state to which the current state STATE will transit by + accepting the current input byte, and update STATE_LOG if necessary. + If STATE can accept a multibyte char/collating element/back reference + update the destination of STATE_LOG. */ + +static re_dfastate_t * +internal_function +transit_state (reg_errcode_t *err, re_match_context_t *mctx, + re_dfastate_t *state) +{ + re_dfastate_t **trtable; + unsigned char ch; + +#ifdef RE_ENABLE_I18N + /* If the current state can accept multibyte. */ + if (BE (state->accept_mb, 0)) + { + *err = transit_state_mb (mctx, state); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } +#endif /* RE_ENABLE_I18N */ + + /* Then decide the next state with the single byte. */ +#if 0 + if (0) + /* don't use transition table */ + return transit_state_sb (err, mctx, state); +#endif + + /* Use transition table */ + ch = re_string_fetch_byte (&mctx->input); + for (;;) + { + trtable = state->trtable; + if (BE (trtable != NULL, 1)) + return trtable[ch]; + + trtable = state->word_trtable; + if (BE (trtable != NULL, 1)) + { + unsigned int context; + context + = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input) - 1, + mctx->eflags); + if (IS_WORD_CONTEXT (context)) + return trtable[ch + SBC_MAX]; + else + return trtable[ch]; + } + + if (!build_trtable (mctx->dfa, state)) + { + *err = REG_ESPACE; + return NULL; + } + + /* Retry, we now have a transition table. */ + } +} + +/* Update the state_log if we need */ +re_dfastate_t * +internal_function +merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx, + re_dfastate_t *next_state) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx cur_idx = re_string_cur_idx (&mctx->input); + + if (cur_idx > mctx->state_log_top) + { + mctx->state_log[cur_idx] = next_state; + mctx->state_log_top = cur_idx; + } + else if (mctx->state_log[cur_idx] == 0) + { + mctx->state_log[cur_idx] = next_state; + } + else + { + re_dfastate_t *pstate; + unsigned int context; + re_node_set next_nodes, *log_nodes, *table_nodes = NULL; + /* If (state_log[cur_idx] != 0), it implies that cur_idx is + the destination of a multibyte char/collating element/ + back reference. Then the next state is the union set of + these destinations and the results of the transition table. */ + pstate = mctx->state_log[cur_idx]; + log_nodes = pstate->entrance_nodes; + if (next_state != NULL) + { + table_nodes = next_state->entrance_nodes; + *err = re_node_set_init_union (&next_nodes, table_nodes, + log_nodes); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + else + next_nodes = *log_nodes; + /* Note: We already add the nodes of the initial state, + then we don't need to add them here. */ + + context = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input) - 1, + mctx->eflags); + next_state = mctx->state_log[cur_idx] + = re_acquire_state_context (err, dfa, &next_nodes, context); + /* We don't need to check errors here, since the return value of + this function is next_state and ERR is already set. */ + + if (table_nodes != NULL) + re_node_set_free (&next_nodes); + } + + if (BE (dfa->nbackref, 0) && next_state != NULL) + { + /* Check OP_OPEN_SUBEXP in the current state in case that we use them + later. We must check them here, since the back references in the + next state might use them. */ + *err = check_subexp_matching_top (mctx, &next_state->nodes, + cur_idx); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + + /* If the next state has back references. */ + if (next_state->has_backref) + { + *err = transit_state_bkref (mctx, &next_state->nodes); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + next_state = mctx->state_log[cur_idx]; + } + } + + return next_state; +} + +/* Skip bytes in the input that correspond to part of a + multi-byte match, then look in the log for a state + from which to restart matching. */ +static re_dfastate_t * +internal_function +find_recover_state (reg_errcode_t *err, re_match_context_t *mctx) +{ + re_dfastate_t *cur_state; + do + { + Idx max = mctx->state_log_top; + Idx cur_str_idx = re_string_cur_idx (&mctx->input); + + do + { + if (++cur_str_idx > max) + return NULL; + re_string_skip_bytes (&mctx->input, 1); + } + while (mctx->state_log[cur_str_idx] == NULL); + + cur_state = merge_state_with_log (err, mctx, NULL); + } + while (*err == REG_NOERROR && cur_state == NULL); + return cur_state; +} + +/* Helper functions for transit_state. */ + +/* From the node set CUR_NODES, pick up the nodes whose types are + OP_OPEN_SUBEXP and which have corresponding back references in the regular + expression. And register them to use them later for evaluating the + correspoding back references. */ + +static reg_errcode_t +internal_function +check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes, + Idx str_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx node_idx; + reg_errcode_t err; + + /* TODO: This isn't efficient. + Because there might be more than one nodes whose types are + OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all + nodes. + E.g. RE: (a){2} */ + for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx) + { + Idx node = cur_nodes->elems[node_idx]; + if (dfa->nodes[node].type == OP_OPEN_SUBEXP + && dfa->nodes[node].opr.idx < BITSET_WORD_BITS + && (dfa->used_bkref_map + & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx))) + { + err = match_ctx_add_subtop (mctx, node, str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +#if 0 +/* Return the next state to which the current state STATE will transit by + accepting the current input byte. */ + +static re_dfastate_t * +transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx, + re_dfastate_t *state) +{ + const re_dfa_t *const dfa = mctx->dfa; + re_node_set next_nodes; + re_dfastate_t *next_state; + Idx node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input); + unsigned int context; + + *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt) + { + Idx cur_node = state->nodes.elems[node_cnt]; + if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx)) + { + *err = re_node_set_merge (&next_nodes, + dfa->eclosures + dfa->nexts[cur_node]); + if (BE (*err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return NULL; + } + } + } + context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags); + next_state = re_acquire_state_context (err, dfa, &next_nodes, context); + /* We don't need to check errors here, since the return value of + this function is next_state and ERR is already set. */ + + re_node_set_free (&next_nodes); + re_string_skip_bytes (&mctx->input, 1); + return next_state; +} +#endif + +#ifdef RE_ENABLE_I18N +static reg_errcode_t +internal_function +transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx i; + + for (i = 0; i < pstate->nodes.nelem; ++i) + { + re_node_set dest_nodes, *new_nodes; + Idx cur_node_idx = pstate->nodes.elems[i]; + int naccepted; + Idx dest_idx; + unsigned int context; + re_dfastate_t *dest_state; + + if (!dfa->nodes[cur_node_idx].accept_mb) + continue; + + if (dfa->nodes[cur_node_idx].constraint) + { + context = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input), + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint, + context)) + continue; + } + + /* How many bytes the node can accept? */ + naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input, + re_string_cur_idx (&mctx->input)); + if (naccepted == 0) + continue; + + /* The node can accepts `naccepted' bytes. */ + dest_idx = re_string_cur_idx (&mctx->input) + naccepted; + mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted + : mctx->max_mb_elem_len); + err = clean_state_log_if_needed (mctx, dest_idx); + if (BE (err != REG_NOERROR, 0)) + return err; +#ifdef DEBUG + assert (dfa->nexts[cur_node_idx] != REG_MISSING); +#endif + new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx]; + + dest_state = mctx->state_log[dest_idx]; + if (dest_state == NULL) + dest_nodes = *new_nodes; + else + { + err = re_node_set_init_union (&dest_nodes, + dest_state->entrance_nodes, new_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + context = re_string_context_at (&mctx->input, dest_idx - 1, + mctx->eflags); + mctx->state_log[dest_idx] + = re_acquire_state_context (&err, dfa, &dest_nodes, context); + if (dest_state != NULL) + re_node_set_free (&dest_nodes); + if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0)) + return err; + } + return REG_NOERROR; +} +#endif /* RE_ENABLE_I18N */ + +static reg_errcode_t +internal_function +transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx i; + Idx cur_str_idx = re_string_cur_idx (&mctx->input); + + for (i = 0; i < nodes->nelem; ++i) + { + Idx dest_str_idx, prev_nelem, bkc_idx; + Idx node_idx = nodes->elems[i]; + unsigned int context; + const re_token_t *node = dfa->nodes + node_idx; + re_node_set *new_dest_nodes; + + /* Check whether `node' is a backreference or not. */ + if (node->type != OP_BACK_REF) + continue; + + if (node->constraint) + { + context = re_string_context_at (&mctx->input, cur_str_idx, + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) + continue; + } + + /* `node' is a backreference. + Check the substring which the substring matched. */ + bkc_idx = mctx->nbkref_ents; + err = get_subexp (mctx, node_idx, cur_str_idx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* And add the epsilon closures (which is `new_dest_nodes') of + the backreference to appropriate state_log. */ +#ifdef DEBUG + assert (dfa->nexts[node_idx] != REG_MISSING); +#endif + for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx) + { + Idx subexp_len; + re_dfastate_t *dest_state; + struct re_backref_cache_entry *bkref_ent; + bkref_ent = mctx->bkref_ents + bkc_idx; + if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx) + continue; + subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from; + new_dest_nodes = (subexp_len == 0 + ? dfa->eclosures + dfa->edests[node_idx].elems[0] + : dfa->eclosures + dfa->nexts[node_idx]); + dest_str_idx = (cur_str_idx + bkref_ent->subexp_to + - bkref_ent->subexp_from); + context = re_string_context_at (&mctx->input, dest_str_idx - 1, + mctx->eflags); + dest_state = mctx->state_log[dest_str_idx]; + prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0 + : mctx->state_log[cur_str_idx]->nodes.nelem); + /* Add `new_dest_node' to state_log. */ + if (dest_state == NULL) + { + mctx->state_log[dest_str_idx] + = re_acquire_state_context (&err, dfa, new_dest_nodes, + context); + if (BE (mctx->state_log[dest_str_idx] == NULL + && err != REG_NOERROR, 0)) + goto free_return; + } + else + { + re_node_set dest_nodes; + err = re_node_set_init_union (&dest_nodes, + dest_state->entrance_nodes, + new_dest_nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&dest_nodes); + goto free_return; + } + mctx->state_log[dest_str_idx] + = re_acquire_state_context (&err, dfa, &dest_nodes, context); + re_node_set_free (&dest_nodes); + if (BE (mctx->state_log[dest_str_idx] == NULL + && err != REG_NOERROR, 0)) + goto free_return; + } + /* We need to check recursively if the backreference can epsilon + transit. */ + if (subexp_len == 0 + && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem) + { + err = check_subexp_matching_top (mctx, new_dest_nodes, + cur_str_idx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + err = transit_state_bkref (mctx, new_dest_nodes); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + } + } + err = REG_NOERROR; + free_return: + return err; +} + +/* Enumerate all the candidates which the backreference BKREF_NODE can match + at BKREF_STR_IDX, and register them by match_ctx_add_entry(). + Note that we might collect inappropriate candidates here. + However, the cost of checking them strictly here is too high, then we + delay these checking for prune_impossible_nodes(). */ + +static reg_errcode_t +internal_function +get_subexp (re_match_context_t *mctx, Idx bkref_node, Idx bkref_str_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx subexp_num, sub_top_idx; + const char *buf = (const char *) re_string_get_buffer (&mctx->input); + /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */ + Idx cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx); + if (cache_idx != REG_MISSING) + { + const struct re_backref_cache_entry *entry + = mctx->bkref_ents + cache_idx; + do + if (entry->node == bkref_node) + return REG_NOERROR; /* We already checked it. */ + while (entry++->more); + } + + subexp_num = dfa->nodes[bkref_node].opr.idx; + + /* For each sub expression */ + for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx) + { + reg_errcode_t err; + re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx]; + re_sub_match_last_t *sub_last; + Idx sub_last_idx, sl_str, bkref_str_off; + + if (dfa->nodes[sub_top->node].opr.idx != subexp_num) + continue; /* It isn't related. */ + + sl_str = sub_top->str_idx; + bkref_str_off = bkref_str_idx; + /* At first, check the last node of sub expressions we already + evaluated. */ + for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx) + { + regoff_t sl_str_diff; + sub_last = sub_top->lasts[sub_last_idx]; + sl_str_diff = sub_last->str_idx - sl_str; + /* The matched string by the sub expression match with the substring + at the back reference? */ + if (sl_str_diff > 0) + { + if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0)) + { + /* Not enough chars for a successful match. */ + if (bkref_str_off + sl_str_diff > mctx->input.len) + break; + + err = clean_state_log_if_needed (mctx, + bkref_str_off + + sl_str_diff); + if (BE (err != REG_NOERROR, 0)) + return err; + buf = (const char *) re_string_get_buffer (&mctx->input); + } + if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0) + /* We don't need to search this sub expression any more. */ + break; + } + bkref_str_off += sl_str_diff; + sl_str += sl_str_diff; + err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, + bkref_str_idx); + + /* Reload buf, since the preceding call might have reallocated + the buffer. */ + buf = (const char *) re_string_get_buffer (&mctx->input); + + if (err == REG_NOMATCH) + continue; + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (sub_last_idx < sub_top->nlasts) + continue; + if (sub_last_idx > 0) + ++sl_str; + /* Then, search for the other last nodes of the sub expression. */ + for (; sl_str <= bkref_str_idx; ++sl_str) + { + Idx cls_node; + regoff_t sl_str_off; + const re_node_set *nodes; + sl_str_off = sl_str - sub_top->str_idx; + /* The matched string by the sub expression match with the substring + at the back reference? */ + if (sl_str_off > 0) + { + if (BE (bkref_str_off >= mctx->input.valid_len, 0)) + { + /* If we are at the end of the input, we cannot match. */ + if (bkref_str_off >= mctx->input.len) + break; + + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + return err; + + buf = (const char *) re_string_get_buffer (&mctx->input); + } + if (buf [bkref_str_off++] != buf[sl_str - 1]) + break; /* We don't need to search this sub expression + any more. */ + } + if (mctx->state_log[sl_str] == NULL) + continue; + /* Does this state have a ')' of the sub expression? */ + nodes = &mctx->state_log[sl_str]->nodes; + cls_node = find_subexp_node (dfa, nodes, subexp_num, + OP_CLOSE_SUBEXP); + if (cls_node == REG_MISSING) + continue; /* No. */ + if (sub_top->path == NULL) + { + sub_top->path = calloc (sizeof (state_array_t), + sl_str - sub_top->str_idx + 1); + if (sub_top->path == NULL) + return REG_ESPACE; + } + /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node + in the current context? */ + err = check_arrival (mctx, sub_top->path, sub_top->node, + sub_top->str_idx, cls_node, sl_str, + OP_CLOSE_SUBEXP); + if (err == REG_NOMATCH) + continue; + if (BE (err != REG_NOERROR, 0)) + return err; + sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str); + if (BE (sub_last == NULL, 0)) + return REG_ESPACE; + err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, + bkref_str_idx); + if (err == REG_NOMATCH) + continue; + } + } + return REG_NOERROR; +} + +/* Helper functions for get_subexp(). */ + +/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR. + If it can arrive, register the sub expression expressed with SUB_TOP + and SUB_LAST. */ + +static reg_errcode_t +internal_function +get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top, + re_sub_match_last_t *sub_last, Idx bkref_node, Idx bkref_str) +{ + reg_errcode_t err; + Idx to_idx; + /* Can the subexpression arrive the back reference? */ + err = check_arrival (mctx, &sub_last->path, sub_last->node, + sub_last->str_idx, bkref_node, bkref_str, + OP_OPEN_SUBEXP); + if (err != REG_NOERROR) + return err; + err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx, + sub_last->str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx; + return clean_state_log_if_needed (mctx, to_idx); +} + +/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX. + Search '(' if FL_OPEN, or search ')' otherwise. + TODO: This function isn't efficient... + Because there might be more than one nodes whose types are + OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all + nodes. + E.g. RE: (a){2} */ + +static Idx +internal_function +find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, + Idx subexp_idx, int type) +{ + Idx cls_idx; + for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx) + { + Idx cls_node = nodes->elems[cls_idx]; + const re_token_t *node = dfa->nodes + cls_node; + if (node->type == type + && node->opr.idx == subexp_idx) + return cls_node; + } + return REG_MISSING; +} + +/* Check whether the node TOP_NODE at TOP_STR can arrive to the node + LAST_NODE at LAST_STR. We record the path onto PATH since it will be + heavily reused. + Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */ + +static reg_errcode_t +internal_function +check_arrival (re_match_context_t *mctx, state_array_t *path, Idx top_node, + Idx top_str, Idx last_node, Idx last_str, int type) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err = REG_NOERROR; + Idx subexp_num, backup_cur_idx, str_idx, null_cnt; + re_dfastate_t *cur_state = NULL; + re_node_set *cur_nodes, next_nodes; + re_dfastate_t **backup_state_log; + unsigned int context; + + subexp_num = dfa->nodes[top_node].opr.idx; + /* Extend the buffer if we need. */ + if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0)) + { + re_dfastate_t **new_array; + Idx old_alloc = path->alloc; + Idx new_alloc = old_alloc + last_str + mctx->max_mb_elem_len + 1; + if (BE (new_alloc < old_alloc, 0) + || BE (SIZE_MAX / sizeof (re_dfastate_t *) < new_alloc, 0)) + return REG_ESPACE; + new_array = re_realloc (path->array, re_dfastate_t *, new_alloc); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + path->array = new_array; + path->alloc = new_alloc; + memset (new_array + old_alloc, '\0', + sizeof (re_dfastate_t *) * (path->alloc - old_alloc)); + } + + str_idx = path->next_idx ? path->next_idx : top_str; + + /* Temporary modify MCTX. */ + backup_state_log = mctx->state_log; + backup_cur_idx = mctx->input.cur_idx; + mctx->state_log = path->array; + mctx->input.cur_idx = str_idx; + + /* Setup initial node set. */ + context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); + if (str_idx == top_str) + { + err = re_node_set_init_1 (&next_nodes, top_node); + if (BE (err != REG_NOERROR, 0)) + return err; + err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + else + { + cur_state = mctx->state_log[str_idx]; + if (cur_state && cur_state->has_backref) + { + err = re_node_set_init_copy (&next_nodes, &cur_state->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + re_node_set_init_empty (&next_nodes); + } + if (str_idx == top_str || (cur_state && cur_state->has_backref)) + { + if (next_nodes.nelem) + { + err = expand_bkref_cache (mctx, &next_nodes, str_idx, + subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + mctx->state_log[str_idx] = cur_state; + } + + for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;) + { + re_node_set_empty (&next_nodes); + if (mctx->state_log[str_idx + 1]) + { + err = re_node_set_merge (&next_nodes, + &mctx->state_log[str_idx + 1]->nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + if (cur_state) + { + err = check_arrival_add_next_nodes (mctx, str_idx, + &cur_state->non_eps_nodes, + &next_nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + ++str_idx; + if (next_nodes.nelem) + { + err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + err = expand_bkref_cache (mctx, &next_nodes, str_idx, + subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); + cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + mctx->state_log[str_idx] = cur_state; + null_cnt = cur_state == NULL ? null_cnt + 1 : 0; + } + re_node_set_free (&next_nodes); + cur_nodes = (mctx->state_log[last_str] == NULL ? NULL + : &mctx->state_log[last_str]->nodes); + path->next_idx = str_idx; + + /* Fix MCTX. */ + mctx->state_log = backup_state_log; + mctx->input.cur_idx = backup_cur_idx; + + /* Then check the current node set has the node LAST_NODE. */ + if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node)) + return REG_NOERROR; + + return REG_NOMATCH; +} + +/* Helper functions for check_arrival. */ + +/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them + to NEXT_NODES. + TODO: This function is similar to the functions transit_state*(), + however this function has many additional works. + Can't we unify them? */ + +static reg_errcode_t +internal_function +check_arrival_add_next_nodes (re_match_context_t *mctx, Idx str_idx, + re_node_set *cur_nodes, re_node_set *next_nodes) +{ + const re_dfa_t *const dfa = mctx->dfa; + bool ok; + Idx cur_idx; + reg_errcode_t err = REG_NOERROR; + re_node_set union_set; + re_node_set_init_empty (&union_set); + for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx) + { + int naccepted = 0; + Idx cur_node = cur_nodes->elems[cur_idx]; +#ifdef DEBUG + re_token_type_t type = dfa->nodes[cur_node].type; + assert (!IS_EPSILON_NODE (type)); +#endif +#ifdef RE_ENABLE_I18N + /* If the node may accept `multi byte'. */ + if (dfa->nodes[cur_node].accept_mb) + { + naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input, + str_idx); + if (naccepted > 1) + { + re_dfastate_t *dest_state; + Idx next_node = dfa->nexts[cur_node]; + Idx next_idx = str_idx + naccepted; + dest_state = mctx->state_log[next_idx]; + re_node_set_empty (&union_set); + if (dest_state) + { + err = re_node_set_merge (&union_set, &dest_state->nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&union_set); + return err; + } + } + ok = re_node_set_insert (&union_set, next_node); + if (BE (! ok, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + mctx->state_log[next_idx] = re_acquire_state (&err, dfa, + &union_set); + if (BE (mctx->state_log[next_idx] == NULL + && err != REG_NOERROR, 0)) + { + re_node_set_free (&union_set); + return err; + } + } + } +#endif /* RE_ENABLE_I18N */ + if (naccepted + || check_node_accept (mctx, dfa->nodes + cur_node, str_idx)) + { + ok = re_node_set_insert (next_nodes, dfa->nexts[cur_node]); + if (BE (! ok, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + } + } + re_node_set_free (&union_set); + return REG_NOERROR; +} + +/* For all the nodes in CUR_NODES, add the epsilon closures of them to + CUR_NODES, however exclude the nodes which are: + - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN. + - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN. +*/ + +static reg_errcode_t +internal_function +check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes, + Idx ex_subexp, int type) +{ + reg_errcode_t err; + Idx idx, outside_node; + re_node_set new_nodes; +#ifdef DEBUG + assert (cur_nodes->nelem); +#endif + err = re_node_set_alloc (&new_nodes, cur_nodes->nelem); + if (BE (err != REG_NOERROR, 0)) + return err; + /* Create a new node set NEW_NODES with the nodes which are epsilon + closures of the node in CUR_NODES. */ + + for (idx = 0; idx < cur_nodes->nelem; ++idx) + { + Idx cur_node = cur_nodes->elems[idx]; + const re_node_set *eclosure = dfa->eclosures + cur_node; + outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type); + if (outside_node == REG_MISSING) + { + /* There are no problematic nodes, just merge them. */ + err = re_node_set_merge (&new_nodes, eclosure); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&new_nodes); + return err; + } + } + else + { + /* There are problematic nodes, re-calculate incrementally. */ + err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node, + ex_subexp, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&new_nodes); + return err; + } + } + } + re_node_set_free (cur_nodes); + *cur_nodes = new_nodes; + return REG_NOERROR; +} + +/* Helper function for check_arrival_expand_ecl. + Check incrementally the epsilon closure of TARGET, and if it isn't + problematic append it to DST_NODES. */ + +static reg_errcode_t +internal_function +check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes, + Idx target, Idx ex_subexp, int type) +{ + Idx cur_node; + for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);) + { + bool ok; + + if (dfa->nodes[cur_node].type == type + && dfa->nodes[cur_node].opr.idx == ex_subexp) + { + if (type == OP_CLOSE_SUBEXP) + { + ok = re_node_set_insert (dst_nodes, cur_node); + if (BE (! ok, 0)) + return REG_ESPACE; + } + break; + } + ok = re_node_set_insert (dst_nodes, cur_node); + if (BE (! ok, 0)) + return REG_ESPACE; + if (dfa->edests[cur_node].nelem == 0) + break; + if (dfa->edests[cur_node].nelem == 2) + { + reg_errcode_t err; + err = check_arrival_expand_ecl_sub (dfa, dst_nodes, + dfa->edests[cur_node].elems[1], + ex_subexp, type); + if (BE (err != REG_NOERROR, 0)) + return err; + } + cur_node = dfa->edests[cur_node].elems[0]; + } + return REG_NOERROR; +} + + +/* For all the back references in the current state, calculate the + destination of the back references by the appropriate entry + in MCTX->BKREF_ENTS. */ + +static reg_errcode_t +internal_function +expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes, + Idx cur_str, Idx subexp_num, int type) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx cache_idx_start = search_cur_bkref_entry (mctx, cur_str); + struct re_backref_cache_entry *ent; + + if (cache_idx_start == REG_MISSING) + return REG_NOERROR; + + restart: + ent = mctx->bkref_ents + cache_idx_start; + do + { + Idx to_idx, next_node; + + /* Is this entry ENT is appropriate? */ + if (!re_node_set_contains (cur_nodes, ent->node)) + continue; /* No. */ + + to_idx = cur_str + ent->subexp_to - ent->subexp_from; + /* Calculate the destination of the back reference, and append it + to MCTX->STATE_LOG. */ + if (to_idx == cur_str) + { + /* The backreference did epsilon transit, we must re-check all the + node in the current state. */ + re_node_set new_dests; + reg_errcode_t err2, err3; + next_node = dfa->edests[ent->node].elems[0]; + if (re_node_set_contains (cur_nodes, next_node)) + continue; + err = re_node_set_init_1 (&new_dests, next_node); + err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type); + err3 = re_node_set_merge (cur_nodes, &new_dests); + re_node_set_free (&new_dests); + if (BE (err != REG_NOERROR || err2 != REG_NOERROR + || err3 != REG_NOERROR, 0)) + { + err = (err != REG_NOERROR ? err + : (err2 != REG_NOERROR ? err2 : err3)); + return err; + } + /* TODO: It is still inefficient... */ + goto restart; + } + else + { + re_node_set union_set; + next_node = dfa->nexts[ent->node]; + if (mctx->state_log[to_idx]) + { + bool ok; + if (re_node_set_contains (&mctx->state_log[to_idx]->nodes, + next_node)) + continue; + err = re_node_set_init_copy (&union_set, + &mctx->state_log[to_idx]->nodes); + ok = re_node_set_insert (&union_set, next_node); + if (BE (err != REG_NOERROR || ! ok, 0)) + { + re_node_set_free (&union_set); + err = err != REG_NOERROR ? err : REG_ESPACE; + return err; + } + } + else + { + err = re_node_set_init_1 (&union_set, next_node); + if (BE (err != REG_NOERROR, 0)) + return err; + } + mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set); + re_node_set_free (&union_set); + if (BE (mctx->state_log[to_idx] == NULL + && err != REG_NOERROR, 0)) + return err; + } + } + while (ent++->more); + return REG_NOERROR; +} + +/* Build transition table for the state. + Return true if successful. */ + +static bool +internal_function +build_trtable (const re_dfa_t *dfa, re_dfastate_t *state) +{ + reg_errcode_t err; + Idx i, j; + int ch; + bool need_word_trtable = false; + bitset_word_t elem, mask; + bool dests_node_malloced = false; + bool dest_states_malloced = false; + Idx ndests; /* Number of the destination states from `state'. */ + re_dfastate_t **trtable; + re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl; + re_node_set follows, *dests_node; + bitset_t *dests_ch; + bitset_t acceptable; + + struct dests_alloc + { + re_node_set dests_node[SBC_MAX]; + bitset_t dests_ch[SBC_MAX]; + } *dests_alloc; + + /* We build DFA states which corresponds to the destination nodes + from `state'. `dests_node[i]' represents the nodes which i-th + destination state contains, and `dests_ch[i]' represents the + characters which i-th destination state accepts. */ + if (__libc_use_alloca (sizeof (struct dests_alloc))) + dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc)); + else + { + dests_alloc = re_malloc (struct dests_alloc, 1); + if (BE (dests_alloc == NULL, 0)) + return false; + dests_node_malloced = true; + } + dests_node = dests_alloc->dests_node; + dests_ch = dests_alloc->dests_ch; + + /* Initialize transiton table. */ + state->word_trtable = state->trtable = NULL; + + /* At first, group all nodes belonging to `state' into several + destinations. */ + ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch); + if (BE (! REG_VALID_NONZERO_INDEX (ndests), 0)) + { + if (dests_node_malloced) + free (dests_alloc); + if (ndests == 0) + { + state->trtable = (re_dfastate_t **) + calloc (sizeof (re_dfastate_t *), SBC_MAX); + return true; + } + return false; + } + + err = re_node_set_alloc (&follows, ndests + 1); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + + /* Avoid arithmetic overflow in size calculation. */ + if (BE ((((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX) + / (3 * sizeof (re_dfastate_t *))) + < ndests), + 0)) + goto out_free; + + if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX + + ndests * 3 * sizeof (re_dfastate_t *))) + dest_states = (re_dfastate_t **) + alloca (ndests * 3 * sizeof (re_dfastate_t *)); + else + { + dest_states = (re_dfastate_t **) + malloc (ndests * 3 * sizeof (re_dfastate_t *)); + if (BE (dest_states == NULL, 0)) + { +out_free: + if (dest_states_malloced) + free (dest_states); + re_node_set_free (&follows); + for (i = 0; i < ndests; ++i) + re_node_set_free (dests_node + i); + if (dests_node_malloced) + free (dests_alloc); + return false; + } + dest_states_malloced = true; + } + dest_states_word = dest_states + ndests; + dest_states_nl = dest_states_word + ndests; + bitset_empty (acceptable); + + /* Then build the states for all destinations. */ + for (i = 0; i < ndests; ++i) + { + Idx next_node; + re_node_set_empty (&follows); + /* Merge the follows of this destination states. */ + for (j = 0; j < dests_node[i].nelem; ++j) + { + next_node = dfa->nexts[dests_node[i].elems[j]]; + if (next_node != REG_MISSING) + { + err = re_node_set_merge (&follows, dfa->eclosures + next_node); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + } + } + dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0); + if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + /* If the new state has context constraint, + build appropriate states for these contexts. */ + if (dest_states[i]->has_constraint) + { + dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows, + CONTEXT_WORD); + if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + + if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1) + need_word_trtable = true; + + dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows, + CONTEXT_NEWLINE); + if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + } + else + { + dest_states_word[i] = dest_states[i]; + dest_states_nl[i] = dest_states[i]; + } + bitset_merge (acceptable, dests_ch[i]); + } + + if (!BE (need_word_trtable, 0)) + { + /* We don't care about whether the following character is a word + character, or we are in a single-byte character set so we can + discern by looking at the character code: allocate a + 256-entry transition table. */ + trtable = state->trtable = + (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX); + if (BE (trtable == NULL, 0)) + goto out_free; + + /* For all characters ch...: */ + for (i = 0; i < BITSET_WORDS; ++i) + for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; + elem; + mask <<= 1, elem >>= 1, ++ch) + if (BE (elem & 1, 0)) + { + /* There must be exactly one destination which accepts + character ch. See group_nodes_into_DFAstates. */ + for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) + ; + + /* j-th destination accepts the word character ch. */ + if (dfa->word_char[i] & mask) + trtable[ch] = dest_states_word[j]; + else + trtable[ch] = dest_states[j]; + } + } + else + { + /* We care about whether the following character is a word + character, and we are in a multi-byte character set: discern + by looking at the character code: build two 256-entry + transition tables, one starting at trtable[0] and one + starting at trtable[SBC_MAX]. */ + trtable = state->word_trtable = + (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX); + if (BE (trtable == NULL, 0)) + goto out_free; + + /* For all characters ch...: */ + for (i = 0; i < BITSET_WORDS; ++i) + for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; + elem; + mask <<= 1, elem >>= 1, ++ch) + if (BE (elem & 1, 0)) + { + /* There must be exactly one destination which accepts + character ch. See group_nodes_into_DFAstates. */ + for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) + ; + + /* j-th destination accepts the word character ch. */ + trtable[ch] = dest_states[j]; + trtable[ch + SBC_MAX] = dest_states_word[j]; + } + } + + /* new line */ + if (bitset_contain (acceptable, NEWLINE_CHAR)) + { + /* The current state accepts newline character. */ + for (j = 0; j < ndests; ++j) + if (bitset_contain (dests_ch[j], NEWLINE_CHAR)) + { + /* k-th destination accepts newline character. */ + trtable[NEWLINE_CHAR] = dest_states_nl[j]; + if (need_word_trtable) + trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j]; + /* There must be only one destination which accepts + newline. See group_nodes_into_DFAstates. */ + break; + } + } + + if (dest_states_malloced) + free (dest_states); + + re_node_set_free (&follows); + for (i = 0; i < ndests; ++i) + re_node_set_free (dests_node + i); + + if (dests_node_malloced) + free (dests_alloc); + + return true; +} + +/* Group all nodes belonging to STATE into several destinations. + Then for all destinations, set the nodes belonging to the destination + to DESTS_NODE[i] and set the characters accepted by the destination + to DEST_CH[i]. This function return the number of destinations. */ + +static Idx +internal_function +group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state, + re_node_set *dests_node, bitset_t *dests_ch) +{ + reg_errcode_t err; + bool ok; + Idx i, j, k; + Idx ndests; /* Number of the destinations from `state'. */ + bitset_t accepts; /* Characters a node can accept. */ + const re_node_set *cur_nodes = &state->nodes; + bitset_empty (accepts); + ndests = 0; + + /* For all the nodes belonging to `state', */ + for (i = 0; i < cur_nodes->nelem; ++i) + { + re_token_t *node = &dfa->nodes[cur_nodes->elems[i]]; + re_token_type_t type = node->type; + unsigned int constraint = node->constraint; + + /* Enumerate all single byte character this node can accept. */ + if (type == CHARACTER) + bitset_set (accepts, node->opr.c); + else if (type == SIMPLE_BRACKET) + { + bitset_merge (accepts, node->opr.sbcset); + } + else if (type == OP_PERIOD) + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + bitset_merge (accepts, dfa->sb_char); + else +#endif + bitset_set_all (accepts); + if (!(dfa->syntax & RE_DOT_NEWLINE)) + bitset_clear (accepts, '\n'); + if (dfa->syntax & RE_DOT_NOT_NULL) + bitset_clear (accepts, '\0'); + } +#ifdef RE_ENABLE_I18N + else if (type == OP_UTF8_PERIOD) + { + if (ASCII_CHARS % BITSET_WORD_BITS == 0) + memset (accepts, -1, ASCII_CHARS / CHAR_BIT); + else + bitset_merge (accepts, utf8_sb_map); + if (!(dfa->syntax & RE_DOT_NEWLINE)) + bitset_clear (accepts, '\n'); + if (dfa->syntax & RE_DOT_NOT_NULL) + bitset_clear (accepts, '\0'); + } +#endif + else + continue; + + /* Check the `accepts' and sift the characters which are not + match it the context. */ + if (constraint) + { + if (constraint & NEXT_NEWLINE_CONSTRAINT) + { + bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR); + bitset_empty (accepts); + if (accepts_newline) + bitset_set (accepts, NEWLINE_CHAR); + else + continue; + } + if (constraint & NEXT_ENDBUF_CONSTRAINT) + { + bitset_empty (accepts); + continue; + } + + if (constraint & NEXT_WORD_CONSTRAINT) + { + bitset_word_t any_set = 0; + if (type == CHARACTER && !node->word_char) + { + bitset_empty (accepts); + continue; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j])); + else +#endif + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= dfa->word_char[j]); + if (!any_set) + continue; + } + if (constraint & NEXT_NOTWORD_CONSTRAINT) + { + bitset_word_t any_set = 0; + if (type == CHARACTER && node->word_char) + { + bitset_empty (accepts); + continue; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j])); + else +#endif + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= ~dfa->word_char[j]); + if (!any_set) + continue; + } + } + + /* Then divide `accepts' into DFA states, or create a new + state. Above, we make sure that accepts is not empty. */ + for (j = 0; j < ndests; ++j) + { + bitset_t intersec; /* Intersection sets, see below. */ + bitset_t remains; + /* Flags, see below. */ + bitset_word_t has_intersec, not_subset, not_consumed; + + /* Optimization, skip if this state doesn't accept the character. */ + if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c)) + continue; + + /* Enumerate the intersection set of this state and `accepts'. */ + has_intersec = 0; + for (k = 0; k < BITSET_WORDS; ++k) + has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k]; + /* And skip if the intersection set is empty. */ + if (!has_intersec) + continue; + + /* Then check if this state is a subset of `accepts'. */ + not_subset = not_consumed = 0; + for (k = 0; k < BITSET_WORDS; ++k) + { + not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k]; + not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k]; + } + + /* If this state isn't a subset of `accepts', create a + new group state, which has the `remains'. */ + if (not_subset) + { + bitset_copy (dests_ch[ndests], remains); + bitset_copy (dests_ch[j], intersec); + err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]); + if (BE (err != REG_NOERROR, 0)) + goto error_return; + ++ndests; + } + + /* Put the position in the current group. */ + ok = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]); + if (BE (! ok, 0)) + goto error_return; + + /* If all characters are consumed, go to next node. */ + if (!not_consumed) + break; + } + /* Some characters remain, create a new group. */ + if (j == ndests) + { + bitset_copy (dests_ch[ndests], accepts); + err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]); + if (BE (err != REG_NOERROR, 0)) + goto error_return; + ++ndests; + bitset_empty (accepts); + } + } + return ndests; + error_return: + for (j = 0; j < ndests; ++j) + re_node_set_free (dests_node + j); + return REG_MISSING; +} + +#ifdef RE_ENABLE_I18N +/* Check how many bytes the node `dfa->nodes[node_idx]' accepts. + Return the number of the bytes the node accepts. + STR_IDX is the current index of the input string. + + This function handles the nodes which can accept one character, or + one collating element like '.', '[a-z]', opposite to the other nodes + can only accept one byte. */ + +static int +internal_function +check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx, + const re_string_t *input, Idx str_idx) +{ + const re_token_t *node = dfa->nodes + node_idx; + int char_len, elem_len; + Idx i; + + if (BE (node->type == OP_UTF8_PERIOD, 0)) + { + unsigned char c = re_string_byte_at (input, str_idx), d; + if (BE (c < 0xc2, 1)) + return 0; + + if (str_idx + 2 > input->len) + return 0; + + d = re_string_byte_at (input, str_idx + 1); + if (c < 0xe0) + return (d < 0x80 || d > 0xbf) ? 0 : 2; + else if (c < 0xf0) + { + char_len = 3; + if (c == 0xe0 && d < 0xa0) + return 0; + } + else if (c < 0xf8) + { + char_len = 4; + if (c == 0xf0 && d < 0x90) + return 0; + } + else if (c < 0xfc) + { + char_len = 5; + if (c == 0xf8 && d < 0x88) + return 0; + } + else if (c < 0xfe) + { + char_len = 6; + if (c == 0xfc && d < 0x84) + return 0; + } + else + return 0; + + if (str_idx + char_len > input->len) + return 0; + + for (i = 1; i < char_len; ++i) + { + d = re_string_byte_at (input, str_idx + i); + if (d < 0x80 || d > 0xbf) + return 0; + } + return char_len; + } + + char_len = re_string_char_size_at (input, str_idx); + if (node->type == OP_PERIOD) + { + if (char_len <= 1) + return 0; + /* FIXME: I don't think this if is needed, as both '\n' + and '\0' are char_len == 1. */ + /* '.' accepts any one character except the following two cases. */ + if ((!(dfa->syntax & RE_DOT_NEWLINE) && + re_string_byte_at (input, str_idx) == '\n') || + ((dfa->syntax & RE_DOT_NOT_NULL) && + re_string_byte_at (input, str_idx) == '\0')) + return 0; + return char_len; + } + + elem_len = re_string_elem_size_at (input, str_idx); + if ((elem_len <= 1 && char_len <= 1) || char_len == 0) + return 0; + + if (node->type == COMPLEX_BRACKET) + { + const re_charset_t *cset = node->opr.mbcset; +# ifdef _LIBC + const unsigned char *pin + = ((const unsigned char *) re_string_get_buffer (input) + str_idx); + Idx j; + uint32_t nrules; +# endif /* _LIBC */ + int match_len = 0; + wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars) + ? re_string_wchar_at (input, str_idx) : 0); + + /* match with multibyte character? */ + for (i = 0; i < cset->nmbchars; ++i) + if (wc == cset->mbchars[i]) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + /* match with character_class? */ + for (i = 0; i < cset->nchar_classes; ++i) + { + wctype_t wt = cset->char_classes[i]; + if (__iswctype (wc, wt)) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + } + +# ifdef _LIBC + nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules != 0) + { + unsigned int in_collseq = 0; + const int32_t *table, *indirect; + const unsigned char *weights, *extra; + const char *collseqwc; + int32_t idx; + /* This #include defines a local function! */ +# include + + /* match with collating_symbol? */ + if (cset->ncoll_syms) + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); + for (i = 0; i < cset->ncoll_syms; ++i) + { + const unsigned char *coll_sym = extra + cset->coll_syms[i]; + /* Compare the length of input collating element and + the length of current collating element. */ + if (*coll_sym != elem_len) + continue; + /* Compare each bytes. */ + for (j = 0; j < *coll_sym; j++) + if (pin[j] != coll_sym[1 + j]) + break; + if (j == *coll_sym) + { + /* Match if every bytes is equal. */ + match_len = j; + goto check_node_accept_bytes_match; + } + } + + if (cset->nranges) + { + if (elem_len <= char_len) + { + collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); + in_collseq = __collseq_table_lookup (collseqwc, wc); + } + else + in_collseq = find_collation_sequence_value (pin, elem_len); + } + /* match with range expression? */ + for (i = 0; i < cset->nranges; ++i) + if (cset->range_starts[i] <= in_collseq + && in_collseq <= cset->range_ends[i]) + { + match_len = elem_len; + goto check_node_accept_bytes_match; + } + + /* match with equivalence_class? */ + if (cset->nequiv_classes) + { + const unsigned char *cp = pin; + table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); + idx = findidx (&cp); + if (idx > 0) + for (i = 0; i < cset->nequiv_classes; ++i) + { + int32_t equiv_class_idx = cset->equiv_classes[i]; + size_t weight_len = weights[idx]; + if (weight_len == weights[equiv_class_idx]) + { + Idx cnt = 0; + while (cnt <= weight_len + && (weights[equiv_class_idx + 1 + cnt] + == weights[idx + 1 + cnt])) + ++cnt; + if (cnt > weight_len) + { + match_len = elem_len; + goto check_node_accept_bytes_match; + } + } + } + } + } + else +# endif /* _LIBC */ + { + /* match with range expression? */ +#if __GNUC__ >= 2 + wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'}; +#else + wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; + cmp_buf[2] = wc; +#endif + for (i = 0; i < cset->nranges; ++i) + { + cmp_buf[0] = cset->range_starts[i]; + cmp_buf[4] = cset->range_ends[i]; + if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 + && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + } + } + check_node_accept_bytes_match: + if (!cset->non_match) + return match_len; + else + { + if (match_len > 0) + return 0; + else + return (elem_len > char_len) ? elem_len : char_len; + } + } + return 0; +} + +# ifdef _LIBC +static unsigned int +internal_function +find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len) +{ + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules == 0) + { + if (mbs_len == 1) + { + /* No valid character. Match it as a single byte character. */ + const unsigned char *collseq = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); + return collseq[mbs[0]]; + } + return UINT_MAX; + } + else + { + int32_t idx; + const unsigned char *extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); + int32_t extrasize = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra; + + for (idx = 0; idx < extrasize;) + { + int mbs_cnt; + bool found = false; + int32_t elem_mbs_len; + /* Skip the name of collating element name. */ + idx = idx + extra[idx] + 1; + elem_mbs_len = extra[idx++]; + if (mbs_len == elem_mbs_len) + { + for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt) + if (extra[idx + mbs_cnt] != mbs[mbs_cnt]) + break; + if (mbs_cnt == elem_mbs_len) + /* Found the entry. */ + found = true; + } + /* Skip the byte sequence of the collating element. */ + idx += elem_mbs_len; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + /* Skip the collation sequence value. */ + idx += sizeof (uint32_t); + /* Skip the wide char sequence of the collating element. */ + idx = idx + sizeof (uint32_t) * (extra[idx] + 1); + /* If we found the entry, return the sequence value. */ + if (found) + return *(uint32_t *) (extra + idx); + /* Skip the collation sequence value. */ + idx += sizeof (uint32_t); + } + return UINT_MAX; + } +} +# endif /* _LIBC */ +#endif /* RE_ENABLE_I18N */ + +/* Check whether the node accepts the byte which is IDX-th + byte of the INPUT. */ + +static bool +internal_function +check_node_accept (const re_match_context_t *mctx, const re_token_t *node, + Idx idx) +{ + unsigned char ch; + ch = re_string_byte_at (&mctx->input, idx); + switch (node->type) + { + case CHARACTER: + if (node->opr.c != ch) + return false; + break; + + case SIMPLE_BRACKET: + if (!bitset_contain (node->opr.sbcset, ch)) + return false; + break; + +#ifdef RE_ENABLE_I18N + case OP_UTF8_PERIOD: + if (ch >= ASCII_CHARS) + return false; + /* FALLTHROUGH */ +#endif + case OP_PERIOD: + if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE)) + || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL))) + return false; + break; + + default: + return false; + } + + if (node->constraint) + { + /* The node has constraints. Check whether the current context + satisfies the constraints. */ + unsigned int context = re_string_context_at (&mctx->input, idx, + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) + return false; + } + + return true; +} + +/* Extend the buffers, if the buffers have run out. */ + +static reg_errcode_t +internal_function +extend_buffers (re_match_context_t *mctx) +{ + reg_errcode_t ret; + re_string_t *pstr = &mctx->input; + + /* Avoid overflow. */ + if (BE (SIZE_MAX / 2 / sizeof (re_dfastate_t *) <= pstr->bufs_len, 0)) + return REG_ESPACE; + + /* Double the lengthes of the buffers. */ + ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + if (mctx->state_log != NULL) + { + /* And double the length of state_log. */ + /* XXX We have no indication of the size of this buffer. If this + allocation fail we have no indication that the state_log array + does not have the right size. */ + re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *, + pstr->bufs_len + 1); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + mctx->state_log = new_array; + } + + /* Then reconstruct the buffers. */ + if (pstr->icase) + { +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + else +#endif /* RE_ENABLE_I18N */ + build_upper_buffer (pstr); + } + else + { +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + build_wcs_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + { + if (pstr->trans != NULL) + re_string_translate_buffer (pstr); + } + } + return REG_NOERROR; +} + + +/* Functions for matching context. */ + +/* Initialize MCTX. */ + +static reg_errcode_t +internal_function +match_ctx_init (re_match_context_t *mctx, int eflags, Idx n) +{ + mctx->eflags = eflags; + mctx->match_last = REG_MISSING; + if (n > 0) + { + /* Avoid overflow. */ + size_t max_object_size = + MAX (sizeof (struct re_backref_cache_entry), + sizeof (re_sub_match_top_t *)); + if (BE (SIZE_MAX / max_object_size < n, 0)) + return REG_ESPACE; + + mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n); + mctx->sub_tops = re_malloc (re_sub_match_top_t *, n); + if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0)) + return REG_ESPACE; + } + /* Already zero-ed by the caller. + else + mctx->bkref_ents = NULL; + mctx->nbkref_ents = 0; + mctx->nsub_tops = 0; */ + mctx->abkref_ents = n; + mctx->max_mb_elem_len = 1; + mctx->asub_tops = n; + return REG_NOERROR; +} + +/* Clean the entries which depend on the current input in MCTX. + This function must be invoked when the matcher changes the start index + of the input, or changes the input string. */ + +static void +internal_function +match_ctx_clean (re_match_context_t *mctx) +{ + Idx st_idx; + for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx) + { + Idx sl_idx; + re_sub_match_top_t *top = mctx->sub_tops[st_idx]; + for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx) + { + re_sub_match_last_t *last = top->lasts[sl_idx]; + re_free (last->path.array); + re_free (last); + } + re_free (top->lasts); + if (top->path) + { + re_free (top->path->array); + re_free (top->path); + } + free (top); + } + + mctx->nsub_tops = 0; + mctx->nbkref_ents = 0; +} + +/* Free all the memory associated with MCTX. */ + +static void +internal_function +match_ctx_free (re_match_context_t *mctx) +{ + /* First, free all the memory associated with MCTX->SUB_TOPS. */ + match_ctx_clean (mctx); + re_free (mctx->sub_tops); + re_free (mctx->bkref_ents); +} + +/* Add a new backreference entry to MCTX. + Note that we assume that caller never call this function with duplicate + entry, and call with STR_IDX which isn't smaller than any existing entry. +*/ + +static reg_errcode_t +internal_function +match_ctx_add_entry (re_match_context_t *mctx, Idx node, Idx str_idx, Idx from, + Idx to) +{ + if (mctx->nbkref_ents >= mctx->abkref_ents) + { + struct re_backref_cache_entry* new_entry; + new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry, + mctx->abkref_ents * 2); + if (BE (new_entry == NULL, 0)) + { + re_free (mctx->bkref_ents); + return REG_ESPACE; + } + mctx->bkref_ents = new_entry; + memset (mctx->bkref_ents + mctx->nbkref_ents, '\0', + sizeof (struct re_backref_cache_entry) * mctx->abkref_ents); + mctx->abkref_ents *= 2; + } + if (mctx->nbkref_ents > 0 + && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx) + mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1; + + mctx->bkref_ents[mctx->nbkref_ents].node = node; + mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx; + mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from; + mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to; + + /* This is a cache that saves negative results of check_dst_limits_calc_pos. + If bit N is clear, means that this entry won't epsilon-transition to + an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression. If + it is set, check_dst_limits_calc_pos_1 will recurse and try to find one + such node. + + A backreference does not epsilon-transition unless it is empty, so set + to all zeros if FROM != TO. */ + mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map + = (from == to ? -1 : 0); + + mctx->bkref_ents[mctx->nbkref_ents++].more = 0; + if (mctx->max_mb_elem_len < to - from) + mctx->max_mb_elem_len = to - from; + return REG_NOERROR; +} + +/* Return the first entry with the same str_idx, or REG_MISSING if none is + found. Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */ + +static Idx +internal_function +search_cur_bkref_entry (const re_match_context_t *mctx, Idx str_idx) +{ + Idx left, right, mid, last; + last = right = mctx->nbkref_ents; + for (left = 0; left < right;) + { + mid = (left + right) / 2; + if (mctx->bkref_ents[mid].str_idx < str_idx) + left = mid + 1; + else + right = mid; + } + if (left < last && mctx->bkref_ents[left].str_idx == str_idx) + return left; + else + return REG_MISSING; +} + +/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches + at STR_IDX. */ + +static reg_errcode_t +internal_function +match_ctx_add_subtop (re_match_context_t *mctx, Idx node, Idx str_idx) +{ +#ifdef DEBUG + assert (mctx->sub_tops != NULL); + assert (mctx->asub_tops > 0); +#endif + if (BE (mctx->nsub_tops == mctx->asub_tops, 0)) + { + Idx new_asub_tops = mctx->asub_tops * 2; + re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops, + re_sub_match_top_t *, + new_asub_tops); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + mctx->sub_tops = new_array; + mctx->asub_tops = new_asub_tops; + } + mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t)); + if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0)) + return REG_ESPACE; + mctx->sub_tops[mctx->nsub_tops]->node = node; + mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx; + return REG_NOERROR; +} + +/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches + at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */ + +static re_sub_match_last_t * +internal_function +match_ctx_add_sublast (re_sub_match_top_t *subtop, Idx node, Idx str_idx) +{ + re_sub_match_last_t *new_entry; + if (BE (subtop->nlasts == subtop->alasts, 0)) + { + Idx new_alasts = 2 * subtop->alasts + 1; + re_sub_match_last_t **new_array = re_realloc (subtop->lasts, + re_sub_match_last_t *, + new_alasts); + if (BE (new_array == NULL, 0)) + return NULL; + subtop->lasts = new_array; + subtop->alasts = new_alasts; + } + new_entry = calloc (1, sizeof (re_sub_match_last_t)); + if (BE (new_entry != NULL, 1)) + { + subtop->lasts[subtop->nlasts] = new_entry; + new_entry->node = node; + new_entry->str_idx = str_idx; + ++subtop->nlasts; + } + return new_entry; +} + +static void +internal_function +sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, + re_dfastate_t **limited_sts, Idx last_node, Idx last_str_idx) +{ + sctx->sifted_states = sifted_sts; + sctx->limited_states = limited_sts; + sctx->last_node = last_node; + sctx->last_str_idx = last_str_idx; + re_node_set_init_empty (&sctx->limits); +} diff --git a/src/bin/m4/lib/sigprocmask.c b/src/bin/m4/lib/sigprocmask.c new file mode 100644 index 0000000000..c38c7e32fd --- /dev/null +++ b/src/bin/m4/lib/sigprocmask.c @@ -0,0 +1,192 @@ +/* POSIX compatible signal blocking. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "sigprocmask.h" + +#include +#include +#include + +/* We assume that a platform without POSIX signal blocking functions also + does not have the POSIX sigaction() function, only the signal() function. + This is true for Woe32 platforms. */ + +/* A signal handler. */ +typedef void (*handler_t) (int signal); + +int +sigismember (const sigset_t *set, int sig) +{ + if (sig >= 0 && sig < NSIG) + return (*set >> sig) & 1; + else + return 0; +} + +int +sigemptyset (sigset_t *set) +{ + *set = 0; + return 0; +} + +int +sigaddset (sigset_t *set, int sig) +{ + if (sig >= 0 && sig < NSIG) + { + *set |= 1U << sig; + return 0; + } + else + { + errno = EINVAL; + return -1; + } +} + +int +sigdelset (sigset_t *set, int sig) +{ + if (sig >= 0 && sig < NSIG) + { + *set &= ~(1U << sig); + return 0; + } + else + { + errno = EINVAL; + return -1; + } +} + +int +sigfillset (sigset_t *set) +{ + *set = (2U << (NSIG - 1)) - 1; + return 0; +} + +/* Set of currently blocked signals. */ +static sigset_t blocked_set /* = 0 */; + +/* Set of currently blocked and pending signals. */ +static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */; + +/* Signal handler that is installed for blocked signals. */ +static void +blocked_handler (int sig) +{ + if (sig >= 0 && sig < NSIG) + pending_array[sig] = 1; +} + +int +sigpending (sigset_t *set) +{ + sigset_t pending = 0; + int sig; + + for (sig = 0; sig < NSIG; sig++) + if (pending_array[sig]) + pending |= 1U << sig; + return pending; +} + +/* The previous signal handlers. + Only the array elements corresponding to blocked signals are relevant. */ +static handler_t old_handlers[NSIG]; + +int +sigprocmask (int operation, const sigset_t *set, sigset_t *old_set) +{ + if (old_set != NULL) + *old_set = blocked_set; + + if (set != NULL) + { + sigset_t new_blocked_set; + sigset_t to_unblock; + sigset_t to_block; + + switch (operation) + { + case SIG_BLOCK: + new_blocked_set = blocked_set | *set; + break; + case SIG_SETMASK: + new_blocked_set = *set; + break; + case SIG_UNBLOCK: + new_blocked_set = blocked_set & ~*set; + break; + default: + errno = EINVAL; + return -1; + } + to_unblock = blocked_set & ~new_blocked_set; + to_block = new_blocked_set & ~blocked_set; + + if (to_block != 0) + { + int sig; + + for (sig = 0; sig < NSIG; sig++) + if ((to_block >> sig) & 1) + { + pending_array[sig] = 0; + if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR) + blocked_set |= 1U << sig; + } + } + + if (to_unblock != 0) + { + sig_atomic_t received[NSIG]; + int sig; + + for (sig = 0; sig < NSIG; sig++) + if ((to_unblock >> sig) & 1) + { + if (signal (sig, old_handlers[sig]) != blocked_handler) + /* The application changed a signal handler while the signal + was blocked. We don't support this. */ + abort (); + received[sig] = pending_array[sig]; + blocked_set &= ~(1U << sig); + pending_array[sig] = 0; + } + else + received[sig] = 0; + + for (sig = 0; sig < NSIG; sig++) + if (received[sig]) + { + #if HAVE_RAISE + raise (sig); + #else + kill (getpid (), sig); + #endif + } + } + } + return 0; +} diff --git a/src/bin/m4/lib/sigprocmask.h b/src/bin/m4/lib/sigprocmask.h new file mode 100644 index 0000000000..d31c365a30 --- /dev/null +++ b/src/bin/m4/lib/sigprocmask.h @@ -0,0 +1,69 @@ +/* POSIX compatible signal blocking. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +#if ! HAVE_POSIX_SIGNALBLOCKING + +/* Mingw defines sigset_t not in , but in . */ +# include + +# include "verify.h" + +/* Maximum signal number + 1. */ +# ifndef NSIG +# define NSIG 32 +# endif + +/* This code supports only 32 signals. */ +verify (NSIG <= 32); + +/* A set or mask of signals. */ +# if !HAVE_SIGSET_T +typedef unsigned int sigset_t; +# endif + +/* Test whether a given signal is contained in a signal set. */ +extern int sigismember (const sigset_t *set, int sig); + +/* Initialize a signal set to the empty set. */ +extern int sigemptyset (sigset_t *set); + +/* Add a signal to a signal set. */ +extern int sigaddset (sigset_t *set, int sig); + +/* Remove a signal from a signal set. */ +extern int sigdelset (sigset_t *set, int sig); + +/* Fill a signal set with all possible signals. */ +extern int sigfillset (sigset_t *set); + +/* Return the set of those blocked signals that are pending. */ +extern int sigpending (sigset_t *set); + +/* If OLD_SET is not NULL, put the current set of blocked signals in *OLD_SET. + Then, if SET is not NULL, affect the current set of blocked signals by + combining it with *SET as indicated in OPERATION. + In this implementation, you are not allowed to change a signal handler + while the signal is blocked. */ +# define SIG_BLOCK 0 /* blocked_set = blocked_set | *set; */ +# define SIG_SETMASK 1 /* blocked_set = *set; */ +# define SIG_UNBLOCK 2 /* blocked_set = blocked_set & ~*set; */ +extern int sigprocmask (int operation, const sigset_t *set, sigset_t *old_set); + +#endif diff --git a/src/bin/m4/lib/size_max.h b/src/bin/m4/lib/size_max.h new file mode 100644 index 0000000000..ed0bc13771 --- /dev/null +++ b/src/bin/m4/lib/size_max.h @@ -0,0 +1,31 @@ +/* size_max.h -- declare SIZE_MAX through system headers + Copyright (C) 2005-2006 Free Software Foundation, Inc. + Written by Simon Josefsson. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef GNULIB_SIZE_MAX_H +#define GNULIB_SIZE_MAX_H + +/* Get SIZE_MAX declaration on systems like Solaris 7/8/9. */ +# include +/* Get SIZE_MAX declaration on systems like glibc 2. */ +# if HAVE_STDINT_H +# include +# endif +/* On systems where these include files don't define it, SIZE_MAX is defined + in config.h. */ + +#endif /* GNULIB_SIZE_MAX_H */ diff --git a/src/bin/m4/lib/stat_.h b/src/bin/m4/lib/stat_.h new file mode 100644 index 0000000000..cbf5ac00ac --- /dev/null +++ b/src/bin/m4/lib/stat_.h @@ -0,0 +1,275 @@ +/* Provide a more complete sys/stat header file. + Copyright (C) 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Eric Blake, Paul Eggert, and Jim Meyering. */ + +#ifndef _gl_SYS_STAT_H +#define _gl_SYS_STAT_H + +/* This file is supposed to be used on platforms where is + incomplete. It is intended to provide definitions and prototypes + needed by an application. Start with what the system provides. */ +#include @ABSOLUTE_SYS_STAT_H@ + +#ifndef S_IFMT +# define S_IFMT 0170000 +#endif + +#if STAT_MACROS_BROKEN +# undef S_ISBLK +# undef S_ISCHR +# undef S_ISDIR +# undef S_ISFIFO +# undef S_ISLNK +# undef S_ISNAM +# undef S_ISMPB +# undef S_ISMPC +# undef S_ISNWK +# undef S_ISREG +# undef S_ISSOCK +#endif + +#ifndef S_ISBLK +# ifdef S_IFBLK +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +# else +# define S_ISBLK(m) 0 +# endif +#endif + +#ifndef S_ISCHR +# ifdef S_IFCHR +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +# else +# define S_ISCHR(m) 0 +# endif +#endif + +#ifndef S_ISDIR +# ifdef S_IFDIR +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# else +# define S_ISDIR(m) 0 +# endif +#endif + +#ifndef S_ISDOOR /* Solaris 2.5 and up */ +# define S_ISDOOR(m) 0 +#endif + +#ifndef S_ISFIFO +# ifdef S_IFIFO +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +# else +# define S_ISFIFO(m) 0 +# endif +#endif + +#ifndef S_ISLNK +# ifdef S_IFLNK +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +# else +# define S_ISLNK(m) 0 +# endif +#endif + +#ifndef S_ISMPB /* V7 */ +# ifdef S_IFMPB +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +# else +# define S_ISMPB(m) 0 +# define S_ISMPC(m) 0 +# endif +#endif + +#ifndef S_ISNAM /* Xenix */ +# ifdef S_IFNAM +# define S_ISNAM(m) (((m) & S_IFMT) == S_IFNAM) +# else +# define S_ISNAM(m) 0 +# endif +#endif + +#ifndef S_ISNWK /* HP/UX */ +# ifdef S_IFNWK +# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +# else +# define S_ISNWK(m) 0 +# endif +#endif + +#ifndef S_ISPORT /* Solaris 10 and up */ +# define S_ISPORT(m) 0 +#endif + +#ifndef S_ISREG +# ifdef S_IFREG +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +# else +# define S_ISREG(m) 0 +# endif +#endif + +#ifndef S_ISSOCK +# ifdef S_IFSOCK +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +# else +# define S_ISSOCK(m) 0 +# endif +#endif + + +#ifndef S_TYPEISMQ +# define S_TYPEISMQ(p) 0 +#endif + +#ifndef S_TYPEISTMO +# define S_TYPEISTMO(p) 0 +#endif + + +#ifndef S_TYPEISSEM +# ifdef S_INSEM +# define S_TYPEISSEM(p) (S_ISNAM ((p)->st_mode) && (p)->st_rdev == S_INSEM) +# else +# define S_TYPEISSEM(p) 0 +# endif +#endif + +#ifndef S_TYPEISSHM +# ifdef S_INSHD +# define S_TYPEISSHM(p) (S_ISNAM ((p)->st_mode) && (p)->st_rdev == S_INSHD) +# else +# define S_TYPEISSHM(p) 0 +# endif +#endif + +/* high performance ("contiguous data") */ +#ifndef S_ISCTG +# define S_ISCTG(p) 0 +#endif + +/* Cray DMF (data migration facility): off line, with data */ +#ifndef S_ISOFD +# define S_ISOFD(p) 0 +#endif + +/* Cray DMF (data migration facility): off line, with no data */ +#ifndef S_ISOFL +# define S_ISOFL(p) 0 +#endif + +/* 4.4BSD whiteout */ +#ifndef S_ISWHT +# define S_ISWHT(m) 0 +#endif + +/* If any of the following are undefined, + define them to their de facto standard values. */ +#if !S_ISUID +# define S_ISUID 04000 +#endif +#if !S_ISGID +# define S_ISGID 02000 +#endif + +/* S_ISVTX is a common extension to POSIX. */ +#ifndef S_ISVTX +# define S_ISVTX 01000 +#endif + +#if !S_IRUSR && S_IREAD +# define S_IRUSR S_IREAD +#endif +#if !S_IRUSR +# define S_IRUSR 00400 +#endif +#if !S_IRGRP +# define S_IRGRP (S_IRUSR >> 3) +#endif +#if !S_IROTH +# define S_IROTH (S_IRUSR >> 6) +#endif + +#if !S_IWUSR && S_IWRITE +# define S_IWUSR S_IWRITE +#endif +#if !S_IWUSR +# define S_IWUSR 00200 +#endif +#if !S_IWGRP +# define S_IWGRP (S_IWUSR >> 3) +#endif +#if !S_IWOTH +# define S_IWOTH (S_IWUSR >> 6) +#endif + +#if !S_IXUSR && S_IEXEC +# define S_IXUSR S_IEXEC +#endif +#if !S_IXUSR +# define S_IXUSR 00100 +#endif +#if !S_IXGRP +# define S_IXGRP (S_IXUSR >> 3) +#endif +#if !S_IXOTH +# define S_IXOTH (S_IXUSR >> 6) +#endif + +#if !S_IRWXU +# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +#endif +#if !S_IRWXG +# define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) +#endif +#if !S_IRWXO +# define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) +#endif + +/* S_IXUGO is a common extension to POSIX. */ +#if !S_IXUGO +# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) +#endif + +#ifndef S_IRWXUGO +# define S_IRWXUGO (S_IRWXU | S_IRWXG | S_IRWXO) +#endif + +/* mingw does not support symlinks, therefore it does not have lstat. But + without links, stat does just fine. */ +#if ! HAVE_LSTAT +# define lstat stat +#endif + +/* mingw's _mkdir() function has 1 argument, but we pass 2 arguments. + Additionally, it declares _mkdir (and depending on compile flags, an + alias mkdir), only in the nonstandard io.h. */ +#if ! HAVE_DECL_MKDIR && HAVE_IO_H +# include + +static inline int +rpl_mkdir (char const *name, mode_t mode) +{ + return _mkdir (name); +} + +# define mkdir rpl_mkdir +#endif + +#endif /* _gl_SYS_STAT_H */ diff --git a/src/bin/m4/lib/stdbool_.h b/src/bin/m4/lib/stdbool_.h new file mode 100644 index 0000000000..efa80ba921 --- /dev/null +++ b/src/bin/m4/lib/stdbool_.h @@ -0,0 +1,115 @@ +/* Copyright (C) 2001, 2002, 2003, 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2001. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _STDBOOL_H +#define _STDBOOL_H + +/* ISO C 99 for platforms that lack it. */ + +/* Usage suggestions: + + Programs that use should be aware of some limitations + and standards compliance issues. + + Standards compliance: + + - must be #included before 'bool', 'false', 'true' + can be used. + + - You cannot assume that sizeof (bool) == 1. + + - Programs should not undefine the macros bool, true, and false, + as C99 lists that as an "obsolescent feature". + + Limitations of this substitute, when used in a C89 environment: + + - must be #included before the '_Bool' type can be used. + + - You cannot assume that _Bool is a typedef; it might be a macro. + + - In C99, casts and automatic conversions to '_Bool' or 'bool' are + performed in such a way that every nonzero value gets converted + to 'true', and zero gets converted to 'false'. This doesn't work + with this substitute. With this substitute, only the values 0 and 1 + give the expected result when converted to _Bool' or 'bool'. + + Also, it is suggested that programs use 'bool' rather than '_Bool'; + this isn't required, but 'bool' is more common. */ + + +/* 7.16. Boolean type and values */ + +/* BeOS already #defines false 0, true 1. We use the same + definitions below, but temporarily we have to #undef them. */ +#ifdef __BEOS__ +# include /* defines bool but not _Bool */ +# undef false +# undef true +#endif + +/* For the sake of symbolic names in gdb, we define true and false as + enum constants, not only as macros. + It is tempting to write + typedef enum { false = 0, true = 1 } _Bool; + so that gdb prints values of type 'bool' symbolically. But if we do + this, values of type '_Bool' may promote to 'int' or 'unsigned int' + (see ISO C 99 6.7.2.2.(4)); however, '_Bool' must promote to 'int' + (see ISO C 99 6.3.1.1.(2)). So we add a negative value to the + enum; this ensures that '_Bool' promotes to 'int'. */ +#if defined __cplusplus || defined __BEOS__ + /* A compiler known to have 'bool'. */ + /* If the compiler already has both 'bool' and '_Bool', we can assume they + are the same types. */ +# if !@HAVE__BOOL@ +typedef bool _Bool; +# endif +#else +# if !defined __GNUC__ + /* If @HAVE__BOOL@: + Some HP-UX cc and AIX IBM C compiler versions have compiler bugs when + the built-in _Bool type is used. See + http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-10/msg00086.html + Similar bugs are likely with other compilers as well; this file + wouldn't be used if was working. + So we override the _Bool type. + If !@HAVE__BOOL@: + Need to define _Bool ourselves. As 'signed char' or as an enum type? + Use of a typedef, with SunPRO C, leads to a stupid + "warning: _Bool is a keyword in ISO C99". + Use of an enum type, with IRIX cc, leads to a stupid + "warning(1185): enumerated type mixed with another type". + The only benefit of the enum type, debuggability, is not important + with these compilers. So use 'signed char' and no typedef. */ +# define _Bool signed char +enum { false = 0, true = 1 }; +# else + /* With this compiler, trust the _Bool type if the compiler has it. */ +# if !@HAVE__BOOL@ +typedef enum { _Bool_must_promote_to_int = -1, false = 0, true = 1 } _Bool; +# endif +# endif +#endif +#define bool _Bool + +/* The other macros must be usable in preprocessor directives. */ +#define false 0 +#define true 1 +#define __bool_true_false_are_defined 1 + +#endif /* _STDBOOL_H */ diff --git a/src/bin/m4/lib/stdint_.h b/src/bin/m4/lib/stdint_.h new file mode 100644 index 0000000000..a4f9a4cf7b --- /dev/null +++ b/src/bin/m4/lib/stdint_.h @@ -0,0 +1,493 @@ +/* Copyright (C) 2001-2002, 2004-2006 Free Software Foundation, Inc. + Written by Paul Eggert, Bruno Haible, Sam Steingold, Peter Burwood. + This file is part of gnulib. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GL_STDINT_H +#define _GL_STDINT_H + +/* + * ISO C 99 for platforms that lack it. + * + */ + +/* Get those types that are already defined in other system include + files, so that we can "#define int8_t signed char" below without + worrying about a later system include file containing a "typedef + signed char int8_t;" that will get messed up by our macro. Our + macros should all be consistent with the system versions, except + for the "fast" types and macros, which we recommend against using + in public interfaces due to compiler differences. */ + +#if @HAVE_STDINT_H@ +# if defined __sgi && ! defined __c99 + /* Bypass IRIX's if in C89 mode, since it merely annoys users + with "This header file is to be used only for c99 mode compilations" + diagnostics. */ +# define __STDINT_H__ +# endif + /* Other systems may have an incomplete or buggy . + Include it before , since any "#include " + in would reinclude us, skipping our contents because + _GL_STDINT_H is defined. */ +# include @ABSOLUTE_STDINT_H@ +#endif + +/* defines some of the stdint.h types as well, on glibc, + IRIX 6.5, and OpenBSD 3.8 (via ). + AIX 5.2 isn't needed and causes troubles. + MacOS X 10.4.6 includes (which is us), but + relies on the system definitions, so include + after @ABSOLUTE_STDINT_H@. */ +#if @HAVE_SYS_TYPES_H@ && ! defined _AIX +# include +#endif + +/* Get LONG_MIN, LONG_MAX, ULONG_MAX. */ +#include + +#if @HAVE_INTTYPES_H@ + /* In OpenBSD 3.8, includes , which defines + int{8,16,32,64}_t, uint{8,16,32,64}_t and __BIT_TYPES_DEFINED__. + also defines intptr_t and uintptr_t. */ +# define _GL_JUST_INCLUDE_ABSOLUTE_INTTYPES_H +# include +# undef _GL_JUST_INCLUDE_ABSOLUTE_INTTYPES_H +#elif @HAVE_SYS_INTTYPES_H@ + /* Solaris 7 has the types except the *_fast*_t types, and + the macros except for *_FAST*_*, INTPTR_MIN, PTRDIFF_MIN, PTRDIFF_MAX. */ +# include +#endif + +#if @HAVE_SYS_BITYPES_H@ && ! defined __BIT_TYPES_DEFINED__ + /* Linux libc4 >= 4.6.7 and libc5 have a that defines + int{8,16,32,64}_t and __BIT_TYPES_DEFINED__. In libc5 >= 5.2.2 it is + included by . */ +# include +#endif + +#if ! defined __cplusplus || defined __STDC_CONSTANT_MACROS + +/* Get WCHAR_MIN, WCHAR_MAX. */ +# if @HAVE_WCHAR_H@ && ! (defined WCHAR_MIN && defined WCHAR_MAX) + /* BSD/OS 4.1 has a bug: and must be included before + . */ +# include +# include +# include +# endif + +#endif + +/* Minimum and maximum values for a integer type under the usual assumption. + Return an unspecified value if BITS == 0, adding a check to pacify + picky compilers. */ + +#define _STDINT_MIN(signed, bits, zero) \ + ((signed) ? (- ((zero) + 1) << ((bits) ? (bits) - 1 : 0)) : (zero)) + +#define _STDINT_MAX(signed, bits, zero) \ + ((signed) \ + ? ~ _STDINT_MIN (signed, bits, zero) \ + : ((((zero) + 1) << ((bits) ? (bits) - 1 : 0)) - 1) * 2 + 1) + +/* 7.18.1.1. Exact-width integer types */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. */ + +#undef int8_t +#undef uint8_t +#define int8_t signed char +#define uint8_t unsigned char + +#undef int16_t +#undef uint16_t +#define int16_t short int +#define uint16_t unsigned short int + +#undef int32_t +#undef uint32_t +#define int32_t int +#define uint32_t unsigned int + +#undef int64_t +#if LONG_MAX >> 31 >> 31 == 1 +# define int64_t long int +#elif defined _MSC_VER +# define int64_t __int64 +#elif @HAVE_LONG_LONG_INT@ +# define int64_t long long int +#endif + +#undef uint64_t +#if ULONG_MAX >> 31 >> 31 >> 1 == 1 +# define uint64_t unsigned long int +#elif defined _MSC_VER +# define uint64_t unsigned __int64 +#elif @HAVE_UNSIGNED_LONG_LONG_INT@ +# define uint64_t unsigned long long int +#endif + +/* Avoid collision with Solaris 2.5.1 etc. */ +#define _UINT8_T +#define _UINT32_T +#define _UINT64_T + + +/* 7.18.1.2. Minimum-width integer types */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. Therefore the leastN_t types + are the same as the corresponding N_t types. */ + +#undef int_least8_t +#undef uint_least8_t +#undef int_least16_t +#undef uint_least16_t +#undef int_least32_t +#undef uint_least32_t +#undef int_least64_t +#undef uint_least64_t +#define int_least8_t int8_t +#define uint_least8_t uint8_t +#define int_least16_t int16_t +#define uint_least16_t uint16_t +#define int_least32_t int32_t +#define uint_least32_t uint32_t +#ifdef int64_t +# define int_least64_t int64_t +#endif +#ifdef uint64_t +# define uint_least64_t uint64_t +#endif + +/* 7.18.1.3. Fastest minimum-width integer types */ + +/* Note: Other substitutes may define these types differently. + It is not recommended to use these types in public header files. */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. Therefore the fastN_t types + are taken from the same list of types. Assume that 'long int' + is fast enough for all narrower integers. */ + +#undef int_fast8_t +#undef uint_fast8_t +#undef int_fast16_t +#undef uint_fast16_t +#undef int_fast32_t +#undef uint_fast32_t +#undef int_fast64_t +#undef uint_fast64_t +#define int_fast8_t long int +#define uint_fast8_t unsigned int_fast8_t +#define int_fast16_t long int +#define uint_fast16_t unsigned int_fast16_t +#define int_fast32_t long int +#define uint_fast32_t unsigned int_fast32_t +#ifdef int64_t +# define int_fast64_t int64_t +#endif +#ifdef uint64_t +# define uint_fast64_t uint64_t +#endif + +/* 7.18.1.4. Integer types capable of holding object pointers */ + +#undef intptr_t +#undef uintptr_t +#define intptr_t long int +#define uintptr_t unsigned long int + +/* 7.18.1.5. Greatest-width integer types */ + +/* Note: These types are compiler dependent. It may be unwise to use them in + public header files. */ + +#undef intmax_t +#if @HAVE_LONG_LONG_INT@ && LONG_MAX >> 30 == 1 +# define intmax_t long long int +#elif defined int64_t +# define intmax_t int64_t +#else +# define intmax_t long int +#endif + +#undef uintmax_t +#if @HAVE_UNSIGNED_LONG_LONG_INT@ && ULONG_MAX >> 31 == 1 +# define uintmax_t unsigned long long int +#elif defined uint64_t +# define uintmax_t uint64_t +#else +# define uintmax_t unsigned long int +#endif + +/* 7.18.2. Limits of specified-width integer types */ + +#if ! defined __cplusplus || defined __STDC_LIMIT_MACROS + +/* 7.18.2.1. Limits of exact-width integer types */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. */ + +#undef INT8_MIN +#undef INT8_MAX +#undef UINT8_MAX +#define INT8_MIN (~ INT8_MAX) +#define INT8_MAX 127 +#define UINT8_MAX 255 + +#undef INT16_MIN +#undef INT16_MAX +#undef UINT16_MAX +#define INT16_MIN (~ INT16_MAX) +#define INT16_MAX 32767 +#define UINT16_MAX 65535 + +#undef INT32_MIN +#undef INT32_MAX +#undef UINT32_MAX +#define INT32_MIN (~ INT32_MAX) +#define INT32_MAX 2147483647 +#define UINT32_MAX 4294967295U + +#undef INT64_MIN +#undef INT64_MAX +#ifdef int64_t +# define INT64_MIN (~ INT64_MAX) +# define INT64_MAX INTMAX_C (9223372036854775807) +#endif + +#undef UINT64_MAX +#ifdef uint64_t +# define UINT64_MAX UINTMAX_C (18446744073709551615) +#endif + +/* 7.18.2.2. Limits of minimum-width integer types */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. Therefore the leastN_t types + are the same as the corresponding N_t types. */ + +#undef INT_LEAST8_MIN +#undef INT_LEAST8_MAX +#undef UINT_LEAST8_MAX +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define UINT_LEAST8_MAX UINT8_MAX + +#undef INT_LEAST16_MIN +#undef INT_LEAST16_MAX +#undef UINT_LEAST16_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define UINT_LEAST16_MAX UINT16_MAX + +#undef INT_LEAST32_MIN +#undef INT_LEAST32_MAX +#undef UINT_LEAST32_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define UINT_LEAST32_MAX UINT32_MAX + +#undef INT_LEAST64_MIN +#undef INT_LEAST64_MAX +#ifdef int64_t +# define INT_LEAST64_MIN INT64_MIN +# define INT_LEAST64_MAX INT64_MAX +#endif + +#undef UINT_LEAST64_MAX +#ifdef uint64_t +# define UINT_LEAST64_MAX UINT64_MAX +#endif + +/* 7.18.2.3. Limits of fastest minimum-width integer types */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. Therefore the fastN_t types + are taken from the same list of types. */ + +#undef INT_FAST8_MIN +#undef INT_FAST8_MAX +#undef UINT_FAST8_MAX +#define INT_FAST8_MIN LONG_MIN +#define INT_FAST8_MAX LONG_MAX +#define UINT_FAST8_MAX ULONG_MAX + +#undef INT_FAST16_MIN +#undef INT_FAST16_MAX +#undef UINT_FAST16_MAX +#define INT_FAST16_MIN LONG_MIN +#define INT_FAST16_MAX LONG_MAX +#define UINT_FAST16_MAX ULONG_MAX + +#undef INT_FAST32_MIN +#undef INT_FAST32_MAX +#undef UINT_FAST32_MAX +#define INT_FAST32_MIN LONG_MIN +#define INT_FAST32_MAX LONG_MAX +#define UINT_FAST32_MAX ULONG_MAX + +#undef INT_FAST64_MIN +#undef INT_FAST64_MAX +#ifdef int64_t +# define INT_FAST64_MIN INT64_MIN +# define INT_FAST64_MAX INT64_MAX +#endif + +#undef UINT_FAST64_MAX +#ifdef uint64_t +# define UINT_FAST64_MAX UINT64_MAX +#endif + +/* 7.18.2.4. Limits of integer types capable of holding object pointers */ + +#undef INTPTR_MIN +#undef INTPTR_MAX +#undef UINTPTR_MAX +#define INTPTR_MIN LONG_MIN +#define INTPTR_MAX LONG_MAX +#define UINTPTR_MAX ULONG_MAX + +/* 7.18.2.5. Limits of greatest-width integer types */ + +#undef INTMAX_MIN +#undef INTMAX_MAX +#define INTMAX_MIN (~ INTMAX_MAX) +#ifdef INT64_MAX +# define INTMAX_MAX INT64_MAX +#else +# define INTMAX_MAX INT32_MAX +#endif + +#undef UINTMAX_MAX +#ifdef UINT64_MAX +# define UINTMAX_MAX UINT64_MAX +#else +# define UINTMAX_MAX UINT32_MAX +#endif + +/* 7.18.3. Limits of other integer types */ + +/* ptrdiff_t limits */ +#undef PTRDIFF_MIN +#undef PTRDIFF_MAX +#define PTRDIFF_MIN \ + _STDINT_MIN (1, @BITSIZEOF_PTRDIFF_T@, 0@PTRDIFF_T_SUFFIX@) +#define PTRDIFF_MAX \ + _STDINT_MAX (1, @BITSIZEOF_PTRDIFF_T@, 0@PTRDIFF_T_SUFFIX@) + +/* sig_atomic_t limits */ +#undef SIG_ATOMIC_MIN +#undef SIG_ATOMIC_MAX +#define SIG_ATOMIC_MIN \ + _STDINT_MIN (@HAVE_SIGNED_SIG_ATOMIC_T@, @BITSIZEOF_SIG_ATOMIC_T@, \ + 0@SIG_ATOMIC_T_SUFFIX@) +#define SIG_ATOMIC_MAX \ + _STDINT_MAX (@HAVE_SIGNED_SIG_ATOMIC_T@, @BITSIZEOF_SIG_ATOMIC_T@, \ + 0@SIG_ATOMIC_T_SUFFIX@) + + +/* size_t limit */ +#undef SIZE_MAX +#define SIZE_MAX _STDINT_MAX (0, @BITSIZEOF_SIZE_T@, 0@SIZE_T_SUFFIX@) + +/* wchar_t limits */ +#undef WCHAR_MIN +#undef WCHAR_MAX +#define WCHAR_MIN \ + _STDINT_MIN (@HAVE_SIGNED_WCHAR_T@, @BITSIZEOF_WCHAR_T@, 0@WCHAR_T_SUFFIX@) +#define WCHAR_MAX \ + _STDINT_MAX (@HAVE_SIGNED_WCHAR_T@, @BITSIZEOF_WCHAR_T@, 0@WCHAR_T_SUFFIX@) + +/* wint_t limits */ +#undef WINT_MIN +#undef WINT_MAX +#define WINT_MIN \ + _STDINT_MIN (@HAVE_SIGNED_WINT_T@, @BITSIZEOF_WINT_T@, 0@WINT_T_SUFFIX@) +#define WINT_MAX \ + _STDINT_MAX (@HAVE_SIGNED_WINT_T@, @BITSIZEOF_WINT_T@, 0@WINT_T_SUFFIX@) + +#endif /* !defined __cplusplus || defined __STDC_LIMIT_MACROS */ + +/* 7.18.4. Macros for integer constants */ + +#if ! defined __cplusplus || defined __STDC_CONSTANT_MACROS + +/* 7.18.4.1. Macros for minimum-width integer constants */ +/* According to ISO C 99 Technical Corrigendum 1 */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits, and int is 32 bits. */ + +#undef INT8_C +#undef UINT8_C +#define INT8_C(x) x +#define UINT8_C(x) x + +#undef INT16_C +#undef UINT16_C +#define INT16_C(x) x +#define UINT16_C(x) x + +#undef INT32_C +#undef UINT32_C +#define INT32_C(x) x +#define UINT32_C(x) x ## U + +#undef INT64_C +#undef UINT64_C +#if LONG_MAX >> 31 >> 31 == 1 +# define INT64_C(x) x##L +#elif defined _MSC_VER +# define INT64_C(x) x##i64 +#elif @HAVE_LONG_LONG_INT@ +# define INT64_C(x) x##LL +#endif +#if ULONG_MAX >> 31 >> 31 >> 1 == 1 +# define UINT64_C(x) x##UL +#elif defined _MSC_VER +# define UINT64_C(x) x##ui64 +#elif @HAVE_UNSIGNED_LONG_LONG_INT@ +# define UINT64_C(x) x##ULL +#endif + +/* 7.18.4.2. Macros for greatest-width integer constants */ + +#undef INTMAX_C +#if @HAVE_LONG_LONG_INT@ && LONG_MAX >> 30 == 1 +# define INTMAX_C(x) x##LL +#elif defined int64_t +# define INTMAX_C(x) INT64_C(x) +#else +# define INTMAX_C(x) x##L +#endif + +#undef UINTMAX_C +#if @HAVE_UNSIGNED_LONG_LONG_INT@ && ULONG_MAX >> 31 == 1 +# define UINTMAX_C(x) x##ULL +#elif defined uint64_t +# define UINTMAX_C(x) UINT64_C(x) +#else +# define UINTMAX_C(x) x##UL +#endif + +#endif /* !defined __cplusplus || defined __STDC_CONSTANT_MACROS */ + +#endif /* _GL_STDINT_H */ diff --git a/src/bin/m4/lib/stdio--.h b/src/bin/m4/lib/stdio--.h new file mode 100644 index 0000000000..2385e626ff --- /dev/null +++ b/src/bin/m4/lib/stdio--.h @@ -0,0 +1,28 @@ +/* Like stdio.h, but redefine some names to avoid glitches. + + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#include +#include "stdio-safer.h" + +#undef fopen +#define fopen fopen_safer + +#undef tmpfile +#define tmpfile tmpfile_safer diff --git a/src/bin/m4/lib/stdio-safer.h b/src/bin/m4/lib/stdio-safer.h new file mode 100644 index 0000000000..8329a1a093 --- /dev/null +++ b/src/bin/m4/lib/stdio-safer.h @@ -0,0 +1,24 @@ +/* Invoke stdio functions, but avoid some glitches. + + Copyright (C) 2001, 2003, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#include + +FILE *fopen_safer (char const *, char const *); +FILE *tmpfile_safer (void); diff --git a/src/bin/m4/lib/stdlib--.h b/src/bin/m4/lib/stdlib--.h new file mode 100644 index 0000000000..b43b3dae6d --- /dev/null +++ b/src/bin/m4/lib/stdlib--.h @@ -0,0 +1,26 @@ +/* Like stdlib.h, but redefine some names to avoid glitches. + + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#include +#include "stdlib-safer.h" + +#include "mkstemp.h" +#undef mkstemp +#define mkstemp mkstemp_safer diff --git a/src/bin/m4/lib/stdlib-safer.h b/src/bin/m4/lib/stdlib-safer.h new file mode 100644 index 0000000000..8b1a7e11e5 --- /dev/null +++ b/src/bin/m4/lib/stdlib-safer.h @@ -0,0 +1,21 @@ +/* Invoke stdlib.h functions, but avoid some glitches. + + Copyright (C) 2005 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +int mkstemp_safer (char *); diff --git a/src/bin/m4/lib/strcase.h b/src/bin/m4/lib/strcase.h new file mode 100644 index 0000000000..07d4c9a42b --- /dev/null +++ b/src/bin/m4/lib/strcase.h @@ -0,0 +1,56 @@ +/* Case-insensitive string comparison functions. + Copyright (C) 1995-1996, 2001, 2003, 2005-2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _STRCASE_H +#define _STRCASE_H + +#include +/* Include header files with a possibly conflicting declarations of strcasecmp + before we define it as a macro, so that they will be no-ops if included + after strcasecmp is defined as a macro. */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* No known system has a strcasecmp() function that works correctly in + multibyte locales. Therefore we use our version always. */ +#define strcasecmp rpl_strcasecmp +/* Compare strings S1 and S2, ignoring case, returning less than, equal to or + greater than zero if S1 is lexicographically less than, equal to or greater + than S2. + Note: This function may, in multibyte locales, return 0 for strings of + different lengths! */ +extern int strcasecmp (const char *s1, const char *s2); + +/* Compare no more than N characters of strings S1 and S2, ignoring case, + returning less than, equal to or greater than zero if S1 is + lexicographically less than, equal to or greater than S2. + Note: This function can not work correctly in multibyte locales. */ +#if ! HAVE_DECL_STRNCASECMP +extern int strncasecmp (const char *s1, const char *s2, size_t n); +#endif + + +#ifdef __cplusplus +} +#endif + + +#endif /* _STRCASE_H */ diff --git a/src/bin/m4/lib/strcasecmp.c b/src/bin/m4/lib/strcasecmp.c new file mode 100644 index 0000000000..99d5dd22a3 --- /dev/null +++ b/src/bin/m4/lib/strcasecmp.c @@ -0,0 +1,103 @@ +/* Case-insensitive string comparison function. + Copyright (C) 1998-1999, 2005-2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2005, + based on earlier glibc code. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "strcase.h" + +#include +#include + +#if HAVE_MBRTOWC +# include "mbuiter.h" +#endif + +#define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch)) + +/* Compare strings S1 and S2, ignoring case, returning less than, equal to or + greater than zero if S1 is lexicographically less than, equal to or greater + than S2. + Note: This function may, in multibyte locales, return 0 for strings of + different lengths! */ +int +strcasecmp (const char *s1, const char *s2) +{ + if (s1 == s2) + return 0; + + /* Be careful not to look at the entire extent of s1 or s2 until needed. + This is useful because when two strings differ, the difference is + most often already in the very few first characters. */ +#if HAVE_MBRTOWC + if (MB_CUR_MAX > 1) + { + mbui_iterator_t iter1; + mbui_iterator_t iter2; + + mbui_init (iter1, s1); + mbui_init (iter2, s2); + + while (mbui_avail (iter1) && mbui_avail (iter2)) + { + int cmp = mb_casecmp (mbui_cur (iter1), mbui_cur (iter2)); + + if (cmp != 0) + return cmp; + + mbui_advance (iter1); + mbui_advance (iter2); + } + if (mbui_avail (iter1)) + /* s2 terminated before s1. */ + return 1; + if (mbui_avail (iter2)) + /* s1 terminated before s2. */ + return -1; + return 0; + } + else +#endif + { + const unsigned char *p1 = (const unsigned char *) s1; + const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + + do + { + c1 = TOLOWER (*p1); + c2 = TOLOWER (*p2); + + if (c1 == '\0') + break; + + ++p1; + ++p2; + } + while (c1 == c2); + + if (UCHAR_MAX <= INT_MAX) + return c1 - c2; + else + /* On machines where 'char' and 'int' are types of the same size, the + difference of two 'unsigned char' values - including the sign bit - + doesn't fit in an 'int'. */ + return (c1 > c2 ? 1 : c1 < c2 ? -1 : 0); + } +} diff --git a/src/bin/m4/lib/strncasecmp.c b/src/bin/m4/lib/strncasecmp.c new file mode 100644 index 0000000000..f59b953a7d --- /dev/null +++ b/src/bin/m4/lib/strncasecmp.c @@ -0,0 +1,63 @@ +/* strncasecmp.c -- case insensitive string comparator + Copyright (C) 1998-1999, 2005-2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "strcase.h" + +#include +#include + +#define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch)) + +/* Compare no more than N bytes of strings S1 and S2, + ignoring case, returning less than, equal to or + greater than zero if S1 is lexicographically less + than, equal to or greater than S2. */ + +int +strncasecmp (const char *s1, const char *s2, size_t n) +{ + register const unsigned char *p1 = (const unsigned char *) s1; + register const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + + if (p1 == p2 || n == 0) + return 0; + + do + { + c1 = TOLOWER (*p1); + c2 = TOLOWER (*p2); + + if (--n == 0 || c1 == '\0') + break; + + ++p1; + ++p2; + } + while (c1 == c2); + + if (UCHAR_MAX <= INT_MAX) + return c1 - c2; + else + /* On machines where 'char' and 'int' are types of the same size, the + difference of two 'unsigned char' values - including the sign bit - + doesn't fit in an 'int'. */ + return (c1 > c2 ? 1 : c1 < c2 ? -1 : 0); +} diff --git a/src/bin/m4/lib/strnlen1.c b/src/bin/m4/lib/strnlen1.c new file mode 100644 index 0000000000..422ed9e80c --- /dev/null +++ b/src/bin/m4/lib/strnlen1.c @@ -0,0 +1,36 @@ +/* Find the length of STRING + 1, but scan at most MAXLEN bytes. + Copyright (C) 2005-2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "strnlen1.h" + +#include + +/* Find the length of STRING + 1, but scan at most MAXLEN bytes. + If no '\0' terminator is found in that many characters, return MAXLEN. */ +/* This is the same as strnlen (string, maxlen - 1) + 1. */ +size_t +strnlen1 (const char *string, size_t maxlen) +{ + const char *end = (const char *) memchr (string, '\0', maxlen); + if (end != NULL) + return end - string + 1; + else + return maxlen; +} diff --git a/src/bin/m4/lib/strnlen1.h b/src/bin/m4/lib/strnlen1.h new file mode 100644 index 0000000000..7ce7d0c803 --- /dev/null +++ b/src/bin/m4/lib/strnlen1.h @@ -0,0 +1,40 @@ +/* Find the length of STRING + 1, but scan at most MAXLEN bytes. + Copyright (C) 2005 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _STRNLEN1_H +#define _STRNLEN1_H + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Find the length of STRING + 1, but scan at most MAXLEN bytes. + If no '\0' terminator is found in that many characters, return MAXLEN. */ +/* This is the same as strnlen (string, maxlen - 1) + 1. */ +extern size_t strnlen1 (const char *string, size_t maxlen); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _STRNLEN1_H */ diff --git a/src/bin/m4/lib/strstr.c b/src/bin/m4/lib/strstr.c new file mode 100644 index 0000000000..f1e2ef6988 --- /dev/null +++ b/src/bin/m4/lib/strstr.c @@ -0,0 +1,126 @@ +/* Searching in a string. + Copyright (C) 2005-2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2005. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "strstr.h" + +#include /* for NULL */ + +#if HAVE_MBRTOWC +# include "mbuiter.h" +#endif + +/* Find the first occurrence of NEEDLE in HAYSTACK. */ +char * +strstr (const char *haystack, const char *needle) +{ + /* Be careful not to look at the entire extent of haystack or needle + until needed. This is useful because of these two cases: + - haystack may be very long, and a match of needle found early, + - needle may be very long, and not even a short initial segment of + needle may be found in haystack. */ +#if HAVE_MBRTOWC + if (MB_CUR_MAX > 1) + { + mbui_iterator_t iter_needle; + + mbui_init (iter_needle, needle); + if (mbui_avail (iter_needle)) + { + mbui_iterator_t iter_haystack; + + mbui_init (iter_haystack, haystack); + for (;; mbui_advance (iter_haystack)) + { + if (!mbui_avail (iter_haystack)) + /* No match. */ + return NULL; + + if (mb_equal (mbui_cur (iter_haystack), mbui_cur (iter_needle))) + /* The first character matches. */ + { + mbui_iterator_t rhaystack; + mbui_iterator_t rneedle; + + memcpy (&rhaystack, &iter_haystack, sizeof (mbui_iterator_t)); + mbui_advance (rhaystack); + + mbui_init (rneedle, needle); + if (!mbui_avail (rneedle)) + abort (); + mbui_advance (rneedle); + + for (;; mbui_advance (rhaystack), mbui_advance (rneedle)) + { + if (!mbui_avail (rneedle)) + /* Found a match. */ + return (char *) mbui_cur_ptr (iter_haystack); + if (!mbui_avail (rhaystack)) + /* No match. */ + return NULL; + if (!mb_equal (mbui_cur (rhaystack), mbui_cur (rneedle))) + /* Nothing in this round. */ + break; + } + } + } + } + else + return (char *) haystack; + } + else +#endif + { + if (*needle != '\0') + { + /* Speed up the following searches of needle by caching its first + character. */ + char b = *needle++; + + for (;; haystack++) + { + if (*haystack == '\0') + /* No match. */ + return NULL; + if (*haystack == b) + /* The first character matches. */ + { + const char *rhaystack = haystack + 1; + const char *rneedle = needle; + + for (;; rhaystack++, rneedle++) + { + if (*rneedle == '\0') + /* Found a match. */ + return (char *) haystack; + if (*rhaystack == '\0') + /* No match. */ + return NULL; + if (*rhaystack != *rneedle) + /* Nothing in this round. */ + break; + } + } + } + } + else + return (char *) haystack; + } +} diff --git a/src/bin/m4/lib/strstr.h b/src/bin/m4/lib/strstr.h new file mode 100644 index 0000000000..a28b1401ea --- /dev/null +++ b/src/bin/m4/lib/strstr.h @@ -0,0 +1,37 @@ +/* Searching in a string. + Copyright (C) 2001-2003, 2005 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + + +/* Include string.h: on glibc systems, it contains a macro definition of + strstr() that would collide with our definition if included afterwards. */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* No known system has a strstr() function that works correctly in + multibyte locales. Therefore we use our version always. */ +#undef strstr +#define strstr rpl_strstr + +/* Find the first occurrence of NEEDLE in HAYSTACK. */ +extern char *strstr (const char *haystack, const char *needle); + +#ifdef __cplusplus +} +#endif diff --git a/src/bin/m4/lib/strtol.c b/src/bin/m4/lib/strtol.c new file mode 100644 index 0000000000..e409d28727 --- /dev/null +++ b/src/bin/m4/lib/strtol.c @@ -0,0 +1,437 @@ +/* Convert string representation of a number into an integer value. + + Copyright (C) 1991, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2003, 2005, 2006 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@gnu.org. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifdef _LIBC +# define USE_NUMBER_GROUPING +#else +# include +#endif + +#include +#include +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + +#include +#include +#include +#include + +#ifdef USE_NUMBER_GROUPING +# include "../locale/localeinfo.h" +#endif + +/* Nonzero if we are defining `strtoul' or `strtoull', operating on + unsigned integers. */ +#ifndef UNSIGNED +# define UNSIGNED 0 +# define INT LONG int +#else +# define INT unsigned LONG int +#endif + +/* Determine the name. */ +#ifdef USE_IN_EXTENDED_LOCALE_MODEL +# if UNSIGNED +# ifdef USE_WIDE_CHAR +# ifdef QUAD +# define strtol __wcstoull_l +# else +# define strtol __wcstoul_l +# endif +# else +# ifdef QUAD +# define strtol __strtoull_l +# else +# define strtol __strtoul_l +# endif +# endif +# else +# ifdef USE_WIDE_CHAR +# ifdef QUAD +# define strtol __wcstoll_l +# else +# define strtol __wcstol_l +# endif +# else +# ifdef QUAD +# define strtol __strtoll_l +# else +# define strtol __strtol_l +# endif +# endif +# endif +#else +# if UNSIGNED +# ifdef USE_WIDE_CHAR +# ifdef QUAD +# define strtol wcstoull +# else +# define strtol wcstoul +# endif +# else +# ifdef QUAD +# define strtol strtoull +# else +# define strtol strtoul +# endif +# endif +# else +# ifdef USE_WIDE_CHAR +# ifdef QUAD +# define strtol wcstoll +# else +# define strtol wcstol +# endif +# else +# ifdef QUAD +# define strtol strtoll +# endif +# endif +# endif +#endif + +/* If QUAD is defined, we are defining `strtoll' or `strtoull', + operating on `long long int's. */ +#ifdef QUAD +# define LONG long long +# define STRTOL_LONG_MIN LONG_LONG_MIN +# define STRTOL_LONG_MAX LONG_LONG_MAX +# define STRTOL_ULONG_MAX ULONG_LONG_MAX + +/* The extra casts in the following macros work around compiler bugs, + e.g., in Cray C 5.0.3.0. */ + +/* True if negative values of the signed integer type T use two's + complement, ones' complement, or signed magnitude representation, + respectively. Much GNU code assumes two's complement, but some + people like to be portable to all possible C hosts. */ +# define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1) +# define TYPE_ONES_COMPLEMENT(t) ((t) ~ (t) 0 == 0) +# define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1) + +/* True if the arithmetic type T is signed. */ +# define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + +/* The maximum and minimum values for the integer type T. These + macros have undefined behavior if T is signed and has padding bits. + If this is a problem for you, please let us know how to fix it for + your host. */ +# define TYPE_MINIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) 0 \ + : TYPE_SIGNED_MAGNITUDE (t) \ + ? ~ (t) 0 \ + : ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))) +# define TYPE_MAXIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) -1 \ + : ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1)))) + +# ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX TYPE_MAXIMUM (unsigned long long) +# endif +# ifndef LONG_LONG_MAX +# define LONG_LONG_MAX TYPE_MAXIMUM (long long int) +# endif +# ifndef LONG_LONG_MIN +# define LONG_LONG_MIN TYPE_MINIMUM (long long int) +# endif + +# if __GNUC__ == 2 && __GNUC_MINOR__ < 7 + /* Work around gcc bug with using this constant. */ + static const unsigned long long int maxquad = ULONG_LONG_MAX; +# undef STRTOL_ULONG_MAX +# define STRTOL_ULONG_MAX maxquad +# endif +#else +# define LONG long +# define STRTOL_LONG_MIN LONG_MIN +# define STRTOL_LONG_MAX LONG_MAX +# define STRTOL_ULONG_MAX ULONG_MAX +#endif + + +/* We use this code also for the extended locale handling where the + function gets as an additional argument the locale which has to be + used. To access the values we have to redefine the _NL_CURRENT + macro. */ +#ifdef USE_IN_EXTENDED_LOCALE_MODEL +# undef _NL_CURRENT +# define _NL_CURRENT(category, item) \ + (current->values[_NL_ITEM_INDEX (item)].string) +# define LOCALE_PARAM , loc +# define LOCALE_PARAM_PROTO , __locale_t loc +#else +# define LOCALE_PARAM +# define LOCALE_PARAM_PROTO +#endif + +#if defined _LIBC || defined HAVE_WCHAR_H +# include +#endif + +#ifdef USE_WIDE_CHAR +# include +# define L_(Ch) L##Ch +# define UCHAR_TYPE wint_t +# define STRING_TYPE wchar_t +# ifdef USE_IN_EXTENDED_LOCALE_MODEL +# define ISSPACE(Ch) __iswspace_l ((Ch), loc) +# define ISALPHA(Ch) __iswalpha_l ((Ch), loc) +# define TOUPPER(Ch) __towupper_l ((Ch), loc) +# else +# define ISSPACE(Ch) iswspace (Ch) +# define ISALPHA(Ch) iswalpha (Ch) +# define TOUPPER(Ch) towupper (Ch) +# endif +#else +# define L_(Ch) Ch +# define UCHAR_TYPE unsigned char +# define STRING_TYPE char +# ifdef USE_IN_EXTENDED_LOCALE_MODEL +# define ISSPACE(Ch) __isspace_l ((Ch), loc) +# define ISALPHA(Ch) __isalpha_l ((Ch), loc) +# define TOUPPER(Ch) __toupper_l ((Ch), loc) +# else +# define ISSPACE(Ch) isspace (Ch) +# define ISALPHA(Ch) isalpha (Ch) +# define TOUPPER(Ch) toupper (Ch) +# endif +#endif + +#define INTERNAL(X) INTERNAL1(X) +#define INTERNAL1(X) __##X##_internal +#define WEAKNAME(X) WEAKNAME1(X) + +#ifdef USE_NUMBER_GROUPING +/* This file defines a function to check for correct grouping. */ +# include "grouping.h" +#endif + + + +/* Convert NPTR to an `unsigned long int' or `long int' in base BASE. + If BASE is 0 the base is determined by the presence of a leading + zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal. + If BASE is < 2 or > 36, it is reset to 10. + If ENDPTR is not NULL, a pointer to the character after the last + one converted is stored in *ENDPTR. */ + +INT +INTERNAL (strtol) (const STRING_TYPE *nptr, STRING_TYPE **endptr, + int base, int group LOCALE_PARAM_PROTO) +{ + int negative; + register unsigned LONG int cutoff; + register unsigned int cutlim; + register unsigned LONG int i; + register const STRING_TYPE *s; + register UCHAR_TYPE c; + const STRING_TYPE *save, *end; + int overflow; + +#ifdef USE_NUMBER_GROUPING +# ifdef USE_IN_EXTENDED_LOCALE_MODEL + struct locale_data *current = loc->__locales[LC_NUMERIC]; +# endif + /* The thousands character of the current locale. */ + wchar_t thousands = L'\0'; + /* The numeric grouping specification of the current locale, + in the format described in . */ + const char *grouping; + + if (group) + { + grouping = _NL_CURRENT (LC_NUMERIC, GROUPING); + if (*grouping <= 0 || *grouping == CHAR_MAX) + grouping = NULL; + else + { + /* Figure out the thousands separator character. */ +# if defined _LIBC || defined _HAVE_BTOWC + thousands = __btowc (*_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP)); + if (thousands == WEOF) + thousands = L'\0'; +# endif + if (thousands == L'\0') + grouping = NULL; + } + } + else + grouping = NULL; +#endif + + if (base < 0 || base == 1 || base > 36) + { + __set_errno (EINVAL); + return 0; + } + + save = s = nptr; + + /* Skip white space. */ + while (ISSPACE (*s)) + ++s; + if (*s == L_('\0')) + goto noconv; + + /* Check for a sign. */ + if (*s == L_('-')) + { + negative = 1; + ++s; + } + else if (*s == L_('+')) + { + negative = 0; + ++s; + } + else + negative = 0; + + /* Recognize number prefix and if BASE is zero, figure it out ourselves. */ + if (*s == L_('0')) + { + if ((base == 0 || base == 16) && TOUPPER (s[1]) == L_('X')) + { + s += 2; + base = 16; + } + else if (base == 0) + base = 8; + } + else if (base == 0) + base = 10; + + /* Save the pointer so we can check later if anything happened. */ + save = s; + +#ifdef USE_NUMBER_GROUPING + if (group) + { + /* Find the end of the digit string and check its grouping. */ + end = s; + for (c = *end; c != L_('\0'); c = *++end) + if ((wchar_t) c != thousands + && ((wchar_t) c < L_('0') || (wchar_t) c > L_('9')) + && (!ISALPHA (c) || (int) (TOUPPER (c) - L_('A') + 10) >= base)) + break; + if (*s == thousands) + end = s; + else + end = correctly_grouped_prefix (s, end, thousands, grouping); + } + else +#endif + end = NULL; + + cutoff = STRTOL_ULONG_MAX / (unsigned LONG int) base; + cutlim = STRTOL_ULONG_MAX % (unsigned LONG int) base; + + overflow = 0; + i = 0; + for (c = *s; c != L_('\0'); c = *++s) + { + if (s == end) + break; + if (c >= L_('0') && c <= L_('9')) + c -= L_('0'); + else if (ISALPHA (c)) + c = TOUPPER (c) - L_('A') + 10; + else + break; + if ((int) c >= base) + break; + /* Check for overflow. */ + if (i > cutoff || (i == cutoff && c > cutlim)) + overflow = 1; + else + { + i *= (unsigned LONG int) base; + i += c; + } + } + + /* Check if anything actually happened. */ + if (s == save) + goto noconv; + + /* Store in ENDPTR the address of one character + past the last character we converted. */ + if (endptr != NULL) + *endptr = (STRING_TYPE *) s; + +#if !UNSIGNED + /* Check for a value that is within the range of + `unsigned LONG int', but outside the range of `LONG int'. */ + if (overflow == 0 + && i > (negative + ? -((unsigned LONG int) (STRTOL_LONG_MIN + 1)) + 1 + : (unsigned LONG int) STRTOL_LONG_MAX)) + overflow = 1; +#endif + + if (overflow) + { + __set_errno (ERANGE); +#if UNSIGNED + return STRTOL_ULONG_MAX; +#else + return negative ? STRTOL_LONG_MIN : STRTOL_LONG_MAX; +#endif + } + + /* Return the result of the appropriate sign. */ + return negative ? -i : i; + +noconv: + /* We must handle a special case here: the base is 0 or 16 and the + first two characters are '0' and 'x', but the rest are no + hexadecimal digits. This is no error case. We return 0 and + ENDPTR points to the `x`. */ + if (endptr != NULL) + { + if (save - nptr >= 2 && TOUPPER (save[-1]) == L_('X') + && save[-2] == L_('0')) + *endptr = (STRING_TYPE *) &save[-1]; + else + /* There was no number to convert. */ + *endptr = (STRING_TYPE *) nptr; + } + + return 0L; +} + +/* External user entry point. */ + + +INT +#ifdef weak_function +weak_function +#endif +strtol (const STRING_TYPE *nptr, STRING_TYPE **endptr, + int base LOCALE_PARAM_PROTO) +{ + return INTERNAL (strtol) (nptr, endptr, base, 0 LOCALE_PARAM); +} diff --git a/src/bin/m4/lib/tempname.c b/src/bin/m4/lib/tempname.c new file mode 100644 index 0000000000..919badb3dc --- /dev/null +++ b/src/bin/m4/lib/tempname.c @@ -0,0 +1,322 @@ +/* tempname.c - generate the name of a temporary file. + + Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2002, 2003, 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Extracted from glibc sysdeps/posix/tempname.c. See also tmpdir.c. */ + +#if !_LIBC +# include +# include "tempname.h" +#endif + +#include +#include + +#include +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + +#include +#ifndef P_tmpdir +# define P_tmpdir "/tmp" +#endif +#ifndef TMP_MAX +# define TMP_MAX 238328 +#endif +#ifndef __GT_FILE +# define __GT_FILE 0 +# define __GT_BIGFILE 1 +# define __GT_DIR 2 +# define __GT_NOCREATE 3 +#endif + +#include +#include +#include + +#include + +#if HAVE_SYS_TIME_H || _LIBC +# include +#endif + +#include +#include + +#include + +#if _LIBC +# define struct_stat64 struct stat64 +# define small_open __open +# define large_open __open64 +#else +# define struct_stat64 struct stat +# define small_open open +# define large_open open +# define __gen_tempname gen_tempname +# define __getpid getpid +# define __gettimeofday gettimeofday +# define __mkdir mkdir +# define __lxstat64(version, file, buf) lstat (file, buf) +# define __xstat64(version, file, buf) stat (file, buf) +#endif + +#if ! (HAVE___SECURE_GETENV || _LIBC) +# define __secure_getenv getenv +#endif + +#ifdef _LIBC +# include +# if HP_TIMING_AVAIL +# define RANDOM_BITS(Var) \ + if (__builtin_expect (value == UINT64_C (0), 0)) \ + { \ + /* If this is the first time this function is used initialize \ + the variable we accumulate the value in to some somewhat \ + random value. If we'd not do this programs at startup time \ + might have a reduced set of possible names, at least on slow \ + machines. */ \ + struct timeval tv; \ + __gettimeofday (&tv, NULL); \ + value = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; \ + } \ + HP_TIMING_NOW (Var) +# endif +#endif + +/* Use the widest available unsigned type if uint64_t is not + available. The algorithm below extracts a number less than 62**6 + (approximately 2**35.725) from uint64_t, so ancient hosts where + uintmax_t is only 32 bits lose about 3.725 bits of randomness, + which is better than not having mkstemp at all. */ +#if !defined UINT64_MAX && !defined uint64_t +# define uint64_t uintmax_t +#endif + +#if _LIBC +/* Return nonzero if DIR is an existent directory. */ +static int +direxists (const char *dir) +{ + struct_stat64 buf; + return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode); +} + +/* Path search algorithm, for tmpnam, tmpfile, etc. If DIR is + non-null and exists, uses it; otherwise uses the first of $TMPDIR, + P_tmpdir, /tmp that exists. Copies into TMPL a template suitable + for use with mk[s]temp. Will fail (-1) if DIR is non-null and + doesn't exist, none of the searched dirs exists, or there's not + enough space in TMPL. */ +int +__path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx, + int try_tmpdir) +{ + const char *d; + size_t dlen, plen; + + if (!pfx || !pfx[0]) + { + pfx = "file"; + plen = 4; + } + else + { + plen = strlen (pfx); + if (plen > 5) + plen = 5; + } + + if (try_tmpdir) + { + d = __secure_getenv ("TMPDIR"); + if (d != NULL && direxists (d)) + dir = d; + else if (dir != NULL && direxists (dir)) + /* nothing */ ; + else + dir = NULL; + } + if (dir == NULL) + { + if (direxists (P_tmpdir)) + dir = P_tmpdir; + else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp")) + dir = "/tmp"; + else + { + __set_errno (ENOENT); + return -1; + } + } + + dlen = strlen (dir); + while (dlen > 1 && dir[dlen - 1] == '/') + dlen--; /* remove trailing slashes */ + + /* check we have room for "${dir}/${pfx}XXXXXX\0" */ + if (tmpl_len < dlen + 1 + plen + 6 + 1) + { + __set_errno (EINVAL); + return -1; + } + + sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx); + return 0; +} +#endif /* _LIBC */ + +/* These are the characters used in temporary file names. */ +static const char letters[] = +"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +/* Generate a temporary file name based on TMPL. TMPL must match the + rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed + does not exist at the time of the call to __gen_tempname. TMPL is + overwritten with the result. + + KIND may be one of: + __GT_NOCREATE: simply verify that the name does not exist + at the time of the call. + __GT_FILE: create the file using open(O_CREAT|O_EXCL) + and return a read-write fd. The file is mode 0600. + __GT_BIGFILE: same as __GT_FILE but use open64(). + __GT_DIR: create a directory, which will be mode 0700. + + We use a clever algorithm to get hard-to-predict names. */ +int +__gen_tempname (char *tmpl, int kind) +{ + int len; + char *XXXXXX; + static uint64_t value; + uint64_t random_time_bits; + unsigned int count; + int fd = -1; + int save_errno = errno; + struct_stat64 st; + + /* A lower bound on the number of temporary files to attempt to + generate. The maximum total number of temporary file names that + can exist for a given template is 62**6. It should never be + necessary to try all these combinations. Instead if a reasonable + number of names is tried (we define reasonable as 62**3) fail to + give the system administrator the chance to remove the problems. */ +#define ATTEMPTS_MIN (62 * 62 * 62) + + /* The number of times to attempt to generate a temporary file. To + conform to POSIX, this must be no smaller than TMP_MAX. */ +#if ATTEMPTS_MIN < TMP_MAX + unsigned int attempts = TMP_MAX; +#else + unsigned int attempts = ATTEMPTS_MIN; +#endif + + len = strlen (tmpl); + if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) + { + __set_errno (EINVAL); + return -1; + } + + /* This is where the Xs start. */ + XXXXXX = &tmpl[len - 6]; + + /* Get some more or less random data. */ +#ifdef RANDOM_BITS + RANDOM_BITS (random_time_bits); +#else +# if HAVE_GETTIMEOFDAY || _LIBC + { + struct timeval tv; + __gettimeofday (&tv, NULL); + random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; + } +# else + random_time_bits = time (NULL); +# endif +#endif + value += random_time_bits ^ __getpid (); + + for (count = 0; count < attempts; value += 7777, ++count) + { + uint64_t v = value; + + /* Fill in the random bits. */ + XXXXXX[0] = letters[v % 62]; + v /= 62; + XXXXXX[1] = letters[v % 62]; + v /= 62; + XXXXXX[2] = letters[v % 62]; + v /= 62; + XXXXXX[3] = letters[v % 62]; + v /= 62; + XXXXXX[4] = letters[v % 62]; + v /= 62; + XXXXXX[5] = letters[v % 62]; + + switch (kind) + { + case __GT_FILE: + fd = small_open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + break; + + case __GT_BIGFILE: + fd = large_open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + break; + + case __GT_DIR: + fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); + break; + + case __GT_NOCREATE: + /* This case is backward from the other three. __gen_tempname + succeeds if __xstat fails because the name does not exist. + Note the continue to bypass the common logic at the bottom + of the loop. */ + if (__lxstat64 (_STAT_VER, tmpl, &st) < 0) + { + if (errno == ENOENT) + { + __set_errno (save_errno); + return 0; + } + else + /* Give up now. */ + return -1; + } + continue; + + default: + assert (! "invalid KIND in __gen_tempname"); + } + + if (fd >= 0) + { + __set_errno (save_errno); + return fd; + } + else if (errno != EEXIST) + return -1; + } + + /* We got out of the loop because we ran out of combinations to try. */ + __set_errno (EEXIST); + return -1; +} diff --git a/src/bin/m4/lib/tempname.h b/src/bin/m4/lib/tempname.h new file mode 100644 index 0000000000..c51fa69424 --- /dev/null +++ b/src/bin/m4/lib/tempname.h @@ -0,0 +1,40 @@ +/* Create a temporary file or directory. + + Copyright (C) 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* header written by Eric Blake */ + +/* In gnulib, always prefer large files. GT_FILE maps to + __GT_BIGFILE, not __GT_FILE, for a reason. */ +#define GT_FILE 1 +#define GT_DIR 2 +#define GT_NOCREATE 3 + +/* Generate a temporary file name based on TMPL. TMPL must match the + rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed + does not exist at the time of the call to gen_tempname. TMPL is + overwritten with the result. + + KIND may be one of: + GT_NOCREATE: simply verify that the name does not exist + at the time of the call. + GT_FILE: create a large file using open(O_CREAT|O_EXCL) + and return a read-write fd. The file is mode 0600. + GT_DIR: create a directory, which will be mode 0700. + + We use a clever algorithm to get hard-to-predict names. */ +extern int gen_tempname (char *tmpl, int kind); diff --git a/src/bin/m4/lib/tmpdir.c b/src/bin/m4/lib/tmpdir.c new file mode 100644 index 0000000000..d29a005789 --- /dev/null +++ b/src/bin/m4/lib/tmpdir.c @@ -0,0 +1,133 @@ +/* Copyright (C) 1999, 2001-2002, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Extracted from sysdeps/posix/tempname.c. */ + +#include + +/* Specification. */ +#include "tmpdir.h" + +#include +#include +#include + +#include +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + +#include +#ifndef P_tmpdir +# define P_tmpdir "/tmp" +#endif + +#include + +#if _LIBC +# define struct_stat64 struct stat64 +#else +# define struct_stat64 struct stat +# define __xstat64(version, path, buf) stat (path, buf) +#endif + +#if ! (HAVE___SECURE_GETENV || _LIBC) +# define __secure_getenv getenv +#endif + +/* Pathname support. + ISSLASH(C) tests whether C is a directory separator character. + */ +#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__ + /* Win32, Cygwin, OS/2, DOS */ +# define ISSLASH(C) ((C) == '/' || (C) == '\\') +#else + /* Unix */ +# define ISSLASH(C) ((C) == '/') +#endif + + +/* Return nonzero if DIR is an existent directory. */ +static bool +direxists (const char *dir) +{ + struct_stat64 buf; + return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode); +} + +/* Path search algorithm, for tmpnam, tmpfile, etc. If DIR is + non-null and exists, uses it; otherwise uses the first of $TMPDIR, + P_tmpdir, /tmp that exists. Copies into TMPL a template suitable + for use with mk[s]temp. Will fail (-1) if DIR is non-null and + doesn't exist, none of the searched dirs exists, or there's not + enough space in TMPL. */ +int +path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx, + bool try_tmpdir) +{ + const char *d; + size_t dlen, plen; + + if (!pfx || !pfx[0]) + { + pfx = "file"; + plen = 4; + } + else + { + plen = strlen (pfx); + if (plen > 5) + plen = 5; + } + + if (try_tmpdir) + { + d = __secure_getenv ("TMPDIR"); + if (d != NULL && direxists (d)) + dir = d; + else if (dir != NULL && direxists (dir)) + /* nothing */ ; + else + dir = NULL; + } + if (dir == NULL) + { + if (direxists (P_tmpdir)) + dir = P_tmpdir; + else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp")) + dir = "/tmp"; + else + { + __set_errno (ENOENT); + return -1; + } + } + + dlen = strlen (dir); + while (dlen >= 1 && ISSLASH (dir[dlen - 1])) + dlen--; /* remove trailing slashes */ + + /* check we have room for "${dir}/${pfx}XXXXXX\0" */ + if (tmpl_len < dlen + 1 + plen + 6 + 1) + { + __set_errno (EINVAL); + return -1; + } + + sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx); + return 0; +} diff --git a/src/bin/m4/lib/tmpdir.h b/src/bin/m4/lib/tmpdir.h new file mode 100644 index 0000000000..454e5cc184 --- /dev/null +++ b/src/bin/m4/lib/tmpdir.h @@ -0,0 +1,27 @@ +/* Determine a temporary directory. + Copyright (C) 2001-2002 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include +#include + +/* Path search algorithm, for tmpnam, tmpfile, etc. If DIR is + non-null and exists, uses it; otherwise uses the first of $TMPDIR, + P_tmpdir, /tmp that exists. Copies into TMPL a template suitable + for use with mk[s]temp. Will fail (-1) if DIR is non-null and + doesn't exist, none of the searched dirs exists, or there's not + enough space in TMPL. */ +extern int path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx, bool try_tmpdir); diff --git a/src/bin/m4/lib/unistd--.h b/src/bin/m4/lib/unistd--.h new file mode 100644 index 0000000000..1fe6ce8b27 --- /dev/null +++ b/src/bin/m4/lib/unistd--.h @@ -0,0 +1,28 @@ +/* Like unistd.h, but redefine some names to avoid glitches. + + Copyright (C) 2005 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#include +#include "unistd-safer.h" + +#undef dup +#define dup dup_safer + +#undef pipe +#define pipe pipe_safer diff --git a/src/bin/m4/lib/unistd-safer.h b/src/bin/m4/lib/unistd-safer.h new file mode 100644 index 0000000000..f95999d391 --- /dev/null +++ b/src/bin/m4/lib/unistd-safer.h @@ -0,0 +1,23 @@ +/* Invoke unistd-like functions, but avoid some glitches. + + Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +int dup_safer (int); +int fd_safer (int); +int pipe_safer (int[2]); diff --git a/src/bin/m4/lib/unlocked-io.h b/src/bin/m4/lib/unlocked-io.h new file mode 100644 index 0000000000..d00930361c --- /dev/null +++ b/src/bin/m4/lib/unlocked-io.h @@ -0,0 +1,137 @@ +/* Prefer faster, non-thread-safe stdio functions if available. + + Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Jim Meyering. */ + +#ifndef UNLOCKED_IO_H +# define UNLOCKED_IO_H 1 + +/* These are wrappers for functions/macros from the GNU C library, and + from other C libraries supporting POSIX's optional thread-safe functions. + + The standard I/O functions are thread-safe. These *_unlocked ones are + more efficient but not thread-safe. That they're not thread-safe is + fine since all of the applications in this package are single threaded. + + Also, some code that is shared with the GNU C library may invoke + the *_unlocked functions directly. On hosts that lack those + functions, invoke the non-thread-safe versions instead. */ + +# include + +# if HAVE_DECL_CLEARERR_UNLOCKED +# undef clearerr +# define clearerr(x) clearerr_unlocked (x) +# else +# define clearerr_unlocked(x) clearerr (x) +# endif + +# if HAVE_DECL_FEOF_UNLOCKED +# undef feof +# define feof(x) feof_unlocked (x) +# else +# define feof_unlocked(x) feof (x) +# endif + +# if HAVE_DECL_FERROR_UNLOCKED +# undef ferror +# define ferror(x) ferror_unlocked (x) +# else +# define ferror_unlocked(x) ferror (x) +# endif + +# if HAVE_DECL_FFLUSH_UNLOCKED +# undef fflush +# define fflush(x) fflush_unlocked (x) +# else +# define fflush_unlocked(x) fflush (x) +# endif + +# if HAVE_DECL_FGETS_UNLOCKED +# undef fgets +# define fgets(x,y,z) fgets_unlocked (x,y,z) +# else +# define fgets_unlocked(x,y,z) fgets (x,y,z) +# endif + +# if HAVE_DECL_FPUTC_UNLOCKED +# undef fputc +# define fputc(x,y) fputc_unlocked (x,y) +# else +# define fputc_unlocked(x,y) fputc (x,y) +# endif + +# if HAVE_DECL_FPUTS_UNLOCKED +# undef fputs +# define fputs(x,y) fputs_unlocked (x,y) +# else +# define fputs_unlocked(x,y) fputs (x,y) +# endif + +# if HAVE_DECL_FREAD_UNLOCKED +# undef fread +# define fread(w,x,y,z) fread_unlocked (w,x,y,z) +# else +# define fread_unlocked(w,x,y,z) fread (w,x,y,z) +# endif + +# if HAVE_DECL_FWRITE_UNLOCKED +# undef fwrite +# define fwrite(w,x,y,z) fwrite_unlocked (w,x,y,z) +# else +# define fwrite_unlocked(w,x,y,z) fwrite (w,x,y,z) +# endif + +# if HAVE_DECL_GETC_UNLOCKED +# undef getc +# define getc(x) getc_unlocked (x) +# else +# define getc_unlocked(x) getc (x) +# endif + +# if HAVE_DECL_GETCHAR_UNLOCKED +# undef getchar +# define getchar() getchar_unlocked () +# else +# define getchar_unlocked() getchar () +# endif + +# if HAVE_DECL_PUTC_UNLOCKED +# undef putc +# define putc(x,y) putc_unlocked (x,y) +# else +# define putc_unlocked(x,y) putc (x,y) +# endif + +# if HAVE_DECL_PUTCHAR_UNLOCKED +# undef putchar +# define putchar(x) putchar_unlocked (x) +# else +# define putchar_unlocked(x) putchar (x) +# endif + +# undef flockfile +# define flockfile(x) ((void) 0) + +# undef ftrylockfile +# define ftrylockfile(x) 0 + +# undef funlockfile +# define funlockfile(x) ((void) 0) + +#endif /* UNLOCKED_IO_H */ diff --git a/src/bin/m4/lib/vasnprintf.c b/src/bin/m4/lib/vasnprintf.c new file mode 100644 index 0000000000..0fe2aada9f --- /dev/null +++ b/src/bin/m4/lib/vasnprintf.c @@ -0,0 +1,889 @@ +/* vsprintf with automatic memory allocation. + Copyright (C) 1999, 2002-2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Tell glibc's to provide a prototype for snprintf(). + This must come before because may include + , and once has been included, it's too late. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#include +#ifndef IN_LIBINTL +# include +#endif + +/* Specification. */ +#if WIDE_CHAR_VERSION +# include "vasnwprintf.h" +#else +# include "vasnprintf.h" +#endif + +#include /* snprintf(), sprintf() */ +#include /* abort(), malloc(), realloc(), free() */ +#include /* memcpy(), strlen() */ +#include /* errno */ +#include /* CHAR_BIT */ +#include /* DBL_MAX_EXP, LDBL_MAX_EXP */ +#if WIDE_CHAR_VERSION +# include "wprintf-parse.h" +#else +# include "printf-parse.h" +#endif + +/* Checked size_t computations. */ +#include "xsize.h" + +#ifdef HAVE_WCHAR_T +# ifdef HAVE_WCSLEN +# define local_wcslen wcslen +# else + /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid + a dependency towards this library, here is a local substitute. + Define this substitute only once, even if this file is included + twice in the same compilation unit. */ +# ifndef local_wcslen_defined +# define local_wcslen_defined 1 +static size_t +local_wcslen (const wchar_t *s) +{ + const wchar_t *ptr; + + for (ptr = s; *ptr != (wchar_t) 0; ptr++) + ; + return ptr - s; +} +# endif +# endif +#endif + +#if WIDE_CHAR_VERSION +# define VASNPRINTF vasnwprintf +# define CHAR_T wchar_t +# define DIRECTIVE wchar_t_directive +# define DIRECTIVES wchar_t_directives +# define PRINTF_PARSE wprintf_parse +# define USE_SNPRINTF 1 +# if HAVE_DECL__SNWPRINTF + /* On Windows, the function swprintf() has a different signature than + on Unix; we use the _snwprintf() function instead. */ +# define SNPRINTF _snwprintf +# else + /* Unix. */ +# define SNPRINTF swprintf +# endif +#else +# define VASNPRINTF vasnprintf +# define CHAR_T char +# define DIRECTIVE char_directive +# define DIRECTIVES char_directives +# define PRINTF_PARSE printf_parse +# define USE_SNPRINTF (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) +# if HAVE_DECL__SNPRINTF + /* Windows. */ +# define SNPRINTF _snprintf +# else + /* Unix. */ +# define SNPRINTF snprintf +# endif +#endif + +CHAR_T * +VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list args) +{ + DIRECTIVES d; + arguments a; + + if (PRINTF_PARSE (format, &d, &a) < 0) + { + errno = EINVAL; + return NULL; + } + +#define CLEANUP() \ + free (d.dir); \ + if (a.arg) \ + free (a.arg); + + if (printf_fetchargs (args, &a) < 0) + { + CLEANUP (); + errno = EINVAL; + return NULL; + } + + { + size_t buf_neededlength; + CHAR_T *buf; + CHAR_T *buf_malloced; + const CHAR_T *cp; + size_t i; + DIRECTIVE *dp; + /* Output string accumulator. */ + CHAR_T *result; + size_t allocated; + size_t length; + + /* Allocate a small buffer that will hold a directive passed to + sprintf or snprintf. */ + buf_neededlength = + xsum4 (7, d.max_width_length, d.max_precision_length, 6); +#if HAVE_ALLOCA + if (buf_neededlength < 4000 / sizeof (CHAR_T)) + { + buf = (CHAR_T *) alloca (buf_neededlength * sizeof (CHAR_T)); + buf_malloced = NULL; + } + else +#endif + { + size_t buf_memsize = xtimes (buf_neededlength, sizeof (CHAR_T)); + if (size_overflow_p (buf_memsize)) + goto out_of_memory_1; + buf = (CHAR_T *) malloc (buf_memsize); + if (buf == NULL) + goto out_of_memory_1; + buf_malloced = buf; + } + + if (resultbuf != NULL) + { + result = resultbuf; + allocated = *lengthp; + } + else + { + result = NULL; + allocated = 0; + } + length = 0; + /* Invariants: + result is either == resultbuf or == NULL or malloc-allocated. + If length > 0, then result != NULL. */ + + /* Ensures that allocated >= needed. Aborts through a jump to + out_of_memory if needed is SIZE_MAX or otherwise too big. */ +#define ENSURE_ALLOCATION(needed) \ + if ((needed) > allocated) \ + { \ + size_t memory_size; \ + CHAR_T *memory; \ + \ + allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \ + if ((needed) > allocated) \ + allocated = (needed); \ + memory_size = xtimes (allocated, sizeof (CHAR_T)); \ + if (size_overflow_p (memory_size)) \ + goto out_of_memory; \ + if (result == resultbuf || result == NULL) \ + memory = (CHAR_T *) malloc (memory_size); \ + else \ + memory = (CHAR_T *) realloc (result, memory_size); \ + if (memory == NULL) \ + goto out_of_memory; \ + if (result == resultbuf && length > 0) \ + memcpy (memory, result, length * sizeof (CHAR_T)); \ + result = memory; \ + } + + for (cp = format, i = 0, dp = &d.dir[0]; ; cp = dp->dir_end, i++, dp++) + { + if (cp != dp->dir_start) + { + size_t n = dp->dir_start - cp; + size_t augmented_length = xsum (length, n); + + ENSURE_ALLOCATION (augmented_length); + memcpy (result + length, cp, n * sizeof (CHAR_T)); + length = augmented_length; + } + if (i == d.count) + break; + + /* Execute a single directive. */ + if (dp->conversion == '%') + { + size_t augmented_length; + + if (!(dp->arg_index == ARG_NONE)) + abort (); + augmented_length = xsum (length, 1); + ENSURE_ALLOCATION (augmented_length); + result[length] = '%'; + length = augmented_length; + } + else + { + if (!(dp->arg_index != ARG_NONE)) + abort (); + + if (dp->conversion == 'n') + { + switch (a.arg[dp->arg_index].type) + { + case TYPE_COUNT_SCHAR_POINTER: + *a.arg[dp->arg_index].a.a_count_schar_pointer = length; + break; + case TYPE_COUNT_SHORT_POINTER: + *a.arg[dp->arg_index].a.a_count_short_pointer = length; + break; + case TYPE_COUNT_INT_POINTER: + *a.arg[dp->arg_index].a.a_count_int_pointer = length; + break; + case TYPE_COUNT_LONGINT_POINTER: + *a.arg[dp->arg_index].a.a_count_longint_pointer = length; + break; +#ifdef HAVE_LONG_LONG_INT + case TYPE_COUNT_LONGLONGINT_POINTER: + *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length; + break; +#endif + default: + abort (); + } + } + else + { + arg_type type = a.arg[dp->arg_index].type; + CHAR_T *p; + unsigned int prefix_count; + int prefixes[2]; +#if !USE_SNPRINTF + size_t tmp_length; + CHAR_T tmpbuf[700]; + CHAR_T *tmp; + + /* Allocate a temporary buffer of sufficient size for calling + sprintf. */ + { + size_t width; + size_t precision; + + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + width = (arg < 0 ? (unsigned int) (-arg) : arg); + } + else + { + const CHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + } + + precision = 6; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + precision = (arg < 0 ? 0 : arg); + } + else + { + const CHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + while (digitp != dp->precision_end) + precision = xsum (xtimes (precision, 10), *digitp++ - '0'); + } + } + + switch (dp->conversion) + { + + case 'd': case 'i': case 'u': +# ifdef HAVE_LONG_LONG_INT + if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long long) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + else +# endif + if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + else + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Multiply by 2, as an estimate for FLAG_GROUP. */ + tmp_length = xsum (tmp_length, tmp_length); + /* Add 1, to account for a leading sign. */ + tmp_length = xsum (tmp_length, 1); + break; + + case 'o': +# ifdef HAVE_LONG_LONG_INT + if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long long) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + else +# endif + if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + else + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Add 1, to account for a leading sign. */ + tmp_length = xsum (tmp_length, 1); + break; + + case 'x': case 'X': +# ifdef HAVE_LONG_LONG_INT + if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long long) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + else +# endif + if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + tmp_length = + (unsigned int) (sizeof (unsigned long) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + else + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Add 2, to account for a leading sign or alternate form. */ + tmp_length = xsum (tmp_length, 2); + break; + + case 'f': case 'F': +# ifdef HAVE_LONG_DOUBLE + if (type == TYPE_LONGDOUBLE) + tmp_length = + (unsigned int) (LDBL_MAX_EXP + * 0.30103 /* binary -> decimal */ + * 2 /* estimate for FLAG_GROUP */ + ) + + 1 /* turn floor into ceil */ + + 10; /* sign, decimal point etc. */ + else +# endif + tmp_length = + (unsigned int) (DBL_MAX_EXP + * 0.30103 /* binary -> decimal */ + * 2 /* estimate for FLAG_GROUP */ + ) + + 1 /* turn floor into ceil */ + + 10; /* sign, decimal point etc. */ + tmp_length = xsum (tmp_length, precision); + break; + + case 'e': case 'E': case 'g': case 'G': + case 'a': case 'A': + tmp_length = + 12; /* sign, decimal point, exponent etc. */ + tmp_length = xsum (tmp_length, precision); + break; + + case 'c': +# if defined HAVE_WINT_T && !WIDE_CHAR_VERSION + if (type == TYPE_WIDE_CHAR) + tmp_length = MB_CUR_MAX; + else +# endif + tmp_length = 1; + break; + + case 's': +# ifdef HAVE_WCHAR_T + if (type == TYPE_WIDE_STRING) + { + tmp_length = + local_wcslen (a.arg[dp->arg_index].a.a_wide_string); + +# if !WIDE_CHAR_VERSION + tmp_length = xtimes (tmp_length, MB_CUR_MAX); +# endif + } + else +# endif + tmp_length = strlen (a.arg[dp->arg_index].a.a_string); + break; + + case 'p': + tmp_length = + (unsigned int) (sizeof (void *) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1 /* turn floor into ceil */ + + 2; /* account for leading 0x */ + break; + + default: + abort (); + } + + if (tmp_length < width) + tmp_length = width; + + tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ + } + + if (tmp_length <= sizeof (tmpbuf) / sizeof (CHAR_T)) + tmp = tmpbuf; + else + { + size_t tmp_memsize = xtimes (tmp_length, sizeof (CHAR_T)); + + if (size_overflow_p (tmp_memsize)) + /* Overflow, would lead to out of memory. */ + goto out_of_memory; + tmp = (CHAR_T *) malloc (tmp_memsize); + if (tmp == NULL) + /* Out of memory. */ + goto out_of_memory; + } +#endif + + /* Construct the format string for calling snprintf or + sprintf. */ + p = buf; + *p++ = '%'; + if (dp->flags & FLAG_GROUP) + *p++ = '\''; + if (dp->flags & FLAG_LEFT) + *p++ = '-'; + if (dp->flags & FLAG_SHOWSIGN) + *p++ = '+'; + if (dp->flags & FLAG_SPACE) + *p++ = ' '; + if (dp->flags & FLAG_ALT) + *p++ = '#'; + if (dp->flags & FLAG_ZERO) + *p++ = '0'; + if (dp->width_start != dp->width_end) + { + size_t n = dp->width_end - dp->width_start; + memcpy (p, dp->width_start, n * sizeof (CHAR_T)); + p += n; + } + if (dp->precision_start != dp->precision_end) + { + size_t n = dp->precision_end - dp->precision_start; + memcpy (p, dp->precision_start, n * sizeof (CHAR_T)); + p += n; + } + + switch (type) + { +#ifdef HAVE_LONG_LONG_INT + case TYPE_LONGLONGINT: + case TYPE_ULONGLONGINT: + *p++ = 'l'; + /*FALLTHROUGH*/ +#endif + case TYPE_LONGINT: + case TYPE_ULONGINT: +#ifdef HAVE_WINT_T + case TYPE_WIDE_CHAR: +#endif +#ifdef HAVE_WCHAR_T + case TYPE_WIDE_STRING: +#endif + *p++ = 'l'; + break; +#ifdef HAVE_LONG_DOUBLE + case TYPE_LONGDOUBLE: + *p++ = 'L'; + break; +#endif + default: + break; + } + *p = dp->conversion; +#if USE_SNPRINTF + p[1] = '%'; + p[2] = 'n'; + p[3] = '\0'; +#else + p[1] = '\0'; +#endif + + /* Construct the arguments for calling snprintf or sprintf. */ + prefix_count = 0; + if (dp->width_arg_index != ARG_NONE) + { + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int; + } + if (dp->precision_arg_index != ARG_NONE) + { + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int; + } + +#if USE_SNPRINTF + /* Prepare checking whether snprintf returns the count + via %n. */ + ENSURE_ALLOCATION (xsum (length, 1)); + result[length] = '\0'; +#endif + + for (;;) + { + size_t maxlen; + int count; + int retcount; + + maxlen = allocated - length; + count = -1; + retcount = 0; + +#if USE_SNPRINTF +# define SNPRINTF_BUF(arg) \ + switch (prefix_count) \ + { \ + case 0: \ + retcount = SNPRINTF (result + length, maxlen, buf, \ + arg, &count); \ + break; \ + case 1: \ + retcount = SNPRINTF (result + length, maxlen, buf, \ + prefixes[0], arg, &count); \ + break; \ + case 2: \ + retcount = SNPRINTF (result + length, maxlen, buf, \ + prefixes[0], prefixes[1], arg, \ + &count); \ + break; \ + default: \ + abort (); \ + } +#else +# define SNPRINTF_BUF(arg) \ + switch (prefix_count) \ + { \ + case 0: \ + count = sprintf (tmp, buf, arg); \ + break; \ + case 1: \ + count = sprintf (tmp, buf, prefixes[0], arg); \ + break; \ + case 2: \ + count = sprintf (tmp, buf, prefixes[0], prefixes[1],\ + arg); \ + break; \ + default: \ + abort (); \ + } +#endif + + switch (type) + { + case TYPE_SCHAR: + { + int arg = a.arg[dp->arg_index].a.a_schar; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UCHAR: + { + unsigned int arg = a.arg[dp->arg_index].a.a_uchar; + SNPRINTF_BUF (arg); + } + break; + case TYPE_SHORT: + { + int arg = a.arg[dp->arg_index].a.a_short; + SNPRINTF_BUF (arg); + } + break; + case TYPE_USHORT: + { + unsigned int arg = a.arg[dp->arg_index].a.a_ushort; + SNPRINTF_BUF (arg); + } + break; + case TYPE_INT: + { + int arg = a.arg[dp->arg_index].a.a_int; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT: + { + unsigned int arg = a.arg[dp->arg_index].a.a_uint; + SNPRINTF_BUF (arg); + } + break; + case TYPE_LONGINT: + { + long int arg = a.arg[dp->arg_index].a.a_longint; + SNPRINTF_BUF (arg); + } + break; + case TYPE_ULONGINT: + { + unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint; + SNPRINTF_BUF (arg); + } + break; +#ifdef HAVE_LONG_LONG_INT + case TYPE_LONGLONGINT: + { + long long int arg = a.arg[dp->arg_index].a.a_longlongint; + SNPRINTF_BUF (arg); + } + break; + case TYPE_ULONGLONGINT: + { + unsigned long long int arg = a.arg[dp->arg_index].a.a_ulonglongint; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_DOUBLE: + { + double arg = a.arg[dp->arg_index].a.a_double; + SNPRINTF_BUF (arg); + } + break; +#ifdef HAVE_LONG_DOUBLE + case TYPE_LONGDOUBLE: + { + long double arg = a.arg[dp->arg_index].a.a_longdouble; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_CHAR: + { + int arg = a.arg[dp->arg_index].a.a_char; + SNPRINTF_BUF (arg); + } + break; +#ifdef HAVE_WINT_T + case TYPE_WIDE_CHAR: + { + wint_t arg = a.arg[dp->arg_index].a.a_wide_char; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_STRING: + { + const char *arg = a.arg[dp->arg_index].a.a_string; + SNPRINTF_BUF (arg); + } + break; +#ifdef HAVE_WCHAR_T + case TYPE_WIDE_STRING: + { + const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_POINTER: + { + void *arg = a.arg[dp->arg_index].a.a_pointer; + SNPRINTF_BUF (arg); + } + break; + default: + abort (); + } + +#if USE_SNPRINTF + /* Portability: Not all implementations of snprintf() + are ISO C 99 compliant. Determine the number of + bytes that snprintf() has produced or would have + produced. */ + if (count >= 0) + { + /* Verify that snprintf() has NUL-terminated its + result. */ + if (count < maxlen && result[length + count] != '\0') + abort (); + /* Portability hack. */ + if (retcount > count) + count = retcount; + } + else + { + /* snprintf() doesn't understand the '%n' + directive. */ + if (p[1] != '\0') + { + /* Don't use the '%n' directive; instead, look + at the snprintf() return value. */ + p[1] = '\0'; + continue; + } + else + { + /* Look at the snprintf() return value. */ + if (retcount < 0) + { + /* HP-UX 10.20 snprintf() is doubly deficient: + It doesn't understand the '%n' directive, + *and* it returns -1 (rather than the length + that would have been required) when the + buffer is too small. */ + size_t bigger_need = + xsum (xtimes (allocated, 2), 12); + ENSURE_ALLOCATION (bigger_need); + continue; + } + else + count = retcount; + } + } +#endif + + /* Attempt to handle failure. */ + if (count < 0) + { + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EINVAL; + return NULL; + } + +#if !USE_SNPRINTF + if (count >= tmp_length) + /* tmp_length was incorrectly calculated - fix the + code above! */ + abort (); +#endif + + /* Make room for the result. */ + if (count >= maxlen) + { + /* Need at least count bytes. But allocate + proportionally, to avoid looping eternally if + snprintf() reports a too small count. */ + size_t n = + xmax (xsum (length, count), xtimes (allocated, 2)); + + ENSURE_ALLOCATION (n); +#if USE_SNPRINTF + continue; +#endif + } + +#if USE_SNPRINTF + /* The snprintf() result did fit. */ +#else + /* Append the sprintf() result. */ + memcpy (result + length, tmp, count * sizeof (CHAR_T)); + if (tmp != tmpbuf) + free (tmp); +#endif + + length += count; + break; + } + } + } + } + + /* Add the final NUL. */ + ENSURE_ALLOCATION (xsum (length, 1)); + result[length] = '\0'; + + if (result != resultbuf && length + 1 < allocated) + { + /* Shrink the allocated memory if possible. */ + CHAR_T *memory; + + memory = (CHAR_T *) realloc (result, (length + 1) * sizeof (CHAR_T)); + if (memory != NULL) + result = memory; + } + + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + *lengthp = length; + /* Note that we can produce a big string of a length > INT_MAX. POSIX + says that snprintf() fails with errno = EOVERFLOW in this case, but + that's only because snprintf() returns an 'int'. This function does + not have this limitation. */ + return result; + + out_of_memory: + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + out_of_memory_1: + CLEANUP (); + errno = ENOMEM; + return NULL; + } +} + +#undef SNPRINTF +#undef USE_SNPRINTF +#undef PRINTF_PARSE +#undef DIRECTIVES +#undef DIRECTIVE +#undef CHAR_T +#undef VASNPRINTF diff --git a/src/bin/m4/lib/vasnprintf.h b/src/bin/m4/lib/vasnprintf.h new file mode 100644 index 0000000000..894008caec --- /dev/null +++ b/src/bin/m4/lib/vasnprintf.h @@ -0,0 +1,77 @@ +/* vsprintf with automatic memory allocation. + Copyright (C) 2002-2004 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _VASNPRINTF_H +#define _VASNPRINTF_H + +/* Get va_list. */ +#include + +/* Get size_t. */ +#include + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ +# define __attribute__(Spec) /* empty */ +# endif +/* The __-protected variants of `format' and `printf' attributes + are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __format__ format +# define __printf__ printf +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Write formatted output to a string dynamically allocated with malloc(). + You can pass a preallocated buffer for the result in RESULTBUF and its + size in *LENGTHP; otherwise you pass RESULTBUF = NULL. + If successful, return the address of the string (this may be = RESULTBUF + if no dynamic memory allocation was necessary) and set *LENGTHP to the + number of resulting bytes, excluding the trailing NUL. Upon error, set + errno and return NULL. + + When dynamic memory allocation occurs, the preallocated buffer is left + alone (with possibly modified contents). This makes it possible to use + a statically allocated or stack-allocated buffer, like this: + + char buf[100]; + size_t len = sizeof (buf); + char *output = vasnprintf (buf, &len, format, args); + if (output == NULL) + ... error handling ...; + else + { + ... use the output string ...; + if (output != buf) + free (output); + } + */ +extern char * asnprintf (char *resultbuf, size_t *lengthp, const char *format, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); +extern char * vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) + __attribute__ ((__format__ (__printf__, 3, 0))); + +#ifdef __cplusplus +} +#endif + +#endif /* _VASNPRINTF_H */ diff --git a/src/bin/m4/lib/vasprintf.c b/src/bin/m4/lib/vasprintf.c new file mode 100644 index 0000000000..824707345a --- /dev/null +++ b/src/bin/m4/lib/vasprintf.c @@ -0,0 +1,52 @@ +/* Formatted output to strings. + Copyright (C) 1999, 2002, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "vasprintf.h" + +#include +#include +#include + +#include "vasnprintf.h" + +/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW. */ +#ifndef EOVERFLOW +# define EOVERFLOW E2BIG +#endif + +int +vasprintf (char **resultp, const char *format, va_list args) +{ + size_t length; + char *result = vasnprintf (NULL, &length, format, args); + if (result == NULL) + return -1; + + if (length > INT_MAX) + { + free (result); + errno = EOVERFLOW; + return -1; + } + + *resultp = result; + /* Return the number of resulting bytes, excluding the trailing NUL. */ + return length; +} diff --git a/src/bin/m4/lib/vasprintf.h b/src/bin/m4/lib/vasprintf.h new file mode 100644 index 0000000000..ab5515ce7f --- /dev/null +++ b/src/bin/m4/lib/vasprintf.h @@ -0,0 +1,63 @@ +/* vsprintf with automatic memory allocation. + Copyright (C) 2002-2003 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _VASPRINTF_H +#define _VASPRINTF_H + +#if HAVE_VASPRINTF + +/* Get asprintf(), vasprintf() declarations. */ +#include + +#else + +/* Get va_list. */ +#include + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ +# define __attribute__(Spec) /* empty */ +# endif +/* The __-protected variants of `format' and `printf' attributes + are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __format__ format +# define __printf__ printf +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Write formatted output to a string dynamically allocated with malloc(). + If the memory allocation succeeds, store the address of the string in + *RESULT and return the number of resulting bytes, excluding the trailing + NUL. Upon memory allocation error, or some other error, return -1. */ +extern int asprintf (char **result, const char *format, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); +extern int vasprintf (char **result, const char *format, va_list args) + __attribute__ ((__format__ (__printf__, 2, 0))); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* _VASPRINTF_H */ diff --git a/src/bin/m4/lib/verify.h b/src/bin/m4/lib/verify.h new file mode 100644 index 0000000000..d603b17379 --- /dev/null +++ b/src/bin/m4/lib/verify.h @@ -0,0 +1,141 @@ +/* Compile-time assert-like macros. + + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert, Bruno Haible, and Jim Meyering. */ + +#ifndef VERIFY_H +# define VERIFY_H 1 + +/* Each of these macros verifies that its argument R is nonzero. To + be portable, R should be an integer constant expression. Unlike + assert (R), there is no run-time overhead. + + There are two macros, since no single macro can be used in all + contexts in C. verify_true (R) is for scalar contexts, including + integer constant expression contexts. verify (R) is for declaration + contexts, e.g., the top level. + + Symbols ending in "__" are private to this header. + + The code below uses several ideas. + + * The first step is ((R) ? 1 : -1). Given an expression R, of + integral or boolean or floating-point type, this yields an + expression of integral type, whose value is later verified to be + constant and nonnegative. + + * Next this expression W is wrapped in a type + struct verify_type__ { unsigned int verify_error_if_negative_size__: W; }. + If W is negative, this yields a compile-time error. No compiler can + deal with a bit-field of negative size. + + One might think that an array size check would have the same + effect, that is, that the type struct { unsigned int dummy[W]; } + would work as well. However, inside a function, some compilers + (such as C++ compilers and GNU C) allow local parameters and + variables inside array size expressions. With these compilers, + an array size check would not properly diagnose this misuse of + the verify macro: + + void function (int n) { verify (n < 0); } + + * For the verify macro, the struct verify_type__ will need to + somehow be embedded into a declaration. To be portable, this + declaration must declare an object, a constant, a function, or a + typedef name. If the declared entity uses the type directly, + such as in + + struct dummy {...}; + typedef struct {...} dummy; + extern struct {...} *dummy; + extern void dummy (struct {...} *); + extern struct {...} *dummy (void); + + two uses of the verify macro would yield colliding declarations + if the entity names are not disambiguated. A workaround is to + attach the current line number to the entity name: + + #define GL_CONCAT0(x, y) x##y + #define GL_CONCAT(x, y) GL_CONCAT0 (x, y) + extern struct {...} * GL_CONCAT(dummy,__LINE__); + + But this has the problem that two invocations of verify from + within the same macro would collide, since the __LINE__ value + would be the same for both invocations. + + A solution is to use the sizeof operator. It yields a number, + getting rid of the identity of the type. Declarations like + + extern int dummy [sizeof (struct {...})]; + extern void dummy (int [sizeof (struct {...})]); + extern int (*dummy (void)) [sizeof (struct {...})]; + + can be repeated. + + * Should the implementation use a named struct or an unnamed struct? + Which of the following alternatives can be used? + + extern int dummy [sizeof (struct {...})]; + extern int dummy [sizeof (struct verify_type__ {...})]; + extern void dummy (int [sizeof (struct {...})]); + extern void dummy (int [sizeof (struct verify_type__ {...})]); + extern int (*dummy (void)) [sizeof (struct {...})]; + extern int (*dummy (void)) [sizeof (struct verify_type__ {...})]; + + In the second and sixth case, the struct type is exported to the + outer scope; two such declarations therefore collide. GCC warns + about the first, third, and fourth cases. So the only remaining + possibility is the fifth case: + + extern int (*dummy (void)) [sizeof (struct {...})]; + + * This implementation exploits the fact that GCC does not warn about + the last declaration mentioned above. If a future version of GCC + introduces a warning for this, the problem could be worked around + by using code specialized to GCC, e.g.,: + + #if 4 <= __GNUC__ + # define verify(R) \ + extern int (* verify_function__ (void)) \ + [__builtin_constant_p (R) && (R) ? 1 : -1] + #endif + + * In C++, any struct definition inside sizeof is invalid. + Use a template type to work around the problem. */ + + +/* Verify requirement R at compile-time, as an integer constant expression. + Return 1. */ + +# ifdef __cplusplus +template + struct verify_type__ { unsigned int verify_error_if_negative_size__: w; }; +# define verify_true(R) \ + (!!sizeof (verify_type__<(R) ? 1 : -1>)) +# else +# define verify_true(R) \ + (!!sizeof \ + (struct { unsigned int verify_error_if_negative_size__: (R) ? 1 : -1; })) +# endif + +/* Verify requirement R at compile-time, as a declaration without a + trailing ';'. */ + +# define verify(R) extern int (* verify_function__ (void)) [verify_true (R)] + +#endif diff --git a/src/bin/m4/lib/verror.c b/src/bin/m4/lib/verror.c new file mode 100644 index 0000000000..531e4edb5f --- /dev/null +++ b/src/bin/m4/lib/verror.c @@ -0,0 +1,77 @@ +/* va_list error handler for noninteractive utilities + Copyright (C) 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Eric Blake. */ + +#include + +#include "verror.h" +#include "xvasprintf.h" + +#include +#include +#include + +#if ENABLE_NLS +# include "gettext.h" +#endif + +#ifndef _ +# define _(String) String +#endif + +/* Print a message with `vfprintf (stderr, FORMAT, ARGS)'; + if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM). + If STATUS is nonzero, terminate the program with `exit (STATUS)'. + Use the globals error_print_progname and error_message_count similarly + to error(). */ +void +verror (int status, int errnum, const char *format, va_list args) +{ + verror_at_line (status, errnum, NULL, 0, format, args); +} + +/* Print a message with `vfprintf (stderr, FORMAT, ARGS)'; + if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM). + If STATUS is nonzero, terminate the program with `exit (STATUS)'. + If FNAME is not NULL, prepend the message with `FNAME:LINENO:'. + Use the globals error_print_progname, error_message_count, and + error_one_per_line similarly to error_at_line(). */ +void +verror_at_line (int status, int errnum, const char *file, + unsigned int line_number, const char *format, va_list args) +{ + char *message = xvasprintf (format, args); + if (message) + { + /* Until http://sourceware.org/bugzilla/show_bug.cgi?id=2997 is fixed, + glibc violates GNU Coding Standards when the file argument to + error_at_line is NULL. */ + if (file) + error_at_line (status, errnum, file, line_number, "%s", message); + else + error (status, errnum, "%s", message); + } + else + { + /* EOVERFLOW, EINVAL, and EILSEQ from xvasprintf are signs of + serious programmer errors. */ + error (0, errno, _("unable to display error message")); + abort (); + } + free (message); +} diff --git a/src/bin/m4/lib/verror.h b/src/bin/m4/lib/verror.h new file mode 100644 index 0000000000..625ef102ee --- /dev/null +++ b/src/bin/m4/lib/verror.h @@ -0,0 +1,54 @@ +/* Declaration for va_list error-reporting function + Copyright (C) 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _VERROR_H +#define _VERROR_H 1 + +#include "error.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Print a message with `vfprintf (stderr, FORMAT, ARGS)'; + if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM). + If STATUS is nonzero, terminate the program with `exit (STATUS)'. + Use the globals error_print_progname and error_message_count similarly + to error(). */ + +extern void verror (int __status, int __errnum, const char *__format, + va_list __args) + __attribute__ ((__format__ (__printf__, 3, 0))); + +/* Print a message with `vfprintf (stderr, FORMAT, ARGS)'; + if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM). + If STATUS is nonzero, terminate the program with `exit (STATUS)'. + If FNAME is not NULL, prepend the message with `FNAME:LINENO:'. + Use the globals error_print_progname, error_message_count, and + error_one_per_line similarly to error_at_line(). */ + +extern void verror_at_line (int __status, int __errnum, const char *__fname, + unsigned int __lineno, const char *__format, + va_list __args) + __attribute__ ((__format__ (__printf__, 5, 0))); + +#ifdef __cplusplus +} +#endif + +#endif /* verror.h */ diff --git a/src/bin/m4/lib/wcwidth.h b/src/bin/m4/lib/wcwidth.h new file mode 100644 index 0000000000..83da8fb0fb --- /dev/null +++ b/src/bin/m4/lib/wcwidth.h @@ -0,0 +1,81 @@ +/* Determine the number of screen columns needed for a character. + Copyright (C) 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _gl_WCWIDTH_H +#define _gl_WCWIDTH_H + +#if HAVE_WCHAR_T + +/* Get wcwidth if available, along with wchar_t. */ +# if HAVE_WCHAR_H +/* Tru64 with Desktop Toolkit C has a bug: must be included before + . + BSD/OS 4.1 has a bug: and must be included before + . */ +# include +# include +# include +# endif + +/* Get iswprint. */ +# if HAVE_WCTYPE_H +# include +# endif +# if !defined iswprint && !HAVE_ISWPRINT +static inline int +# if HAVE_WINT_T +iswprint (wint_t wc) +# else +iswprint (int wc) +# endif +{ + return (wc >= 0 && wc < 128 + ? wc >= ' ' && wc <= '~' + : 1); +} +# define iswprint iswprint +# endif + +# ifndef HAVE_DECL_WCWIDTH +"this configure-time declaration test was not run" +# endif +# ifndef wcwidth +# if !HAVE_WCWIDTH + +/* wcwidth doesn't exist, so assume all printable characters have + width 1. */ +static inline int +wcwidth (wchar_t wc) +{ + return wc == 0 ? 0 : iswprint (wc) ? 1 : -1; +} + +# elif !HAVE_DECL_WCWIDTH + +/* wcwidth exists but is not declared. */ +extern +# ifdef __cplusplus +"C" +# endif +int wcwidth (int /* actually wchar_t */); + +# endif +# endif + +#endif /* HAVE_WCHAR_H */ + +#endif /* _gl_WCWIDTH_H */ diff --git a/src/bin/m4/lib/xalloc-die.c b/src/bin/m4/lib/xalloc-die.c new file mode 100644 index 0000000000..090f060df7 --- /dev/null +++ b/src/bin/m4/lib/xalloc-die.c @@ -0,0 +1,42 @@ +/* Report a memory allocation failure and exit. + + Copyright (C) 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2006 Free + Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +#include "xalloc.h" + +#include + +#include "error.h" +#include "exitfail.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +void +xalloc_die (void) +{ + error (exit_failure, 0, "%s", _("memory exhausted")); + + /* The `noreturn' cannot be given to error, since it may return if + its first argument is 0. To help compilers understand the + xalloc_die does not return, call abort. Also, the abort is a + safety feature if exit_failure is 0 (which shouldn't happen). */ + abort (); +} diff --git a/src/bin/m4/lib/xalloc.h b/src/bin/m4/lib/xalloc.h new file mode 100644 index 0000000000..17ab514218 --- /dev/null +++ b/src/bin/m4/lib/xalloc.h @@ -0,0 +1,267 @@ +/* xalloc.h -- malloc with out-of-memory checking + + Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2003, 2004, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef XALLOC_H_ +# define XALLOC_H_ + +# include + + +# ifdef __cplusplus +extern "C" { +# endif + + +# ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ +# define __attribute__(x) +# endif +# endif + +# ifndef ATTRIBUTE_NORETURN +# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) +# endif + +/* This function is always triggered when memory is exhausted. + It must be defined by the application, either explicitly + or by using gnulib's xalloc-die module. This is the + function to call when one wants the program to die because of a + memory allocation failure. */ +extern void xalloc_die (void) ATTRIBUTE_NORETURN; + +void *xmalloc (size_t s); +void *xzalloc (size_t s); +void *xcalloc (size_t n, size_t s); +void *xrealloc (void *p, size_t s); +void *x2realloc (void *p, size_t *pn); +void *xmemdup (void const *p, size_t s); +char *xstrdup (char const *str); + +/* Return 1 if an array of N objects, each of size S, cannot exist due + to size arithmetic overflow. S must be positive and N must be + nonnegative. This is a macro, not an inline function, so that it + works correctly even when SIZE_MAX < N. + + By gnulib convention, SIZE_MAX represents overflow in size + calculations, so the conservative dividend to use here is + SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value. + However, malloc (SIZE_MAX) fails on all known hosts where + sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for + exactly-SIZE_MAX allocations on such hosts; this avoids a test and + branch when S is known to be 1. */ +# define xalloc_oversized(n, s) \ + ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n)) + + +/* In the following macros, T must be an elementary or structure/union or + typedef'ed type, or a pointer to such a type. To apply one of the + following macros to a function pointer or array type, you need to typedef + it first and use the typedef name. */ + +/* Allocate an object of type T dynamically, with error checking. */ +/* extern t *XMALLOC (typename t); */ +# define XMALLOC(t) ((t *) xmalloc (sizeof (t))) + +/* Allocate memory for N elements of type T, with error checking. */ +/* extern t *XNMALLOC (size_t n, typename t); */ +# define XNMALLOC(n, t) \ + ((t *) (sizeof (t) == 1 ? xmalloc (n) : xnmalloc (n, sizeof (t)))) + +/* Allocate an object of type T dynamically, with error checking, + and zero it. */ +/* extern t *XZALLOC (typename t); */ +# define XZALLOC(t) ((t *) xzalloc (sizeof (t))) + +/* Allocate memory for N elements of type T, with error checking, + and zero it. */ +/* extern t *XCALLOC (size_t n, typename t); */ +# define XCALLOC(n, t) \ + ((t *) (sizeof (t) == 1 ? xzalloc (n) : xcalloc (n, sizeof (t)))) + + +# if HAVE_INLINE +# define static_inline static inline +# else + void *xnmalloc (size_t n, size_t s); + void *xnrealloc (void *p, size_t n, size_t s); + void *x2nrealloc (void *p, size_t *pn, size_t s); + char *xcharalloc (size_t n); +# endif + +# ifdef static_inline + +/* Allocate an array of N objects, each with S bytes of memory, + dynamically, with error checking. S must be nonzero. */ + +static_inline void * +xnmalloc (size_t n, size_t s) +{ + if (xalloc_oversized (n, s)) + xalloc_die (); + return xmalloc (n * s); +} + +/* Change the size of an allocated block of memory P to an array of N + objects each of S bytes, with error checking. S must be nonzero. */ + +static_inline void * +xnrealloc (void *p, size_t n, size_t s) +{ + if (xalloc_oversized (n, s)) + xalloc_die (); + return xrealloc (p, n * s); +} + +/* If P is null, allocate a block of at least *PN such objects; + otherwise, reallocate P so that it contains more than *PN objects + each of S bytes. *PN must be nonzero unless P is null, and S must + be nonzero. Set *PN to the new number of objects, and return the + pointer to the new block. *PN is never set to zero, and the + returned pointer is never null. + + Repeated reallocations are guaranteed to make progress, either by + allocating an initial block with a nonzero size, or by allocating a + larger block. + + In the following implementation, nonzero sizes are doubled so that + repeated reallocations have O(N log N) overall cost rather than + O(N**2) cost, but the specification for this function does not + guarantee that sizes are doubled. + + Here is an example of use: + + int *p = NULL; + size_t used = 0; + size_t allocated = 0; + + void + append_int (int value) + { + if (used == allocated) + p = x2nrealloc (p, &allocated, sizeof *p); + p[used++] = value; + } + + This causes x2nrealloc to allocate a block of some nonzero size the + first time it is called. + + To have finer-grained control over the initial size, set *PN to a + nonzero value before calling this function with P == NULL. For + example: + + int *p = NULL; + size_t used = 0; + size_t allocated = 0; + size_t allocated1 = 1000; + + void + append_int (int value) + { + if (used == allocated) + { + p = x2nrealloc (p, &allocated1, sizeof *p); + allocated = allocated1; + } + p[used++] = value; + } + + */ + +static_inline void * +x2nrealloc (void *p, size_t *pn, size_t s) +{ + size_t n = *pn; + + if (! p) + { + if (! n) + { + /* The approximate size to use for initial small allocation + requests, when the invoking code specifies an old size of + zero. 64 bytes is the largest "small" request for the + GNU C library malloc. */ + enum { DEFAULT_MXFAST = 64 }; + + n = DEFAULT_MXFAST / s; + n += !n; + } + } + else + { + if (((size_t) -1) / 2 / s < n) + xalloc_die (); + n *= 2; + } + + *pn = n; + return xrealloc (p, n * s); +} + +/* Return a pointer to a new buffer of N bytes. This is like xmalloc, + except it returns char *. */ + +static_inline char * +xcharalloc (size_t n) +{ + return XNMALLOC (n, char); +} + +# endif + +# ifdef __cplusplus +} + +/* C++ does not allow conversions from void * to other pointer types + without a cast. Use templates to work around the problem when + possible. */ + +template inline T * +xrealloc (T *p, size_t s) +{ + return (T *) xrealloc ((void *) p, s); +} + +template inline T * +xnrealloc (T *p, size_t n, size_t s) +{ + return (T *) xnrealloc ((void *) p, n, s); +} + +template inline T * +x2realloc (T *p, size_t *pn) +{ + return (T *) x2realloc ((void *) p, pn); +} + +template inline T * +x2nrealloc (T *p, size_t *pn, size_t s) +{ + return (T *) x2nrealloc ((void *) p, pn, s); +} + +template inline T * +xmemdup (T const *p, size_t s) +{ + return (T *) xmemdup ((void const *) p, s); +} + +# endif + + +#endif /* !XALLOC_H_ */ diff --git a/src/bin/m4/lib/xallocsa.c b/src/bin/m4/lib/xallocsa.c new file mode 100644 index 0000000000..c69128a365 --- /dev/null +++ b/src/bin/m4/lib/xallocsa.c @@ -0,0 +1,35 @@ +/* Safe automatic memory allocation with out of memory checking. + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "xallocsa.h" + +#include "xalloc.h" + +void * +xmallocsa (size_t n) +{ + void *p; + + p = mallocsa (n); + if (p == NULL) + xalloc_die (); + return p; +} diff --git a/src/bin/m4/lib/xallocsa.h b/src/bin/m4/lib/xallocsa.h new file mode 100644 index 0000000000..baede95775 --- /dev/null +++ b/src/bin/m4/lib/xallocsa.h @@ -0,0 +1,54 @@ +/* Safe automatic memory allocation with out of memory checking. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _XALLOCSA_H +#define _XALLOCSA_H + +#include "allocsa.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* xallocsa(N) is a checking safe variant of alloca(N). It allocates N bytes + of memory allocated on the stack, that must be freed using freesa() before + the function returns. Upon failure, it exits with an error message. */ +#if HAVE_ALLOCA +# define xallocsa(N) \ + ((N) < 4032 - sa_increment \ + ? (void *) ((char *) alloca ((N) + sa_increment) + sa_increment) \ + : xmallocsa (N)) +extern void * xmallocsa (size_t n); +#else +# define xallocsa(N) \ + xmalloc (N) +#endif + +/* Maybe we should also define a variant + xnallocsa (size_t n, size_t s) - behaves like xallocsa (n * s) + If this would be useful in your application. please speak up. */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* _XALLOCSA_H */ diff --git a/src/bin/m4/lib/xasprintf.c b/src/bin/m4/lib/xasprintf.c new file mode 100644 index 0000000000..cf1b8a5ee4 --- /dev/null +++ b/src/bin/m4/lib/xasprintf.c @@ -0,0 +1,34 @@ +/* vasprintf and asprintf with out-of-memory checking. + Copyright (C) 1999, 2002-2004, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "xvasprintf.h" + +char * +xasprintf (const char *format, ...) +{ + va_list args; + char *result; + + va_start (args, format); + result = xvasprintf (format, args); + va_end (args); + + return result; +} diff --git a/src/bin/m4/lib/xmalloc.c b/src/bin/m4/lib/xmalloc.c new file mode 100644 index 0000000000..318e0ddb5a --- /dev/null +++ b/src/bin/m4/lib/xmalloc.c @@ -0,0 +1,123 @@ +/* xmalloc.c -- malloc with out of memory checking + + Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, + Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +#if ! HAVE_INLINE +# define static_inline +#endif +#include "xalloc.h" +#undef static_inline + +#include +#include + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +/* 1 if calloc is known to be compatible with GNU calloc. This + matters if we are not also using the calloc module, which defines + HAVE_CALLOC and supports the GNU API even on non-GNU platforms. */ +#if defined HAVE_CALLOC || defined __GLIBC__ +enum { HAVE_GNU_CALLOC = 1 }; +#else +enum { HAVE_GNU_CALLOC = 0 }; +#endif + +/* Allocate N bytes of memory dynamically, with error checking. */ + +void * +xmalloc (size_t n) +{ + void *p = malloc (n); + if (!p && n != 0) + xalloc_die (); + return p; +} + +/* Change the size of an allocated block of memory P to N bytes, + with error checking. */ + +void * +xrealloc (void *p, size_t n) +{ + p = realloc (p, n); + if (!p && n != 0) + xalloc_die (); + return p; +} + +/* If P is null, allocate a block of at least *PN bytes; otherwise, + reallocate P so that it contains more than *PN bytes. *PN must be + nonzero unless P is null. Set *PN to the new block's size, and + return the pointer to the new block. *PN is never set to zero, and + the returned pointer is never null. */ + +void * +x2realloc (void *p, size_t *pn) +{ + return x2nrealloc (p, pn, 1); +} + +/* Allocate S bytes of zeroed memory dynamically, with error checking. + There's no need for xnzalloc (N, S), since it would be equivalent + to xcalloc (N, S). */ + +void * +xzalloc (size_t s) +{ + return memset (xmalloc (s), 0, s); +} + +/* Allocate zeroed memory for N elements of S bytes, with error + checking. S must be nonzero. */ + +void * +xcalloc (size_t n, size_t s) +{ + void *p; + /* Test for overflow, since some calloc implementations don't have + proper overflow checks. But omit overflow and size-zero tests if + HAVE_GNU_CALLOC, since GNU calloc catches overflow and never + returns NULL if successful. */ + if ((! HAVE_GNU_CALLOC && xalloc_oversized (n, s)) + || (! (p = calloc (n, s)) && (HAVE_GNU_CALLOC || n != 0))) + xalloc_die (); + return p; +} + +/* Clone an object P of size S, with error checking. There's no need + for xnmemdup (P, N, S), since xmemdup (P, N * S) works without any + need for an arithmetic overflow check. */ + +void * +xmemdup (void const *p, size_t s) +{ + return memcpy (xmalloc (s), p, s); +} + +/* Clone STRING. */ + +char * +xstrdup (char const *string) +{ + return xmemdup (string, strlen (string) + 1); +} diff --git a/src/bin/m4/lib/xsize.h b/src/bin/m4/lib/xsize.h new file mode 100644 index 0000000000..341fb16ca7 --- /dev/null +++ b/src/bin/m4/lib/xsize.h @@ -0,0 +1,108 @@ +/* xsize.h -- Checked size_t computations. + + Copyright (C) 2003 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _XSIZE_H +#define _XSIZE_H + +/* Get size_t. */ +#include + +/* Get SIZE_MAX. */ +#include +#if HAVE_STDINT_H +# include +#endif + +/* The size of memory objects is often computed through expressions of + type size_t. Example: + void* p = malloc (header_size + n * element_size). + These computations can lead to overflow. When this happens, malloc() + returns a piece of memory that is way too small, and the program then + crashes while attempting to fill the memory. + To avoid this, the functions and macros in this file check for overflow. + The convention is that SIZE_MAX represents overflow. + malloc (SIZE_MAX) is not guaranteed to fail -- think of a malloc + implementation that uses mmap --, it's recommended to use size_overflow_p() + or size_in_bounds_p() before invoking malloc(). + The example thus becomes: + size_t size = xsum (header_size, xtimes (n, element_size)); + void *p = (size_in_bounds_p (size) ? malloc (size) : NULL); +*/ + +/* Convert an arbitrary value >= 0 to type size_t. */ +#define xcast_size_t(N) \ + ((N) <= SIZE_MAX ? (size_t) (N) : SIZE_MAX) + +/* Sum of two sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum (size_t size1, size_t size2) +{ + size_t sum = size1 + size2; + return (sum >= size1 ? sum : SIZE_MAX); +} + +/* Sum of three sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum3 (size_t size1, size_t size2, size_t size3) +{ + return xsum (xsum (size1, size2), size3); +} + +/* Sum of four sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum4 (size_t size1, size_t size2, size_t size3, size_t size4) +{ + return xsum (xsum (xsum (size1, size2), size3), size4); +} + +/* Maximum of two sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xmax (size_t size1, size_t size2) +{ + /* No explicit check is needed here, because for any n: + max (SIZE_MAX, n) == SIZE_MAX and max (n, SIZE_MAX) == SIZE_MAX. */ + return (size1 >= size2 ? size1 : size2); +} + +/* Multiplication of a count with an element size, with overflow check. + The count must be >= 0 and the element size must be > 0. + This is a macro, not an inline function, so that it works correctly even + when N is of a wider tupe and N > SIZE_MAX. */ +#define xtimes(N, ELSIZE) \ + ((N) <= SIZE_MAX / (ELSIZE) ? (size_t) (N) * (ELSIZE) : SIZE_MAX) + +/* Check for overflow. */ +#define size_overflow_p(SIZE) \ + ((SIZE) == SIZE_MAX) +/* Check against overflow. */ +#define size_in_bounds_p(SIZE) \ + ((SIZE) != SIZE_MAX) + +#endif /* _XSIZE_H */ diff --git a/src/bin/m4/lib/xvasprintf.c b/src/bin/m4/lib/xvasprintf.c new file mode 100644 index 0000000000..b68442764d --- /dev/null +++ b/src/bin/m4/lib/xvasprintf.c @@ -0,0 +1,116 @@ +/* vasprintf and asprintf with out-of-memory checking. + Copyright (C) 1999, 2002-2004, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "xvasprintf.h" + +#include +#include +#include + +#include "vasprintf.h" +#include "xalloc.h" + +/* Checked size_t computations. */ +#include "xsize.h" + +/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW. */ +#ifndef EOVERFLOW +# define EOVERFLOW E2BIG +#endif + +static inline char * +xstrcat (size_t argcount, va_list args) +{ + char *result; + va_list ap; + size_t totalsize; + size_t i; + char *p; + + /* Determine the total size. */ + totalsize = 0; + va_copy (ap, args); + for (i = argcount; i > 0; i--) + { + const char *next = va_arg (ap, const char *); + totalsize = xsum (totalsize, strlen (next)); + } + va_end (ap); + + /* Test for overflow in the summing pass above or in (totalsize + 1) below. + Also, don't return a string longer than INT_MAX, for consistency with + vasprintf(). */ + if (totalsize == SIZE_MAX || totalsize > INT_MAX) + { + errno = EOVERFLOW; + return NULL; + } + + /* Allocate and fill the result string. */ + result = XNMALLOC (totalsize + 1, char); + p = result; + for (i = argcount; i > 0; i--) + { + const char *next = va_arg (args, const char *); + size_t len = strlen (next); + memcpy (p, next, len); + p += len; + } + *p = '\0'; + + return result; +} + +char * +xvasprintf (const char *format, va_list args) +{ + char *result; + + /* Recognize the special case format = "%s...%s". It is a frequently used + idiom for string concatenation and needs to be fast. We don't want to + have a separate function xstrcat() for this purpose. */ + { + size_t argcount = 0; + const char *f; + + for (f = format;;) + { + if (*f == '\0') + /* Recognized the special case of string concatenation. */ + return xstrcat (argcount, args); + if (*f != '%') + break; + f++; + if (*f != 's') + break; + f++; + argcount++; + } + } + + if (vasprintf (&result, format, args) < 0) + { + if (errno == ENOMEM) + xalloc_die (); + return NULL; + } + + return result; +} diff --git a/src/bin/m4/lib/xvasprintf.h b/src/bin/m4/lib/xvasprintf.h new file mode 100644 index 0000000000..de55a9f0c8 --- /dev/null +++ b/src/bin/m4/lib/xvasprintf.h @@ -0,0 +1,57 @@ +/* vasprintf and asprintf with out-of-memory checking. + Copyright (C) 2002-2004, 2006 Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _XVASPRINTF_H +#define _XVASPRINTF_H + +/* Get va_list. */ +#include + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ +# define __attribute__(Spec) /* empty */ +# endif +/* The __-protected variants of `format' and `printf' attributes + are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __format__ format +# define __printf__ printf +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Write formatted output to a string dynamically allocated with malloc(), + and return it. Upon [ENOMEM] memory allocation error, call xalloc_die. + On some other error + - [EOVERFLOW] resulting string length is > INT_MAX, + - [EINVAL] invalid format string, + - [EILSEQ] error during conversion between wide and multibyte characters, + return NULL. */ +extern char *xasprintf (const char *format, ...) + __attribute__ ((__format__ (__printf__, 1, 2))); +extern char *xvasprintf (const char *format, va_list args) + __attribute__ ((__format__ (__printf__, 1, 0))); + +#ifdef __cplusplus +} +#endif + +#endif /* _XVASPRINTF_H */ diff --git a/src/bin/m4/src/Jamfile b/src/bin/m4/src/Jamfile new file mode 100644 index 0000000000..e19888352e --- /dev/null +++ b/src/bin/m4/src/Jamfile @@ -0,0 +1,24 @@ +SubDir HAIKU_TOP src bin m4 src ; + +#SubDirCcFlags -DHAVE_CONFIG_H -DPKGDATADIR=\'\"/etc\"\' -DLOCALEDIR=\'\"/etc/locale\"\' ; + +SubDirSysHdrs [ FDirName $(SUBDIR) $(DOTDOT) lib ] ; +SubDirSysHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ; + +BinCommand m4 : + builtin.c + debug.c + eval.c + format.c + freeze.c + input.c + m4.c + macro.c + output.c + path.c + #stackovf.c + symtab.c + : libm4.a + : m4.rdef +; + diff --git a/src/bin/m4/src/Makefile.am b/src/bin/m4/src/Makefile.am new file mode 100644 index 0000000000..ac7f1a82fc --- /dev/null +++ b/src/bin/m4/src/Makefile.am @@ -0,0 +1,30 @@ +## This file is part of GNU M4. +## +## Copyright (C) 2006 Free Software Foundation, Inc. +## +## 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; see the file COPYING. If not, write to +## the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +## Boston, MA 02110-1301, USA. +## +## This file written by Eric Blake + +AUTOMAKE_OPTIONS = nostdinc +AM_CPPFLAGS = -I$(top_srcdir)/lib -I../lib +bin_PROGRAMS = m4 +m4_SOURCES = m4.h m4.c builtin.c debug.c eval.c format.c freeze.c input.c \ +macro.c output.c path.c symtab.c +if STACKOVF +m4_SOURCES += stackovf.c +endif +m4_LDADD = ../lib/libm4.a $(LIBM4_LIBDEPS) diff --git a/src/bin/m4/src/Makefile.in b/src/bin/m4/src/Makefile.in new file mode 100644 index 0000000000..c73eeebd0e --- /dev/null +++ b/src/bin/m4/src/Makefile.in @@ -0,0 +1,510 @@ +# Makefile.in generated by automake 1.10 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = m4$(EXEEXT) +@STACKOVF_TRUE@am__append_1 = stackovf.c +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \ + $(top_srcdir)/m4/alloca.m4 $(top_srcdir)/m4/allocsa.m4 \ + $(top_srcdir)/m4/cloexec.m4 $(top_srcdir)/m4/close-stream.m4 \ + $(top_srcdir)/m4/closeout.m4 $(top_srcdir)/m4/codeset.m4 \ + $(top_srcdir)/m4/config-h.m4 $(top_srcdir)/m4/eealloc.m4 \ + $(top_srcdir)/m4/eoverflow.m4 $(top_srcdir)/m4/error.m4 \ + $(top_srcdir)/m4/exitfail.m4 $(top_srcdir)/m4/extensions.m4 \ + $(top_srcdir)/m4/fatal-signal.m4 $(top_srcdir)/m4/fpending.m4 \ + $(top_srcdir)/m4/free.m4 $(top_srcdir)/m4/getopt.m4 \ + $(top_srcdir)/m4/gl_list.m4 $(top_srcdir)/m4/gnulib-comp.m4 \ + $(top_srcdir)/m4/inline.m4 $(top_srcdir)/m4/intmax_t.m4 \ + $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/longdouble.m4 \ + $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/mbchar.m4 \ + $(top_srcdir)/m4/mbiter.m4 $(top_srcdir)/m4/mbrtowc.m4 \ + $(top_srcdir)/m4/mbstate_t.m4 $(top_srcdir)/m4/memchr.m4 \ + $(top_srcdir)/m4/mkdtemp.m4 $(top_srcdir)/m4/mkstemp.m4 \ + $(top_srcdir)/m4/pathmax.m4 $(top_srcdir)/m4/quotearg.m4 \ + $(top_srcdir)/m4/regex.m4 $(top_srcdir)/m4/sig_atomic_t.m4 \ + $(top_srcdir)/m4/signalblocking.m4 \ + $(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/ssize_t.m4 \ + $(top_srcdir)/m4/stdarg.m4 $(top_srcdir)/m4/stdbool.m4 \ + $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/m4/stdint_h.m4 \ + $(top_srcdir)/m4/stdio-safer.m4 \ + $(top_srcdir)/m4/stdlib-safer.m4 $(top_srcdir)/m4/strcase.m4 \ + $(top_srcdir)/m4/strstr.m4 $(top_srcdir)/m4/strtol.m4 \ + $(top_srcdir)/m4/sys_stat_h.m4 $(top_srcdir)/m4/tempname.m4 \ + $(top_srcdir)/m4/tmpdir.m4 $(top_srcdir)/m4/ulonglong.m4 \ + $(top_srcdir)/m4/unistd-safer.m4 $(top_srcdir)/m4/unistd_h.m4 \ + $(top_srcdir)/m4/unlocked-io.m4 $(top_srcdir)/m4/vasnprintf.m4 \ + $(top_srcdir)/m4/vasprintf.m4 $(top_srcdir)/m4/wchar_t.m4 \ + $(top_srcdir)/m4/wcwidth.m4 $(top_srcdir)/m4/wint_t.m4 \ + $(top_srcdir)/m4/xalloc.m4 $(top_srcdir)/m4/xsize.m4 \ + $(top_srcdir)/m4/xvasprintf.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/lib/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am__m4_SOURCES_DIST = m4.h m4.c builtin.c debug.c eval.c format.c \ + freeze.c input.c macro.c output.c path.c symtab.c stackovf.c +@STACKOVF_TRUE@am__objects_1 = stackovf.$(OBJEXT) +am_m4_OBJECTS = m4.$(OBJEXT) builtin.$(OBJEXT) debug.$(OBJEXT) \ + eval.$(OBJEXT) format.$(OBJEXT) freeze.$(OBJEXT) \ + input.$(OBJEXT) macro.$(OBJEXT) output.$(OBJEXT) \ + path.$(OBJEXT) symtab.$(OBJEXT) $(am__objects_1) +m4_OBJECTS = $(am_m4_OBJECTS) +am__DEPENDENCIES_1 = +m4_DEPENDENCIES = ../lib/libm4.a $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(m4_SOURCES) +DIST_SOURCES = $(am__m4_SOURCES_DIST) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ABSOLUTE_STDINT_H = @ABSOLUTE_STDINT_H@ +ABSOLUTE_SYS_STAT_H = @ABSOLUTE_SYS_STAT_H@ +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +ALLOCA_H = @ALLOCA_H@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@ +BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@ +BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@ +BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@ +BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EOVERFLOW = @EOVERFLOW@ +EXEEXT = @EXEEXT@ +GETOPT_H = @GETOPT_H@ +GREP = @GREP@ +HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ +HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@ +HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ +HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ +HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ +HAVE_STDINT_H = @HAVE_STDINT_H@ +HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@ +HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@ +HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ +HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@ +HAVE_WCHAR_H = @HAVE_WCHAR_H@ +HAVE__BOOL = @HAVE__BOOL@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBM4_LIBDEPS = @LIBM4_LIBDEPS@ +LIBM4_LTLIBDEPS = @LIBM4_LTLIBDEPS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +M4_LIBOBJS = @M4_LIBOBJS@ +M4_LTLIBOBJS = @M4_LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@ +SIZE_T_SUFFIX = @SIZE_T_SUFFIX@ +STDBOOL_H = @STDBOOL_H@ +STDINT_H = @STDINT_H@ +STRIP = @STRIP@ +SYS_STAT_H = @SYS_STAT_H@ +UNISTD_H = @UNISTD_H@ +VERSION = @VERSION@ +WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ +WINT_T_SUFFIX = @WINT_T_SUFFIX@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = nostdinc +AM_CPPFLAGS = -I$(top_srcdir)/lib -I../lib +m4_SOURCES = m4.h m4.c builtin.c debug.c eval.c format.c freeze.c \ + input.c macro.c output.c path.c symtab.c $(am__append_1) +m4_LDADD = ../lib/libm4.a $(LIBM4_LIBDEPS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +m4$(EXEEXT): $(m4_OBJECTS) $(m4_DEPENDENCIES) + @rm -f m4$(EXEEXT) + $(LINK) $(m4_OBJECTS) $(m4_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/builtin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/format.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freeze.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m4.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macro.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/path.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stackovf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symtab.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/bin/m4/src/builtin.c b/src/bin/m4/src/builtin.c new file mode 100644 index 0000000000..e7b120224d --- /dev/null +++ b/src/bin/m4/src/builtin.c @@ -0,0 +1,2081 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2000, 2004, 2006 + Free Software Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* Code for all builtin macros, initialization of symbol table, and + expansion of user defined macros. */ + +#include "m4.h" + +extern FILE *popen (); + +#include "regex.h" +#include "strstr.h" + +#if HAVE_SYS_WAIT_H +# include +#endif + +#define ARG(i) (argc > (i) ? TOKEN_DATA_TEXT (argv[i]) : "") + +/* Initialization of builtin and predefined macros. The table + "builtin_tab" is both used for initialization, and by the "builtin" + builtin. */ + +#define DECLARE(name) \ + static void name (struct obstack *, int, token_data **) + +DECLARE (m4___file__); +DECLARE (m4___line__); +DECLARE (m4___program__); +DECLARE (m4_builtin); +DECLARE (m4_changecom); +DECLARE (m4_changequote); +#ifdef ENABLE_CHANGEWORD +DECLARE (m4_changeword); +#endif +DECLARE (m4_debugmode); +DECLARE (m4_debugfile); +DECLARE (m4_decr); +DECLARE (m4_define); +DECLARE (m4_defn); +DECLARE (m4_divert); +DECLARE (m4_divnum); +DECLARE (m4_dnl); +DECLARE (m4_dumpdef); +DECLARE (m4_errprint); +DECLARE (m4_esyscmd); +DECLARE (m4_eval); +DECLARE (m4_format); +DECLARE (m4_ifdef); +DECLARE (m4_ifelse); +DECLARE (m4_include); +DECLARE (m4_incr); +DECLARE (m4_index); +DECLARE (m4_indir); +DECLARE (m4_len); +DECLARE (m4_m4exit); +DECLARE (m4_m4wrap); +DECLARE (m4_maketemp); +DECLARE (m4_mkstemp); +DECLARE (m4_patsubst); +DECLARE (m4_popdef); +DECLARE (m4_pushdef); +DECLARE (m4_regexp); +DECLARE (m4_shift); +DECLARE (m4_sinclude); +DECLARE (m4_substr); +DECLARE (m4_syscmd); +DECLARE (m4_sysval); +DECLARE (m4_traceoff); +DECLARE (m4_traceon); +DECLARE (m4_translit); +DECLARE (m4_undefine); +DECLARE (m4_undivert); + +#undef DECLARE + +static builtin +builtin_tab[] = +{ + + /* name GNUext macros blind function */ + + { "__file__", true, false, false, m4___file__ }, + { "__line__", true, false, false, m4___line__ }, + { "__program__", true, false, false, m4___program__ }, + { "builtin", true, true, true, m4_builtin }, + { "changecom", false, false, false, m4_changecom }, + { "changequote", false, false, false, m4_changequote }, +#ifdef ENABLE_CHANGEWORD + { "changeword", true, false, true, m4_changeword }, +#endif + { "debugmode", true, false, false, m4_debugmode }, + { "debugfile", true, false, false, m4_debugfile }, + { "decr", false, false, true, m4_decr }, + { "define", false, true, true, m4_define }, + { "defn", false, false, true, m4_defn }, + { "divert", false, false, false, m4_divert }, + { "divnum", false, false, false, m4_divnum }, + { "dnl", false, false, false, m4_dnl }, + { "dumpdef", false, false, false, m4_dumpdef }, + { "errprint", false, false, true, m4_errprint }, + { "esyscmd", true, false, true, m4_esyscmd }, + { "eval", false, false, true, m4_eval }, + { "format", true, false, true, m4_format }, + { "ifdef", false, false, true, m4_ifdef }, + { "ifelse", false, false, true, m4_ifelse }, + { "include", false, false, true, m4_include }, + { "incr", false, false, true, m4_incr }, + { "index", false, false, true, m4_index }, + { "indir", true, true, true, m4_indir }, + { "len", false, false, true, m4_len }, + { "m4exit", false, false, false, m4_m4exit }, + { "m4wrap", false, false, true, m4_m4wrap }, + { "maketemp", false, false, true, m4_maketemp }, + { "mkstemp", false, false, true, m4_mkstemp }, + { "patsubst", true, false, true, m4_patsubst }, + { "popdef", false, false, true, m4_popdef }, + { "pushdef", false, true, true, m4_pushdef }, + { "regexp", true, false, true, m4_regexp }, + { "shift", false, false, true, m4_shift }, + { "sinclude", false, false, true, m4_sinclude }, + { "substr", false, false, true, m4_substr }, + { "syscmd", false, false, true, m4_syscmd }, + { "sysval", false, false, false, m4_sysval }, + { "traceoff", false, false, false, m4_traceoff }, + { "traceon", false, false, false, m4_traceon }, + { "translit", false, false, true, m4_translit }, + { "undefine", false, false, true, m4_undefine }, + { "undivert", false, false, false, m4_undivert }, + + { 0, false, false, false, 0 }, + + /* placeholder is intentionally stuck after the table end delimiter, + so that we can easily find it, while not treating it as a real + builtin. */ + { "placeholder", true, false, false, m4_placeholder }, +}; + +static predefined const +predefined_tab[] = +{ +#if UNIX + { "unix", "__unix__", "" }, +#elif W32_NATIVE + { "windows", "__windows__", "" }, +#elif OS2 + { "os2", "__os2__", "" }, +#else +# warning Platform macro not provided +#endif + { NULL, "__gnu__", "" }, + + { NULL, NULL, NULL }, +}; + +/*----------------------------------------. +| Find the builtin, which lives on ADDR. | +`----------------------------------------*/ + +const builtin * +find_builtin_by_addr (builtin_func *func) +{ + const builtin *bp; + + for (bp = &builtin_tab[0]; bp->name != NULL; bp++) + if (bp->func == func) + return bp; + if (func == m4_placeholder) + return bp + 1; + return NULL; +} + +/*----------------------------------------------------------. +| Find the builtin, which has NAME. On failure, return the | +| placeholder builtin. | +`----------------------------------------------------------*/ + +const builtin * +find_builtin_by_name (const char *name) +{ + const builtin *bp; + + for (bp = &builtin_tab[0]; bp->name != NULL; bp++) + if (strcmp (bp->name, name) == 0) + return bp; + return bp + 1; +} + +/*-------------------------------------------------------------------------. +| Install a builtin macro with name NAME, bound to the C function given in | +| BP. MODE is SYMBOL_INSERT or SYMBOL_PUSHDEF. TRACED defines whether | +| NAME is to be traced. | +`-------------------------------------------------------------------------*/ + +void +define_builtin (const char *name, const builtin *bp, symbol_lookup mode) +{ + symbol *sym; + + sym = lookup_symbol (name, mode); + SYMBOL_TYPE (sym) = TOKEN_FUNC; + SYMBOL_MACRO_ARGS (sym) = bp->groks_macro_args; + SYMBOL_BLIND_NO_ARGS (sym) = bp->blind_if_no_args; + SYMBOL_FUNC (sym) = bp->func; +} + +/*-------------------------------------------------------------------------. +| Define a predefined or user-defined macro, with name NAME, and expansion | +| TEXT. MODE destinguishes between the "define" and the "pushdef" case. | +| It is also used from main (). | +`-------------------------------------------------------------------------*/ + +void +define_user_macro (const char *name, const char *text, symbol_lookup mode) +{ + symbol *s; + + s = lookup_symbol (name, mode); + if (SYMBOL_TYPE (s) == TOKEN_TEXT) + free (SYMBOL_TEXT (s)); + + SYMBOL_TYPE (s) = TOKEN_TEXT; + SYMBOL_TEXT (s) = xstrdup (text ? text : ""); +} + +/*-----------------------------------------------. +| Initialize all builtin and predefined macros. | +`-----------------------------------------------*/ + +void +builtin_init (void) +{ + const builtin *bp; + const predefined *pp; + char *string; + + for (bp = &builtin_tab[0]; bp->name != NULL; bp++) + if (!no_gnu_extensions || !bp->gnu_extension) + { + if (prefix_all_builtins) + { + string = (char *) xmalloc (strlen (bp->name) + 4); + strcpy (string, "m4_"); + strcat (string, bp->name); + define_builtin (string, bp, SYMBOL_INSERT); + free (string); + } + else + define_builtin (bp->name, bp, SYMBOL_INSERT); + } + + for (pp = &predefined_tab[0]; pp->func != NULL; pp++) + if (no_gnu_extensions) + { + if (pp->unix_name != NULL) + define_user_macro (pp->unix_name, pp->func, SYMBOL_INSERT); + } + else + { + if (pp->gnu_name != NULL) + define_user_macro (pp->gnu_name, pp->func, SYMBOL_INSERT); + } +} + +/*------------------------------------------------------------------------. +| Give friendly warnings if a builtin macro is passed an inappropriate | +| number of arguments. NAME is macro name for messages, ARGC is actual | +| number of arguments, MIN is the minimum number of acceptable arguments, | +| negative if not applicable, MAX is the maximum number, negative if not | +| applicable. | +`------------------------------------------------------------------------*/ + +static bool +bad_argc (token_data *name, int argc, int min, int max) +{ + bool isbad = false; + + if (min > 0 && argc < min) + { + if (!suppress_warnings) + M4ERROR ((warning_status, 0, + "Warning: too few arguments to builtin `%s'", + TOKEN_DATA_TEXT (name))); + isbad = true; + } + else if (max > 0 && argc > max && !suppress_warnings) + M4ERROR ((warning_status, 0, + "Warning: excess arguments to builtin `%s' ignored", + TOKEN_DATA_TEXT (name))); + + return isbad; +} + +/*--------------------------------------------------------------------------. +| The function numeric_arg () converts ARG to an int pointed to by VALUEP. | +| If the conversion fails, print error message for macro MACRO. Return | +| true iff conversion succeeds. | +`--------------------------------------------------------------------------*/ + +static bool +numeric_arg (token_data *macro, const char *arg, int *valuep) +{ + char *endp; + + if (*arg == '\0') + { + *valuep = 0; + M4ERROR ((warning_status, 0, + "empty string treated as 0 in builtin `%s'", + TOKEN_DATA_TEXT (macro))); + } + else + { + errno = 0; + *valuep = strtol (arg, &endp, 10); + if (*endp != '\0') + { + M4ERROR ((warning_status, 0, + "non-numeric argument to builtin `%s'", + TOKEN_DATA_TEXT (macro))); + return false; + } + if (isspace (to_uchar (*arg))) + M4ERROR ((warning_status, 0, + "leading whitespace ignored in builtin `%s'", + TOKEN_DATA_TEXT (macro))); + else if (errno == ERANGE) + M4ERROR ((warning_status, 0, + "numeric overflow detected in builtin `%s'", + TOKEN_DATA_TEXT (macro))); + } + return true; +} + +/*------------------------------------------------------------------------. +| The function ntoa () converts VALUE to a signed ascii representation in | +| radix RADIX. | +`------------------------------------------------------------------------*/ + +/* Digits for number to ascii conversions. */ +static char const digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +static const char * +ntoa (eval_t value, int radix) +{ + bool negative; + unsigned_eval_t uvalue; + static char str[256]; + char *s = &str[sizeof str]; + + *--s = '\0'; + + if (value < 0) + { + negative = true; + uvalue = (unsigned_eval_t) -value; + } + else + { + negative = false; + uvalue = (unsigned_eval_t) value; + } + + do + { + *--s = digits[uvalue % radix]; + uvalue /= radix; + } + while (uvalue > 0); + + if (negative) + *--s = '-'; + return s; +} + +/*----------------------------------------------------------------------. +| Format an int VAL, and stuff it into an obstack OBS. Used for macros | +| expanding to numbers. | +`----------------------------------------------------------------------*/ + +static void +shipout_int (struct obstack *obs, int val) +{ + const char *s; + + s = ntoa ((eval_t) val, 10); + obstack_grow (obs, s, strlen (s)); +} + +/*----------------------------------------------------------------------. +| Print ARGC arguments from the table ARGV to obstack OBS, separated by | +| SEP, and quoted by the current quotes, if QUOTED is true. | +`----------------------------------------------------------------------*/ + +static void +dump_args (struct obstack *obs, int argc, token_data **argv, + const char *sep, bool quoted) +{ + int i; + size_t len = strlen (sep); + + for (i = 1; i < argc; i++) + { + if (i > 1) + obstack_grow (obs, sep, len); + if (quoted) + obstack_grow (obs, lquote.string, lquote.length); + obstack_grow (obs, TOKEN_DATA_TEXT (argv[i]), + strlen (TOKEN_DATA_TEXT (argv[i]))); + if (quoted) + obstack_grow (obs, rquote.string, rquote.length); + } +} + +/* The rest of this file is code for builtins and expansion of user + defined macros. All the functions for builtins have a prototype as: + + void m4_MACRONAME (struct obstack *obs, int argc, char *argv[]); + + The function are expected to leave their expansion on the obstack OBS, + as an unfinished object. ARGV is a table of ARGC pointers to the + individual arguments to the macro. Please note that in general + argv[argc] != NULL. */ + +/* The first section are macros for definining, undefining, examining, + changing, ... other macros. */ + +/*-------------------------------------------------------------------------. +| The function define_macro is common for the builtins "define", | +| "undefine", "pushdef" and "popdef". ARGC and ARGV is as for the caller, | +| and MODE argument determines how the macro name is entered into the | +| symbol table. | +`-------------------------------------------------------------------------*/ + +static void +define_macro (int argc, token_data **argv, symbol_lookup mode) +{ + const builtin *bp; + + if (bad_argc (argv[0], argc, 2, 3)) + return; + + if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT) + { + M4ERROR ((warning_status, 0, + "Warning: %s: invalid macro name ignored", ARG (0))); + return; + } + + if (argc == 2) + { + define_user_macro (ARG (1), "", mode); + return; + } + + switch (TOKEN_DATA_TYPE (argv[2])) + { + case TOKEN_TEXT: + define_user_macro (ARG (1), ARG (2), mode); + break; + + case TOKEN_FUNC: + bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[2])); + if (bp == NULL) + return; + else + define_builtin (ARG (1), bp, mode); + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad token data type in define_macro ()")); + abort (); + } +} + +static void +m4_define (struct obstack *obs, int argc, token_data **argv) +{ + define_macro (argc, argv, SYMBOL_INSERT); +} + +static void +m4_undefine (struct obstack *obs, int argc, token_data **argv) +{ + int i; + if (bad_argc (argv[0], argc, 2, -1)) + return; + for (i = 1; i < argc; i++) + lookup_symbol (ARG (i), SYMBOL_DELETE); +} + +static void +m4_pushdef (struct obstack *obs, int argc, token_data **argv) +{ + define_macro (argc, argv, SYMBOL_PUSHDEF); +} + +static void +m4_popdef (struct obstack *obs, int argc, token_data **argv) +{ + int i; + if (bad_argc (argv[0], argc, 2, -1)) + return; + for (i = 1; i < argc; i++) + lookup_symbol (ARG (i), SYMBOL_POPDEF); +} + +/*---------------------. +| Conditionals of m4. | +`---------------------*/ + +static void +m4_ifdef (struct obstack *obs, int argc, token_data **argv) +{ + symbol *s; + const char *result; + + if (bad_argc (argv[0], argc, 3, 4)) + return; + s = lookup_symbol (ARG (1), SYMBOL_LOOKUP); + + if (s != NULL && SYMBOL_TYPE (s) != TOKEN_VOID) + result = ARG (2); + else if (argc >= 4) + result = ARG (3); + else + result = NULL; + + if (result != NULL) + obstack_grow (obs, result, strlen (result)); +} + +static void +m4_ifelse (struct obstack *obs, int argc, token_data **argv) +{ + const char *result; + token_data *argv0; + + if (argc == 2) + return; + + if (bad_argc (argv[0], argc, 4, -1)) + return; + else + /* Diagnose excess arguments if 5, 8, 11, etc., actual arguments. */ + bad_argc (argv[0], (argc + 2) % 3, -1, 1); + + argv0 = argv[0]; + argv++; + argc--; + + result = NULL; + while (result == NULL) + + if (strcmp (ARG (0), ARG (1)) == 0) + result = ARG (2); + + else + switch (argc) + { + case 3: + return; + + case 4: + case 5: + result = ARG (3); + break; + + default: + argc -= 3; + argv += 3; + } + + obstack_grow (obs, result, strlen (result)); +} + +/*---------------------------------------------------------------------. +| The function dump_symbol () is for use by "dumpdef". It builds up a | +| table of all defined, un-shadowed, symbols. | +`---------------------------------------------------------------------*/ + +/* The structure dump_symbol_data is used to pass the information needed + from call to call to dump_symbol. */ + +struct dump_symbol_data +{ + struct obstack *obs; /* obstack for table */ + symbol **base; /* base of table */ + int size; /* size of table */ +}; + +static void +dump_symbol (symbol *sym, void *arg) +{ + struct dump_symbol_data *data = (struct dump_symbol_data *) arg; + if (!SYMBOL_SHADOWED (sym) && SYMBOL_TYPE (sym) != TOKEN_VOID) + { + obstack_blank (data->obs, sizeof (symbol *)); + data->base = (symbol **) obstack_base (data->obs); + data->base[data->size++] = sym; + } +} + +/*------------------------------------------------------------------------. +| qsort comparison routine, for sorting the table made in m4_dumpdef (). | +`------------------------------------------------------------------------*/ + +static int +dumpdef_cmp (const void *s1, const void *s2) +{ + return strcmp (SYMBOL_NAME (* (symbol *const *) s1), + SYMBOL_NAME (* (symbol *const *) s2)); +} + +/*-------------------------------------------------------------------------. +| Implementation of "dumpdef" itself. It builds up a table of pointers to | +| symbols, sorts it and prints the sorted table. | +`-------------------------------------------------------------------------*/ + +static void +m4_dumpdef (struct obstack *obs, int argc, token_data **argv) +{ + symbol *s; + int i; + struct dump_symbol_data data; + const builtin *bp; + + data.obs = obs; + data.base = (symbol **) obstack_base (obs); + data.size = 0; + + if (argc == 1) + { + hack_all_symbols (dump_symbol, &data); + } + else + { + for (i = 1; i < argc; i++) + { + s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_LOOKUP); + if (s != NULL && SYMBOL_TYPE (s) != TOKEN_VOID) + dump_symbol (s, &data); + else + M4ERROR ((warning_status, 0, + "undefined macro `%s'", TOKEN_DATA_TEXT (argv[i]))); + } + } + + /* Make table of symbols invisible to expand_macro (). */ + + obstack_finish (obs); + + qsort (data.base, data.size, sizeof (symbol *), dumpdef_cmp); + + for (; data.size > 0; --data.size, data.base++) + { + DEBUG_PRINT1 ("%s:\t", SYMBOL_NAME (data.base[0])); + + switch (SYMBOL_TYPE (data.base[0])) + { + case TOKEN_TEXT: + if (debug_level & DEBUG_TRACE_QUOTE) + DEBUG_PRINT3 ("%s%s%s\n", + lquote.string, SYMBOL_TEXT (data.base[0]), rquote.string); + else + DEBUG_PRINT1 ("%s\n", SYMBOL_TEXT (data.base[0])); + break; + + case TOKEN_FUNC: + bp = find_builtin_by_addr (SYMBOL_FUNC (data.base[0])); + if (bp == NULL) + { + M4ERROR ((warning_status, 0, "\ +INTERNAL ERROR: builtin not found in builtin table")); + abort (); + } + DEBUG_PRINT1 ("<%s>\n", bp->name); + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad token data type in m4_dumpdef ()")); + abort (); + break; + } + } +} + +/*---------------------------------------------------------------------. +| The builtin "builtin" allows calls to builtin macros, even if their | +| definition has been overridden or shadowed. It is thus possible to | +| redefine builtins, and still access their original definition. This | +| macro is not available in compatibility mode. | +`---------------------------------------------------------------------*/ + +static void +m4_builtin (struct obstack *obs, int argc, token_data **argv) +{ + const builtin *bp; + const char *name; + + if (bad_argc (argv[0], argc, 2, -1)) + return; + if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT) + { + M4ERROR ((warning_status, 0, + "Warning: %s: invalid macro name ignored", ARG (0))); + return; + } + + name = ARG (1); + bp = find_builtin_by_name (name); + if (bp->func == m4_placeholder) + M4ERROR ((warning_status, 0, + "undefined builtin `%s'", name)); + else + { + int i; + if (! bp->groks_macro_args) + for (i = 2; i < argc; i++) + if (TOKEN_DATA_TYPE (argv[i]) != TOKEN_TEXT) + { + TOKEN_DATA_TYPE (argv[i]) = TOKEN_TEXT; + TOKEN_DATA_TEXT (argv[i]) = (char *) ""; + } + bp->func (obs, argc - 1, argv + 1); + } +} + +/*------------------------------------------------------------------------. +| The builtin "indir" allows indirect calls to macros, even if their name | +| is not a proper macro name. It is thus possible to define macros with | +| ill-formed names for internal use in larger macro packages. This macro | +| is not available in compatibility mode. | +`------------------------------------------------------------------------*/ + +static void +m4_indir (struct obstack *obs, int argc, token_data **argv) +{ + symbol *s; + const char *name; + + if (bad_argc (argv[0], argc, 2, -1)) + return; + if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT) + { + M4ERROR ((warning_status, 0, + "Warning: %s: invalid macro name ignored", ARG (0))); + return; + } + + name = ARG (1); + s = lookup_symbol (name, SYMBOL_LOOKUP); + if (s == NULL || SYMBOL_TYPE (s) == TOKEN_VOID) + M4ERROR ((warning_status, 0, + "undefined macro `%s'", name)); + else + { + int i; + if (! SYMBOL_MACRO_ARGS (s)) + for (i = 2; i < argc; i++) + if (TOKEN_DATA_TYPE (argv[i]) != TOKEN_TEXT) + { + TOKEN_DATA_TYPE (argv[i]) = TOKEN_TEXT; + TOKEN_DATA_TEXT (argv[i]) = (char *) ""; + } + call_macro (s, argc - 1, argv + 1, obs); + } +} + +/*-------------------------------------------------------------------------. +| The macro "defn" returns the quoted definition of the macro named by the | +| first argument. If the macro is builtin, it will push a special | +| macro-definition token on the input stack. | +`-------------------------------------------------------------------------*/ + +static void +m4_defn (struct obstack *obs, int argc, token_data **argv) +{ + symbol *s; + builtin_func *b; + + if (bad_argc (argv[0], argc, 2, 2)) + return; + + s = lookup_symbol (ARG (1), SYMBOL_LOOKUP); + if (s == NULL) + return; + + switch (SYMBOL_TYPE (s)) + { + case TOKEN_TEXT: + obstack_grow (obs, lquote.string, lquote.length); + obstack_grow (obs, SYMBOL_TEXT (s), strlen (SYMBOL_TEXT (s))); + obstack_grow (obs, rquote.string, rquote.length); + break; + + case TOKEN_FUNC: + b = SYMBOL_FUNC (s); + if (b == m4_placeholder) + M4ERROR ((warning_status, 0, "\ +builtin `%s' requested by frozen file is not supported", ARG (1))); + else + push_macro (b); + break; + + case TOKEN_VOID: + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad symbol type in m4_defn ()")); + abort (); + } +} + +/*------------------------------------------------------------------------. +| This section contains macros to handle the builtins "syscmd", "esyscmd" | +| and "sysval". "esyscmd" is GNU specific. | +`------------------------------------------------------------------------*/ + +/* Helper macros for readability. */ +#if UNIX || defined WEXITSTATUS +# define M4SYSVAL_EXITBITS(status) \ + (WIFEXITED (status) ? WEXITSTATUS (status) : 0) +# define M4SYSVAL_TERMSIGBITS(status) \ + (WIFSIGNALED (status) ? WTERMSIG (status) << 8 : 0) + +#else /* ! UNIX && ! defined WEXITSTATUS */ +/* Platforms such as mingw do not support the notion of reporting + which signal terminated a process. Furthermore if WEXITSTATUS was + not provided, then the exit value is in the low eight bits. */ +# define M4SYSVAL_EXITBITS(status) status +# define M4SYSVAL_TERMSIGBITS(status) 0 +#endif /* ! UNIX && ! defined WEXITSTATUS */ + +/* Fallback definitions if or are inadequate. */ +#ifndef WEXITSTATUS +# define WEXITSTATUS(status) (((status) >> 8) & 0xff) +#endif +#ifndef WTERMSIG +# define WTERMSIG(status) ((status) & 0x7f) +#endif +#ifndef WIFSIGNALED +# define WIFSIGNALED(status) (WTERMSIG (status) != 0) +#endif +#ifndef WIFEXITED +# define WIFEXITED(status) (WTERMSIG (status) == 0) +#endif + +/* Exit code from last "syscmd" command. */ +static int sysval; + +static void +m4_syscmd (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 2, 2)) + { + /* The empty command is successful. */ + sysval = 0; + return; + } + + debug_flush_files (); + sysval = system (ARG (1)); +#if FUNC_SYSTEM_BROKEN + /* OS/2 has a buggy system() that returns exit status in the lowest eight + bits, although pclose() and WEXITSTATUS are defined to return exit + status in the next eight bits. This approach can't detect signals, but + at least syscmd(`ls') still works when stdout is a terminal. An + alternate approach is popen/insert_file/pclose, but that makes stdout + a pipe, which can change how some child processes behave. */ + if (sysval != -1) + sysval <<= 8; +#endif /* FUNC_SYSTEM_BROKEN */ +} + +static void +m4_esyscmd (struct obstack *obs, int argc, token_data **argv) +{ + FILE *pin; + int ch; + + if (bad_argc (argv[0], argc, 2, 2)) + { + /* The empty command is successful. */ + sysval = 0; + return; + } + + debug_flush_files (); + errno = 0; + pin = popen (ARG (1), "r"); + if (pin == NULL) + { + M4ERROR ((warning_status, errno, + "cannot open pipe to command `%s'", ARG (1))); + sysval = -1; + } + else + { + while ((ch = getc (pin)) != EOF) + obstack_1grow (obs, (char) ch); + sysval = pclose (pin); + } +} + +static void +m4_sysval (struct obstack *obs, int argc, token_data **argv) +{ + shipout_int (obs, (sysval == -1 ? 127 + : (M4SYSVAL_EXITBITS (sysval) + | M4SYSVAL_TERMSIGBITS (sysval)))); +} + +/*-------------------------------------------------------------------------. +| This section contains the top level code for the "eval" builtin. The | +| actual work is done in the function evaluate (), which lives in eval.c. | +`-------------------------------------------------------------------------*/ + +static void +m4_eval (struct obstack *obs, int argc, token_data **argv) +{ + eval_t value = 0; + int radix = 10; + int min = 1; + const char *s; + + if (bad_argc (argv[0], argc, 2, 4)) + return; + + if (*ARG (2) && !numeric_arg (argv[0], ARG (2), &radix)) + return; + + if (radix < 1 || radix > (int) strlen (digits)) + { + M4ERROR ((warning_status, 0, + "radix in builtin `%s' out of range (radix = %d)", + ARG (0), radix)); + return; + } + + if (argc >= 4 && !numeric_arg (argv[0], ARG (3), &min)) + return; + if (min < 0) + { + M4ERROR ((warning_status, 0, + "negative width to builtin `%s'", ARG (0))); + return; + } + + if (!*ARG (1)) + M4ERROR ((warning_status, 0, + "empty string treated as 0 in builtin `%s'", ARG (0))); + else if (evaluate (ARG (1), &value)) + return; + + if (radix == 1) + { + if (value < 0) + { + obstack_1grow (obs, '-'); + value = -value; + } + /* This assumes 2's-complement for correctly handling INT_MIN. */ + while (min-- - value > 0) + obstack_1grow (obs, '0'); + while (value-- != 0) + obstack_1grow (obs, '1'); + obstack_1grow (obs, '\0'); + return; + } + + s = ntoa (value, radix); + + if (*s == '-') + { + obstack_1grow (obs, '-'); + s++; + } + for (min -= strlen (s); --min >= 0;) + obstack_1grow (obs, '0'); + + obstack_grow (obs, s, strlen (s)); +} + +static void +m4_incr (struct obstack *obs, int argc, token_data **argv) +{ + int value; + + if (bad_argc (argv[0], argc, 2, 2)) + return; + + if (!numeric_arg (argv[0], ARG (1), &value)) + return; + + shipout_int (obs, value + 1); +} + +static void +m4_decr (struct obstack *obs, int argc, token_data **argv) +{ + int value; + + if (bad_argc (argv[0], argc, 2, 2)) + return; + + if (!numeric_arg (argv[0], ARG (1), &value)) + return; + + shipout_int (obs, value - 1); +} + +/* This section contains the macros "divert", "undivert" and "divnum" for + handling diversion. The utility functions used lives in output.c. */ + +/*-----------------------------------------------------------------------. +| Divert further output to the diversion given by ARGV[1]. Out of range | +| means discard further output. | +`-----------------------------------------------------------------------*/ + +static void +m4_divert (struct obstack *obs, int argc, token_data **argv) +{ + int i = 0; + + if (bad_argc (argv[0], argc, 1, 2)) + return; + + if (argc >= 2 && !numeric_arg (argv[0], ARG (1), &i)) + return; + + make_diversion (i); +} + +/*-----------------------------------------------------. +| Expand to the current diversion number, -1 if none. | +`-----------------------------------------------------*/ + +static void +m4_divnum (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 1, 1)) + return; + shipout_int (obs, current_diversion); +} + +/*-----------------------------------------------------------------------. +| Bring back the diversion given by the argument list. If none is | +| specified, bring back all diversions. GNU specific is the option of | +| undiverting named files, by passing a non-numeric argument to undivert | +| (). | +`-----------------------------------------------------------------------*/ + +static void +m4_undivert (struct obstack *obs, int argc, token_data **argv) +{ + int i, file; + FILE *fp; + char *endp; + + if (argc == 1) + undivert_all (); + else + for (i = 1; i < argc; i++) + { + file = strtol (ARG (i), &endp, 10); + if (*endp == '\0' && !isspace (to_uchar (*ARG (i)))) + insert_diversion (file); + else if (no_gnu_extensions) + M4ERROR ((warning_status, 0, + "non-numeric argument to builtin `%s'", ARG (0))); + else + { + fp = m4_path_search (ARG (i), NULL); + if (fp != NULL) + { + insert_file (fp); + if (fclose (fp) == EOF) + M4ERROR ((warning_status, errno, + "error undiverting `%s'", ARG (i))); + } + else + M4ERROR ((warning_status, errno, + "cannot undivert `%s'", ARG (i))); + } + } +} + +/* This section contains various macros, which does not fall into any + specific group. These are "dnl", "shift", "changequote", "changecom" + and "changeword". */ + +/*------------------------------------------------------------------------. +| Delete all subsequent whitespace from input. The function skip_line () | +| lives in input.c. | +`------------------------------------------------------------------------*/ + +static void +m4_dnl (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 1, 1)) + return; + + skip_line (); +} + +/*-------------------------------------------------------------------------. +| Shift all argument one to the left, discarding the first argument. Each | +| output argument is quoted with the current quotes. | +`-------------------------------------------------------------------------*/ + +static void +m4_shift (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 2, -1)) + return; + dump_args (obs, argc - 1, argv + 1, ",", true); +} + +/*--------------------------------------------------------------------------. +| Change the current quotes. The function set_quotes () lives in input.c. | +`--------------------------------------------------------------------------*/ + +static void +m4_changequote (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 1, 3)) + return; + + /* Explicit NULL distinguishes between empty and missing argument. */ + set_quotes ((argc >= 2) ? TOKEN_DATA_TEXT (argv[1]) : NULL, + (argc >= 3) ? TOKEN_DATA_TEXT (argv[2]) : NULL); +} + +/*--------------------------------------------------------------------. +| Change the current comment delimiters. The function set_comment () | +| lives in input.c. | +`--------------------------------------------------------------------*/ + +static void +m4_changecom (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 1, 3)) + return; + + /* Explicit NULL distinguishes between empty and missing argument. */ + set_comment ((argc >= 2) ? TOKEN_DATA_TEXT (argv[1]) : NULL, + (argc >= 3) ? TOKEN_DATA_TEXT (argv[2]) : NULL); +} + +#ifdef ENABLE_CHANGEWORD + +/*-----------------------------------------------------------------------. +| Change the regular expression used for breaking the input into words. | +| The function set_word_regexp () lives in input.c. | +`-----------------------------------------------------------------------*/ + +static void +m4_changeword (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 2, 2)) + return; + + set_word_regexp (TOKEN_DATA_TEXT (argv[1])); +} + +#endif /* ENABLE_CHANGEWORD */ + +/* This section contains macros for inclusion of other files -- "include" + and "sinclude". This differs from bringing back diversions, in that + the input is scanned before being copied to the output. */ + +/*-------------------------------------------------------------------------. +| Generic include function. Include the file given by the first argument, | +| if it exists. Complain about inaccesible files iff SILENT is false. | +`-------------------------------------------------------------------------*/ + +static void +include (int argc, token_data **argv, bool silent) +{ + FILE *fp; + char *name; + + if (bad_argc (argv[0], argc, 2, 2)) + return; + + fp = m4_path_search (ARG (1), &name); + if (fp == NULL) + { + if (!silent) + M4ERROR ((warning_status, errno, + "cannot open `%s'", ARG (1))); + return; + } + + push_file (fp, name, true); + free (name); +} + +/*------------------------------------------------. +| Include a file, complaining in case of errors. | +`------------------------------------------------*/ + +static void +m4_include (struct obstack *obs, int argc, token_data **argv) +{ + include (argc, argv, false); +} + +/*----------------------------------. +| Include a file, ignoring errors. | +`----------------------------------*/ + +static void +m4_sinclude (struct obstack *obs, int argc, token_data **argv) +{ + include (argc, argv, true); +} + +/* More miscellaneous builtins -- "maketemp", "errprint", "__file__", + "__line__", and "__program__". The last three are GNU specific. */ + +/*------------------------------------------------------------------. +| Use the first argument as at template for a temporary file name. | +`------------------------------------------------------------------*/ + +/* Add trailing 'X' to NAME if necessary, securely create the file, + and place the new file name on OBS. */ +static void +mkstemp_helper (struct obstack *obs, const char *name) +{ + int fd; + int len; + int i; + + /* Guarantee that there are six trailing 'X' characters, even if the + user forgot to supply them. */ + len = strlen (name); + obstack_grow (obs, name, len); + for (i = 0; len > 0 && i < 6; i++) + if (name[--len] != 'X') + break; + for (; i < 6; i++) + obstack_1grow (obs, 'X'); + obstack_1grow (obs, '\0'); + + errno = 0; + fd = mkstemp ((char *) obstack_base (obs)); + if (fd < 0) + { + M4ERROR ((0, errno, "cannot create tempfile `%s'", name)); + obstack_free (obs, obstack_finish (obs)); + } + else + close (fd); +} + +static void +m4_maketemp (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 2, 2)) + return; + if (no_gnu_extensions) + { + /* POSIX states "any trailing 'X' characters [are] replaced with + the current process ID as a string", without referencing the + file system. Horribly insecure, but we have to do it when we + are in traditional mode. + + For reference, Solaris m4 does: + maketemp() -> `' + maketemp(X) -> `X' + maketemp(XX) -> `Xn', where n is last digit of pid + maketemp(XXXXXXXX) -> `X00nnnnn', where nnnnn is 16-bit pid + */ + const char *str = ARG (1); + int len = strlen (str); + int i; + int len2; + + M4ERROR ((warning_status, 0, "recommend using mkstemp instead")); + for (i = len; i > 1; i--) + if (str[i - 1] != 'X') + break; + obstack_grow (obs, str, i); + str = ntoa ((eval_t) getpid (), 10); + len2 = strlen (str); + if (len2 > len - i) + obstack_grow0 (obs, str + len2 - (len - i), len - i); + else + { + while (i++ < len - len2) + obstack_1grow (obs, '0'); + obstack_grow0 (obs, str, len2); + } + } + else + mkstemp_helper (obs, ARG (1)); +} + +static void +m4_mkstemp (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 2, 2)) + return; + mkstemp_helper (obs, ARG (1)); +} + +/*----------------------------------------. +| Print all arguments on standard error. | +`----------------------------------------*/ + +static void +m4_errprint (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 2, -1)) + return; + dump_args (obs, argc, argv, " ", false); + obstack_1grow (obs, '\0'); + debug_flush_files (); + fprintf (stderr, "%s", (char *) obstack_finish (obs)); + fflush (stderr); +} + +static void +m4___file__ (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 1, 1)) + return; + obstack_grow (obs, lquote.string, lquote.length); + obstack_grow (obs, current_file, strlen (current_file)); + obstack_grow (obs, rquote.string, rquote.length); +} + +static void +m4___line__ (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 1, 1)) + return; + shipout_int (obs, current_line); +} + +static void +m4___program__ (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 1, 1)) + return; + obstack_grow (obs, lquote.string, lquote.length); + obstack_grow (obs, program_name, strlen (program_name)); + obstack_grow (obs, rquote.string, rquote.length); +} + +/* This section contains various macros for exiting, saving input until + EOF is seen, and tracing macro calls. That is: "m4exit", "m4wrap", + "traceon" and "traceoff". */ + +/*-------------------------------------------------------------------------. +| Exit immediately, with exitcode specified by the first argument, 0 if no | +| arguments are present. | +`-------------------------------------------------------------------------*/ + +static void +m4_m4exit (struct obstack *obs, int argc, token_data **argv) +{ + int exit_code = EXIT_SUCCESS; + + /* Warn on bad arguments, but still exit. */ + bad_argc (argv[0], argc, 1, 2); + if (argc >= 2 && !numeric_arg (argv[0], ARG (1), &exit_code)) + exit_code = EXIT_FAILURE; + if (exit_code < 0 || exit_code > 255) + { + M4ERROR ((warning_status, 0, + "exit status out of range: `%d'", exit_code)); + exit_code = EXIT_FAILURE; + } + /* Change debug stream back to stderr, to force flushing debug stream and + detect any errors it might have encountered. */ + debug_set_output (NULL); + debug_flush_files (); + if (exit_code == EXIT_SUCCESS && retcode != EXIT_SUCCESS) + exit_code = retcode; + /* Propagate non-zero status to atexit handlers. */ + if (exit_code != EXIT_SUCCESS) + exit_failure = exit_code; + exit (exit_code); +} + +/*-------------------------------------------------------------------------. +| Save the argument text until EOF has been seen, allowing for user | +| specified cleanup action. GNU version saves all arguments, the standard | +| version only the first. | +`-------------------------------------------------------------------------*/ + +static void +m4_m4wrap (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 2, -1)) + return; + if (no_gnu_extensions) + obstack_grow (obs, ARG (1), strlen (ARG (1))); + else + dump_args (obs, argc, argv, " ", false); + obstack_1grow (obs, '\0'); + push_wrapup ((char *) obstack_finish (obs)); +} + +/* Enable tracing of all specified macros, or all, if none is specified. + Tracing is disabled by default, when a macro is defined. This can be + overridden by the "t" debug flag. */ + +/*-----------------------------------------------------------------------. +| Set_trace () is used by "traceon" and "traceoff" to enable and disable | +| tracing of a macro. It disables tracing if DATA is NULL, otherwise it | +| enable tracing. | +`-----------------------------------------------------------------------*/ + +static void +set_trace (symbol *sym, void *data) +{ + SYMBOL_TRACED (sym) = data != NULL; + /* Remove placeholder from table if macro is undefined and untraced. */ + if (SYMBOL_TYPE (sym) == TOKEN_VOID && data == NULL) + lookup_symbol (SYMBOL_NAME (sym), SYMBOL_POPDEF); +} + +static void +m4_traceon (struct obstack *obs, int argc, token_data **argv) +{ + symbol *s; + int i; + + if (argc == 1) + hack_all_symbols (set_trace, obs); + else + for (i = 1; i < argc; i++) + { + s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_INSERT); + set_trace (s, obs); + } +} + +/*------------------------------------------------------------------------. +| Disable tracing of all specified macros, or all, if none is specified. | +`------------------------------------------------------------------------*/ + +static void +m4_traceoff (struct obstack *obs, int argc, token_data **argv) +{ + symbol *s; + int i; + + if (argc == 1) + hack_all_symbols (set_trace, NULL); + else + for (i = 1; i < argc; i++) + { + s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_LOOKUP); + if (s != NULL) + set_trace (s, NULL); + } +} + +/*----------------------------------------------------------------------. +| On-the-fly control of the format of the tracing output. It takes one | +| argument, which is a character string like given to the -d option, or | +| none in which case the debug_level is zeroed. | +`----------------------------------------------------------------------*/ + +static void +m4_debugmode (struct obstack *obs, int argc, token_data **argv) +{ + int new_debug_level; + int change_flag; + + if (bad_argc (argv[0], argc, 1, 2)) + return; + + if (argc == 1) + debug_level = 0; + else + { + if (ARG (1)[0] == '+' || ARG (1)[0] == '-') + { + change_flag = ARG (1)[0]; + new_debug_level = debug_decode (ARG (1) + 1); + } + else + { + change_flag = 0; + new_debug_level = debug_decode (ARG (1)); + } + + if (new_debug_level < 0) + M4ERROR ((warning_status, 0, + "Debugmode: bad debug flags: `%s'", ARG (1))); + else + { + switch (change_flag) + { + case 0: + debug_level = new_debug_level; + break; + + case '+': + debug_level |= new_debug_level; + break; + + case '-': + debug_level &= ~new_debug_level; + break; + } + } + } +} + +/*-------------------------------------------------------------------------. +| Specify the destination of the debugging output. With one argument, the | +| argument is taken as a file name, with no arguments, revert to stderr. | +`-------------------------------------------------------------------------*/ + +static void +m4_debugfile (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 1, 2)) + return; + + if (argc == 1) + debug_set_output (NULL); + else if (!debug_set_output (ARG (1))) + M4ERROR ((warning_status, errno, + "cannot set error file: `%s'", ARG (1))); +} + +/* This section contains text processing macros: "len", "index", + "substr", "translit", "format", "regexp" and "patsubst". The last + three are GNU specific. */ + +/*---------------------------------------------. +| Expand to the length of the first argument. | +`---------------------------------------------*/ + +static void +m4_len (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 2, 2)) + return; + shipout_int (obs, strlen (ARG (1))); +} + +/*-------------------------------------------------------------------------. +| The macro expands to the first index of the second argument in the first | +| argument. | +`-------------------------------------------------------------------------*/ + +static void +m4_index (struct obstack *obs, int argc, token_data **argv) +{ + const char *haystack; + const char *result; + int retval; + + if (bad_argc (argv[0], argc, 3, 3)) + { + /* builtin(`index') is blank, but index(`abc') is 0. */ + if (argc == 2) + shipout_int (obs, 0); + return; + } + + haystack = ARG (1); + result = strstr (haystack, ARG (2)); + retval = result ? result - haystack : -1; + + shipout_int (obs, retval); +} + +/*-------------------------------------------------------------------------. +| The macro "substr" extracts substrings from the first argument, starting | +| from the index given by the second argument, extending for a length | +| given by the third argument. If the third argument is missing, the | +| substring extends to the end of the first argument. | +`-------------------------------------------------------------------------*/ + +static void +m4_substr (struct obstack *obs, int argc, token_data **argv) +{ + int start = 0; + int length, avail; + + if (bad_argc (argv[0], argc, 3, 4)) + { + /* builtin(`substr') is blank, but substr(`abc') is abc. */ + if (argc == 2) + obstack_grow (obs, ARG (1), strlen (ARG (1))); + return; + } + + length = avail = strlen (ARG (1)); + if (!numeric_arg (argv[0], ARG (2), &start)) + return; + + if (argc >= 4 && !numeric_arg (argv[0], ARG (3), &length)) + return; + + if (start < 0 || length <= 0 || start >= avail) + return; + + if (start + length > avail) + length = avail - start; + obstack_grow (obs, ARG (1) + start, length); +} + +/*------------------------------------------------------------------------. +| For "translit", ranges are allowed in the second and third argument. | +| They are expanded in the following function, and the expanded strings, | +| without any ranges left, are used to translate the characters of the | +| first argument. A single - (dash) can be included in the strings by | +| being the first or the last character in the string. If the first | +| character in a range is after the first in the character set, the range | +| is made backwards, thus 9-0 is the string 9876543210. | +`------------------------------------------------------------------------*/ + +static const char * +expand_ranges (const char *s, struct obstack *obs) +{ + unsigned char from; + unsigned char to; + + for (from = '\0'; *s != '\0'; from = to_uchar (*s++)) + { + if (*s == '-' && from != '\0') + { + to = to_uchar (*++s); + if (to == '\0') + { + /* trailing dash */ + obstack_1grow (obs, '-'); + break; + } + else if (from <= to) + { + while (from++ < to) + obstack_1grow (obs, from); + } + else + { + while (--from >= to) + obstack_1grow (obs, from); + } + } + else + obstack_1grow (obs, *s); + } + obstack_1grow (obs, '\0'); + return (char *) obstack_finish (obs); +} + +/*----------------------------------------------------------------------. +| The macro "translit" translates all characters in the first argument, | +| which are present in the second argument, into the corresponding | +| character from the third argument. If the third argument is shorter | +| than the second, the extra characters in the second argument, are | +| deleted from the first (pueh). | +`----------------------------------------------------------------------*/ + +static void +m4_translit (struct obstack *obs, int argc, token_data **argv) +{ + const char *data; + const char *from; + const char *to; + char map[256] = {0}; + char found[256] = {0}; + unsigned char ch; + + if (bad_argc (argv[0], argc, 3, 4)) + { + /* builtin(`translit') is blank, but translit(`abc') is abc. */ + if (argc == 2) + obstack_grow (obs, ARG (1), strlen (ARG (1))); + return; + } + + from = ARG (2); + if (strchr (from, '-') != NULL) + { + from = expand_ranges (from, obs); + if (from == NULL) + return; + } + + to = ARG (3); + if (strchr (to, '-') != NULL) + { + to = expand_ranges (to, obs); + if (to == NULL) + return; + } + + /* Calling strchr(from) for each character in data is quadratic, + since both strings can be arbitrarily long. Instead, create a + from-to mapping in one pass of from, then use that map in one + pass of data, for linear behavior. Traditional behavior is that + only the first instance of a character in from is consulted, + hence the found map. */ + for ( ; (ch = *from) != '\0'; from++) + { + if (! found[ch]) + { + found[ch] = 1; + map[ch] = *to; + } + if (*to != '\0') + to++; + } + + for (data = ARG (1); (ch = *data) != '\0'; data++) + { + if (! found[ch]) + obstack_1grow (obs, ch); + else if (map[ch]) + obstack_1grow (obs, map[ch]); + } +} + +/*----------------------------------------------------------------------. +| Frontend for printf like formatting. The function format () lives in | +| the file format.c. | +`----------------------------------------------------------------------*/ + +static void +m4_format (struct obstack *obs, int argc, token_data **argv) +{ + if (bad_argc (argv[0], argc, 2, -1)) + return; + format (obs, argc - 1, argv + 1); +} + +/*-------------------------------------------------------------------------. +| Function to perform substitution by regular expressions. Used by the | +| builtins regexp and patsubst. The changed text is placed on the | +| obstack. The substitution is REPL, with \& substituted by this part of | +| VICTIM matched by the last whole regular expression, taken from REGS[0], | +| and \N substituted by the text matched by the Nth parenthesized | +| sub-expression, taken from REGS[N]. | +`-------------------------------------------------------------------------*/ + +static int substitute_warned = 0; + +static void +substitute (struct obstack *obs, const char *victim, const char *repl, + struct re_registers *regs) +{ + int ch; + + for (;;) + { + while ((ch = *repl++) != '\\') + { + if (ch == '\0') + return; + obstack_1grow (obs, ch); + } + + switch ((ch = *repl++)) + { + case '0': + if (!substitute_warned) + { + M4ERROR ((warning_status, 0, "\ +Warning: \\0 will disappear, use \\& instead in replacements")); + substitute_warned = 1; + } + /* Fall through. */ + + case '&': + obstack_grow (obs, victim + regs->start[0], + regs->end[0] - regs->start[0]); + break; + + case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': + ch -= '0'; + if (regs->num_regs - 1 <= ch) + M4ERROR ((warning_status, 0, "\ +Warning: sub-expression %d not present", ch)); + else if (regs->end[ch] > 0) + obstack_grow (obs, victim + regs->start[ch], + regs->end[ch] - regs->start[ch]); + break; + + case '\0': + M4ERROR ((warning_status, 0, "\ +Warning: trailing \\ ignored in replacement")); + return; + + default: + obstack_1grow (obs, ch); + break; + } + } +} + +/*------------------------------------------. +| Initialize regular expression variables. | +`------------------------------------------*/ + +static void +init_pattern_buffer (struct re_pattern_buffer *buf, struct re_registers *regs) +{ + buf->translate = NULL; + buf->fastmap = NULL; + buf->buffer = NULL; + buf->allocated = 0; + regs->start = NULL; + regs->end = NULL; +} + +/*----------------------------------------. +| Clean up regular expression variables. | +`----------------------------------------*/ + +static void +free_pattern_buffer (struct re_pattern_buffer *buf, struct re_registers *regs) +{ + regfree (buf); + free (regs->start); + free (regs->end); +} + +/*--------------------------------------------------------------------------. +| Regular expression version of index. Given two arguments, expand to the | +| index of the first match of the second argument (a regexp) in the first. | +| Expand to -1 if here is no match. Given a third argument, is changes | +| the expansion to this argument. | +`--------------------------------------------------------------------------*/ + +static void +m4_regexp (struct obstack *obs, int argc, token_data **argv) +{ + const char *victim; /* first argument */ + const char *regexp; /* regular expression */ + const char *repl; /* replacement string */ + + struct re_pattern_buffer buf; /* compiled regular expression */ + struct re_registers regs; /* for subexpression matches */ + const char *msg; /* error message from re_compile_pattern */ + int startpos; /* start position of match */ + int length; /* length of first argument */ + + if (bad_argc (argv[0], argc, 3, 4)) + { + /* builtin(`regexp') is blank, but regexp(`abc') is 0. */ + if (argc == 2) + shipout_int (obs, 0); + return; + } + + victim = TOKEN_DATA_TEXT (argv[1]); + regexp = TOKEN_DATA_TEXT (argv[2]); + + init_pattern_buffer (&buf, ®s); + msg = re_compile_pattern (regexp, strlen (regexp), &buf); + + if (msg != NULL) + { + M4ERROR ((warning_status, 0, + "bad regular expression: `%s': %s", regexp, msg)); + free_pattern_buffer (&buf, ®s); + return; + } + + length = strlen (victim); + /* Avoid overhead of allocating regs if we won't use it. */ + startpos = re_search (&buf, victim, length, 0, length, + argc == 3 ? NULL : ®s); + + if (startpos == -2) + M4ERROR ((warning_status, 0, + "error matching regular expression `%s'", regexp)); + else if (argc == 3) + shipout_int (obs, startpos); + else if (startpos >= 0) + { + repl = TOKEN_DATA_TEXT (argv[3]); + substitute (obs, victim, repl, ®s); + } + + free_pattern_buffer (&buf, ®s); +} + +/*--------------------------------------------------------------------------. +| Substitute all matches of a regexp occuring in a string. Each match of | +| the second argument (a regexp) in the first argument is changed to the | +| third argument, with \& substituted by the matched text, and \N | +| substituted by the text matched by the Nth parenthesized sub-expression. | +`--------------------------------------------------------------------------*/ + +static void +m4_patsubst (struct obstack *obs, int argc, token_data **argv) +{ + const char *victim; /* first argument */ + const char *regexp; /* regular expression */ + + struct re_pattern_buffer buf; /* compiled regular expression */ + struct re_registers regs; /* for subexpression matches */ + const char *msg; /* error message from re_compile_pattern */ + int matchpos; /* start position of match */ + int offset; /* current match offset */ + int length; /* length of first argument */ + + if (bad_argc (argv[0], argc, 3, 4)) + { + /* builtin(`patsubst') is blank, but patsubst(`abc') is abc. */ + if (argc == 2) + obstack_grow (obs, ARG (1), strlen (ARG (1))); + return; + } + + regexp = TOKEN_DATA_TEXT (argv[2]); + + init_pattern_buffer (&buf, ®s); + msg = re_compile_pattern (regexp, strlen (regexp), &buf); + + if (msg != NULL) + { + M4ERROR ((warning_status, 0, + "bad regular expression `%s': %s", regexp, msg)); + free (buf.buffer); + return; + } + + victim = TOKEN_DATA_TEXT (argv[1]); + length = strlen (victim); + + offset = 0; + matchpos = 0; + while (offset <= length) + { + matchpos = re_search (&buf, victim, length, + offset, length - offset, ®s); + if (matchpos < 0) + { + + /* Match failed -- either error or there is no match in the + rest of the string, in which case the rest of the string is + copied verbatim. */ + + if (matchpos == -2) + M4ERROR ((warning_status, 0, + "error matching regular expression `%s'", regexp)); + else if (offset < length) + obstack_grow (obs, victim + offset, length - offset); + break; + } + + /* Copy the part of the string that was skipped by re_search (). */ + + if (matchpos > offset) + obstack_grow (obs, victim + offset, matchpos - offset); + + /* Handle the part of the string that was covered by the match. */ + + substitute (obs, victim, ARG (3), ®s); + + /* Update the offset to the end of the match. If the regexp + matched a null string, advance offset one more, to avoid + infinite loops. */ + + offset = regs.end[0]; + if (regs.start[0] == regs.end[0]) + obstack_1grow (obs, victim[offset++]); + } + obstack_1grow (obs, '\0'); + + free_pattern_buffer (&buf, ®s); +} + +/* Finally, a placeholder builtin. This builtin is not installed by + default, but when reading back frozen files, this is associated + with any builtin we don't recognize (for example, if the frozen + file was created with a changeword capable m4, but is then loaded + by a different m4 that does not support changeword). This way, we + can keep 'm4 -R' quiet in the common case that the user did not + know or care about the builtin when the frozen file was created, + while still flagging it as a potential error if an attempt is made + to actually use the builtin. */ + +/*--------------------------------------------------------------------. +| Issue a warning that this macro is a placeholder for an unsupported | +| builtin that was requested while reloading a frozen file. | +`--------------------------------------------------------------------*/ + +void +m4_placeholder (struct obstack *obs, int argc, token_data **argv) +{ + M4ERROR ((warning_status, 0, "\ +builtin `%s' requested by frozen file is not supported", ARG (0))); +} + +/*-------------------------------------------------------------------------. +| This function handles all expansion of user defined and predefined | +| macros. It is called with an obstack OBS, where the macros expansion | +| will be placed, as an unfinished object. SYM points to the macro | +| definition, giving the expansion text. ARGC and ARGV are the arguments, | +| as usual. | +`-------------------------------------------------------------------------*/ + +void +expand_user_macro (struct obstack *obs, symbol *sym, + int argc, token_data **argv) +{ + const char *text; + int i; + + for (text = SYMBOL_TEXT (sym); *text != '\0';) + { + if (*text != '$') + { + obstack_1grow (obs, *text); + text++; + continue; + } + text++; + switch (*text) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (no_gnu_extensions) + { + i = *text++ - '0'; + } + else + { + for (i = 0; isdigit (to_uchar (*text)); text++) + i = i*10 + (*text - '0'); + } + if (i < argc) + obstack_grow (obs, TOKEN_DATA_TEXT (argv[i]), + strlen (TOKEN_DATA_TEXT (argv[i]))); + break; + + case '#': /* number of arguments */ + shipout_int (obs, argc - 1); + text++; + break; + + case '*': /* all arguments */ + case '@': /* ... same, but quoted */ + dump_args (obs, argc, argv, ",", *text == '@'); + text++; + break; + + default: + obstack_1grow (obs, '$'); + break; + } + } +} diff --git a/src/bin/m4/src/debug.c b/src/bin/m4/src/debug.c new file mode 100644 index 0000000000..44354d0249 --- /dev/null +++ b/src/bin/m4/src/debug.c @@ -0,0 +1,448 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1991, 1992, 1993, 1994, 2004, 2006 Free Software + Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "m4.h" + +#include +#include + +/* File for debugging output. */ +FILE *debug = NULL; + +/* Obstack for trace messages. */ +static struct obstack trace; + +extern int expansion_level; + +static void debug_set_file (FILE *); + +/*----------------------------------. +| Initialise the debugging module. | +`----------------------------------*/ + +void +debug_init (void) +{ + debug_set_file (stderr); + obstack_init (&trace); +} + +/*-----------------------------------------------------------------. +| Function to decode the debugging flags OPTS. Used by main while | +| processing option -d, and by the builtin debugmode (). | +`-----------------------------------------------------------------*/ + +int +debug_decode (const char *opts) +{ + int level; + + if (opts == NULL || *opts == '\0') + level = DEBUG_TRACE_DEFAULT; + else + { + for (level = 0; *opts; opts++) + { + switch (*opts) + { + case 'a': + level |= DEBUG_TRACE_ARGS; + break; + + case 'e': + level |= DEBUG_TRACE_EXPANSION; + break; + + case 'q': + level |= DEBUG_TRACE_QUOTE; + break; + + case 't': + level |= DEBUG_TRACE_ALL; + break; + + case 'l': + level |= DEBUG_TRACE_LINE; + break; + + case 'f': + level |= DEBUG_TRACE_FILE; + break; + + case 'p': + level |= DEBUG_TRACE_PATH; + break; + + case 'c': + level |= DEBUG_TRACE_CALL; + break; + + case 'i': + level |= DEBUG_TRACE_INPUT; + break; + + case 'x': + level |= DEBUG_TRACE_CALLID; + break; + + case 'V': + level |= DEBUG_TRACE_VERBOSE; + break; + + default: + return -1; + } + } + } + + /* This is to avoid screwing up the trace output due to changes in the + debug_level. */ + + obstack_free (&trace, obstack_finish (&trace)); + + return level; +} + +/*------------------------------------------------------------------------. +| Change the debug output stream to FP. If the underlying file is the | +| same as stdout, use stdout instead so that debug messages appear in the | +| correct relative position. | +`------------------------------------------------------------------------*/ + +static void +debug_set_file (FILE *fp) +{ + struct stat stdout_stat, debug_stat; + + if (debug != NULL && debug != stderr && debug != stdout + && close_stream (debug) != 0) + { + M4ERROR ((warning_status, errno, "error writing to debug stream")); + retcode = EXIT_FAILURE; + } + debug = fp; + + if (debug != NULL && debug != stdout) + { + if (fstat (STDOUT_FILENO, &stdout_stat) < 0) + return; + if (fstat (fileno (debug), &debug_stat) < 0) + return; + + /* mingw has a bug where fstat on a regular file reports st_ino + of 0. On normal system, st_ino should never be 0. */ + if (stdout_stat.st_ino == debug_stat.st_ino + && stdout_stat.st_dev == debug_stat.st_dev + && stdout_stat.st_ino != 0) + { + if (debug != stderr && close_stream (debug) != 0) + { + M4ERROR ((warning_status, errno, + "error writing to debug stream")); + retcode = EXIT_FAILURE; + } + debug = stdout; + } + } +} + +/*-----------------------------------------------------------. +| Serialize files. Used before executing a system command. | +`-----------------------------------------------------------*/ + +void +debug_flush_files (void) +{ + fflush (stdout); + fflush (stderr); + if (debug != NULL && debug != stdout && debug != stderr) + fflush (debug); + /* POSIX requires that if m4 doesn't consume all input, but stdin is + opened on a seekable file, that the file pointer be left at the + next character on exit (but places no restrictions on the file + pointer location on a non-seekable file). It also requires that + fflush() followed by fseek() on an input file set the underlying + file pointer. However, fflush() on a non-seekable file can lose + buffered data, which we might otherwise want to process after + syscmd. Hence, we must check whether stdin is seekable. We must + also be tolerant of operating with stdin closed, so we don't + report any failures in this attempt. The stdio-safer module and + friends are essential, so that if stdin was closed, this lseek is + not on some other file that we have since opened. Mingw has bugs + when using fseek on text files, so we only strive for POSIX + behavior when we detect a UNIX environment. */ +#if UNIX + if (lseek (STDIN_FILENO, 0, SEEK_CUR) >= 0 + && fflush (stdin) == 0) + { + fseek (stdin, 0, SEEK_CUR); + } +#endif /* UNIX */ +} + +/*-------------------------------------------------------------------------. +| Change the debug output to file NAME. If NAME is NULL, debug output is | +| reverted to stderr, and if empty debug output is discarded. Return true | +| iff the output stream was changed. | +`-------------------------------------------------------------------------*/ + +bool +debug_set_output (const char *name) +{ + FILE *fp; + + if (name == NULL) + debug_set_file (stderr); + else if (*name == '\0') + debug_set_file (NULL); + else + { + fp = fopen (name, "a"); + if (fp == NULL) + return false; + + if (set_cloexec_flag (fileno (fp), true) != 0) + M4ERROR ((warning_status, errno, + "Warning: cannot protect debug file across forks")); + debug_set_file (fp); + } + return true; +} + +/*-----------------------------------------------------------------------. +| Print the header of a one-line debug message, starting by "m4 debug". | +`-----------------------------------------------------------------------*/ + +void +debug_message_prefix (void) +{ + fprintf (debug, "m4debug:"); + if (current_line) + { + if (debug_level & DEBUG_TRACE_FILE) + fprintf (debug, "%s:", current_file); + if (debug_level & DEBUG_TRACE_LINE) + fprintf (debug, "%d:", current_line); + } + putc (' ', debug); +} + +/* The rest of this file contains the functions for macro tracing output. + All tracing output for a macro call is collected on an obstack TRACE, + and printed whenever the line is complete. This prevents tracing + output from interfering with other debug messages generated by the + various builtins. */ + +/*---------------------------------------------------------------------. +| Tracing output is formatted here, by a simplified printf-to-obstack | +| function trace_format (). Understands only %S, %s, %d, %l (optional | +| left quote) and %r (optional right quote). | +`---------------------------------------------------------------------*/ + +static void +trace_format (const char *fmt, ...) +{ + va_list args; + char ch; + + int d; + char nbuf[32]; + const char *s; + int slen; + int maxlen; + + va_start (args, fmt); + + while (true) + { + while ((ch = *fmt++) != '\0' && ch != '%') + obstack_1grow (&trace, ch); + + if (ch == '\0') + break; + + maxlen = 0; + switch (*fmt++) + { + case 'S': + maxlen = max_debug_argument_length; + /* fall through */ + + case 's': + s = va_arg (args, const char *); + break; + + case 'l': + s = (debug_level & DEBUG_TRACE_QUOTE) ? lquote.string : ""; + break; + + case 'r': + s = (debug_level & DEBUG_TRACE_QUOTE) ? rquote.string : ""; + break; + + case 'd': + d = va_arg (args, int); + sprintf (nbuf, "%d", d); + s = nbuf; + break; + + default: + s = ""; + break; + } + + slen = strlen (s); + if (maxlen == 0 || maxlen > slen) + obstack_grow (&trace, s, slen); + else + { + obstack_grow (&trace, s, maxlen); + obstack_grow (&trace, "...", 3); + } + } + + va_end (args); +} + +/*------------------------------------------------------------------. +| Format the standard header attached to all tracing output lines. | +`------------------------------------------------------------------*/ + +static void +trace_header (int id) +{ + trace_format ("m4trace:"); + if (current_line) + { + if (debug_level & DEBUG_TRACE_FILE) + trace_format ("%s:", current_file); + if (debug_level & DEBUG_TRACE_LINE) + trace_format ("%d:", current_line); + } + trace_format (" -%d- ", expansion_level); + if (debug_level & DEBUG_TRACE_CALLID) + trace_format ("id %d: ", id); +} + +/*----------------------------------------------------. +| Print current tracing line, and clear the obstack. | +`----------------------------------------------------*/ + +static void +trace_flush (void) +{ + char *line; + + obstack_1grow (&trace, '\0'); + line = (char *) obstack_finish (&trace); + DEBUG_PRINT1 ("%s\n", line); + obstack_free (&trace, line); +} + +/*-------------------------------------------------------------. +| Do pre-argument-collction tracing for macro NAME. Used from | +| expand_macro (). | +`-------------------------------------------------------------*/ + +void +trace_prepre (const char *name, int id) +{ + trace_header (id); + trace_format ("%s ...", name); + trace_flush (); +} + +/*-----------------------------------------------------------------------. +| Format the parts of a trace line, that can be made before the macro is | +| actually expanded. Used from expand_macro (). | +`-----------------------------------------------------------------------*/ + +void +trace_pre (const char *name, int id, int argc, token_data **argv) +{ + int i; + const builtin *bp; + + trace_header (id); + trace_format ("%s", name); + + if (argc > 1 && (debug_level & DEBUG_TRACE_ARGS)) + { + trace_format ("("); + + for (i = 1; i < argc; i++) + { + if (i != 1) + trace_format (", "); + + switch (TOKEN_DATA_TYPE (argv[i])) + { + case TOKEN_TEXT: + trace_format ("%l%S%r", TOKEN_DATA_TEXT (argv[i])); + break; + + case TOKEN_FUNC: + bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[i])); + if (bp == NULL) + { + M4ERROR ((warning_status, 0, "\ +INTERNAL ERROR: builtin not found in builtin table! (trace_pre ())")); + abort (); + } + trace_format ("<%s>", bp->name); + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad token data type (trace_pre ())")); + abort (); + } + + } + trace_format (")"); + } + + if (debug_level & DEBUG_TRACE_CALL) + { + trace_format (" -> ???"); + trace_flush (); + } +} + +/*-------------------------------------------------------------------. +| Format the final part of a trace line and print it all. Used from | +| expand_macro (). | +`-------------------------------------------------------------------*/ + +void +trace_post (const char *name, int id, int argc, token_data **argv, + const char *expanded) +{ + if (debug_level & DEBUG_TRACE_CALL) + { + trace_header (id); + trace_format ("%s%s", name, (argc > 1) ? "(...)" : ""); + } + + if (expanded && (debug_level & DEBUG_TRACE_EXPANSION)) + trace_format (" -> %l%S%r", expanded); + trace_flush (); +} diff --git a/src/bin/m4/src/eval.c b/src/bin/m4/src/eval.c new file mode 100644 index 0000000000..c2a55cbf7c --- /dev/null +++ b/src/bin/m4/src/eval.c @@ -0,0 +1,799 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006 + Free Software Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* This file contains the functions to evaluate integer expressions for + the "eval" macro. It is a little, fairly self-contained module, with + its own scanner, and a recursive descent parser. The only entry point + is evaluate (). For POSIX semantics of the "eval" macro, the type + eval_t must be a 32-bit signed integer. */ + +#include "m4.h" + +/* Evaluates token types. */ + +typedef enum eval_token + { + ERROR, + PLUS, MINUS, + EXPONENT, + TIMES, DIVIDE, MODULO, + EQ, NOTEQ, GT, GTEQ, LS, LSEQ, + LSHIFT, RSHIFT, + LNOT, LAND, LOR, + NOT, AND, OR, XOR, + LEFTP, RIGHTP, + NUMBER, EOTEXT + } +eval_token; + +/* Error types. */ + +typedef enum eval_error + { + NO_ERROR, + MISSING_RIGHT, + SYNTAX_ERROR, + UNKNOWN_INPUT, + EXCESS_INPUT, + DIVIDE_ZERO, + MODULO_ZERO + } +eval_error; + +static eval_error logical_or_term (eval_token, eval_t *); +static eval_error logical_and_term (eval_token, eval_t *); +static eval_error or_term (eval_token, eval_t *); +static eval_error xor_term (eval_token, eval_t *); +static eval_error and_term (eval_token, eval_t *); +static eval_error not_term (eval_token, eval_t *); +static eval_error logical_not_term (eval_token, eval_t *); +static eval_error cmp_term (eval_token, eval_t *); +static eval_error shift_term (eval_token, eval_t *); +static eval_error add_term (eval_token, eval_t *); +static eval_error mult_term (eval_token, eval_t *); +static eval_error exp_term (eval_token, eval_t *); +static eval_error unary_term (eval_token, eval_t *); +static eval_error simple_term (eval_token, eval_t *); + +/*--------------------. +| Lexical functions. | +`--------------------*/ + +/* Pointer to next character of input text. */ +static const char *eval_text; + +/* Value of eval_text, from before last call of eval_lex (). This is so we + can back up, if we have read too much. */ +static const char *last_text; + +static void +eval_init_lex (const char *text) +{ + eval_text = text; + last_text = NULL; +} + +static void +eval_undo (void) +{ + eval_text = last_text; +} + +/* VAL is numerical value, if any. */ + +static eval_token +eval_lex (eval_t *val) +{ + while (isspace (to_uchar (*eval_text))) + eval_text++; + + last_text = eval_text; + + if (*eval_text == '\0') + return EOTEXT; + + if (isdigit (to_uchar (*eval_text))) + { + int base, digit; + + if (*eval_text == '0') + { + eval_text++; + switch (*eval_text) + { + case 'x': + case 'X': + base = 16; + eval_text++; + break; + + case 'b': + case 'B': + base = 2; + eval_text++; + break; + + case 'r': + case 'R': + base = 0; + eval_text++; + while (isdigit (to_uchar (*eval_text)) && base <= 36) + base = 10 * base + *eval_text++ - '0'; + if (base == 0 || base > 36 || *eval_text != ':') + return ERROR; + eval_text++; + break; + + default: + base = 8; + } + } + else + base = 10; + + (*val) = 0; + for (; *eval_text; eval_text++) + { + if (isdigit (to_uchar (*eval_text))) + digit = *eval_text - '0'; + else if (islower (to_uchar (*eval_text))) + digit = *eval_text - 'a' + 10; + else if (isupper (to_uchar (*eval_text))) + digit = *eval_text - 'A' + 10; + else + break; + + if (base == 1) + { + if (digit == 1) + (*val)++; + else if (digit == 0 && !*val) + continue; + else + break; + } + else if (digit >= base) + break; + else + (*val) = (*val) * base + digit; + } + return NUMBER; + } + + switch (*eval_text++) + { + case '+': + return PLUS; + case '-': + return MINUS; + case '*': + if (*eval_text == '*') + { + eval_text++; + return EXPONENT; + } + else + return TIMES; + case '/': + return DIVIDE; + case '%': + return MODULO; + case '=': + if (*eval_text == '=') + eval_text++; + return EQ; + case '!': + if (*eval_text == '=') + { + eval_text++; + return NOTEQ; + } + else + return LNOT; + case '>': + if (*eval_text == '=') + { + eval_text++; + return GTEQ; + } + else if (*eval_text == '>') + { + eval_text++; + return RSHIFT; + } + else + return GT; + case '<': + if (*eval_text == '=') + { + eval_text++; + return LSEQ; + } + else if (*eval_text == '<') + { + eval_text++; + return LSHIFT; + } + else + return LS; + case '^': + return XOR; + case '~': + return NOT; + case '&': + if (*eval_text == '&') + { + eval_text++; + return LAND; + } + else + return AND; + case '|': + if (*eval_text == '|') + { + eval_text++; + return LOR; + } + else + return OR; + case '(': + return LEFTP; + case ')': + return RIGHTP; + default: + return ERROR; + } +} + +/*---------------------------------------. +| Main entry point, called from "eval". | +`---------------------------------------*/ + +bool +evaluate (const char *expr, eval_t *val) +{ + eval_token et; + eval_error err; + + eval_init_lex (expr); + et = eval_lex (val); + err = logical_or_term (et, val); + + if (err == NO_ERROR && *eval_text != '\0') + err = EXCESS_INPUT; + + switch (err) + { + case NO_ERROR: + break; + + case MISSING_RIGHT: + M4ERROR ((warning_status, 0, + "bad expression in eval (missing right parenthesis): %s", + expr)); + break; + + case SYNTAX_ERROR: + M4ERROR ((warning_status, 0, + "bad expression in eval: %s", expr)); + break; + + case UNKNOWN_INPUT: + M4ERROR ((warning_status, 0, + "bad expression in eval (bad input): %s", expr)); + break; + + case EXCESS_INPUT: + M4ERROR ((warning_status, 0, + "bad expression in eval (excess input): %s", expr)); + break; + + case DIVIDE_ZERO: + M4ERROR ((warning_status, 0, + "divide by zero in eval: %s", expr)); + break; + + case MODULO_ZERO: + M4ERROR ((warning_status, 0, + "modulo by zero in eval: %s", expr)); + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad error code in evaluate ()")); + abort (); + } + + return err != NO_ERROR; +} + +/*---------------------------. +| Recursive descent parser. | +`---------------------------*/ + +static eval_error +logical_or_term (eval_token et, eval_t *v1) +{ + eval_t v2; + eval_error er; + + if ((er = logical_and_term (et, v1)) != NO_ERROR) + return er; + + while ((et = eval_lex (&v2)) == LOR) + { + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = logical_and_term (et, &v2)) != NO_ERROR) + return er; + + *v1 = *v1 || v2; + } + if (et == ERROR) + return UNKNOWN_INPUT; + + eval_undo (); + return NO_ERROR; +} + +static eval_error +logical_and_term (eval_token et, eval_t *v1) +{ + eval_t v2; + eval_error er; + + if ((er = or_term (et, v1)) != NO_ERROR) + return er; + + while ((et = eval_lex (&v2)) == LAND) + { + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = or_term (et, &v2)) != NO_ERROR) + return er; + + *v1 = *v1 && v2; + } + if (et == ERROR) + return UNKNOWN_INPUT; + + eval_undo (); + return NO_ERROR; +} + +static eval_error +or_term (eval_token et, eval_t *v1) +{ + eval_t v2; + eval_error er; + + if ((er = xor_term (et, v1)) != NO_ERROR) + return er; + + while ((et = eval_lex (&v2)) == OR) + { + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = xor_term (et, &v2)) != NO_ERROR) + return er; + + *v1 = *v1 | v2; + } + if (et == ERROR) + return UNKNOWN_INPUT; + + eval_undo (); + return NO_ERROR; +} + +static eval_error +xor_term (eval_token et, eval_t *v1) +{ + eval_t v2; + eval_error er; + + if ((er = and_term (et, v1)) != NO_ERROR) + return er; + + while ((et = eval_lex (&v2)) == XOR) + { + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = and_term (et, &v2)) != NO_ERROR) + return er; + + *v1 = *v1 ^ v2; + } + if (et == ERROR) + return UNKNOWN_INPUT; + + eval_undo (); + return NO_ERROR; +} + +static eval_error +and_term (eval_token et, eval_t *v1) +{ + eval_t v2; + eval_error er; + + if ((er = not_term (et, v1)) != NO_ERROR) + return er; + + while ((et = eval_lex (&v2)) == AND) + { + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = not_term (et, &v2)) != NO_ERROR) + return er; + + *v1 = *v1 & v2; + } + if (et == ERROR) + return UNKNOWN_INPUT; + + eval_undo (); + return NO_ERROR; +} + +static eval_error +not_term (eval_token et, eval_t *v1) +{ + eval_error er; + + if (et == NOT) + { + et = eval_lex (v1); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = not_term (et, v1)) != NO_ERROR) + return er; + *v1 = ~*v1; + } + else + if ((er = logical_not_term (et, v1)) != NO_ERROR) + return er; + + return NO_ERROR; +} + +static eval_error +logical_not_term (eval_token et, eval_t *v1) +{ + eval_error er; + + if (et == LNOT) + { + et = eval_lex (v1); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = logical_not_term (et, v1)) != NO_ERROR) + return er; + *v1 = !*v1; + } + else + if ((er = cmp_term (et, v1)) != NO_ERROR) + return er; + + return NO_ERROR; +} + +static eval_error +cmp_term (eval_token et, eval_t *v1) +{ + eval_token op; + eval_t v2; + eval_error er; + + if ((er = shift_term (et, v1)) != NO_ERROR) + return er; + + while ((op = eval_lex (&v2)) == EQ || op == NOTEQ + || op == GT || op == GTEQ + || op == LS || op == LSEQ) + { + + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = shift_term (et, &v2)) != NO_ERROR) + return er; + + switch (op) + { + case EQ: + *v1 = *v1 == v2; + break; + + case NOTEQ: + *v1 = *v1 != v2; + break; + + case GT: + *v1 = *v1 > v2; + break; + + case GTEQ: + *v1 = *v1 >= v2; + break; + + case LS: + *v1 = *v1 < v2; + break; + + case LSEQ: + *v1 = *v1 <= v2; + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad comparison operator in cmp_term ()")); + abort (); + } + } + if (op == ERROR) + return UNKNOWN_INPUT; + + eval_undo (); + return NO_ERROR; +} + +static eval_error +shift_term (eval_token et, eval_t *v1) +{ + eval_token op; + eval_t v2; + eval_error er; + + if ((er = add_term (et, v1)) != NO_ERROR) + return er; + + while ((op = eval_lex (&v2)) == LSHIFT || op == RSHIFT) + { + + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = add_term (et, &v2)) != NO_ERROR) + return er; + + /* Shifting by a negative number, or by greater than the width, is + undefined in C, but POSIX requires eval to operate on 32-bit signed + numbers. Explicitly mask the right argument to ensure defined + behavior. */ + switch (op) + { + case LSHIFT: + *v1 = *v1 << (v2 & 0x1f); + break; + + case RSHIFT: + /* This assumes 2's-complement with sign-extension, since shifting + a negative number right is implementation-defined in C. */ + *v1 = *v1 >> (v2 & 0x1f); + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad shift operator in shift_term ()")); + abort (); + } + } + if (op == ERROR) + return UNKNOWN_INPUT; + + eval_undo (); + return NO_ERROR; +} + +static eval_error +add_term (eval_token et, eval_t *v1) +{ + eval_token op; + eval_t v2; + eval_error er; + + if ((er = mult_term (et, v1)) != NO_ERROR) + return er; + + while ((op = eval_lex (&v2)) == PLUS || op == MINUS) + { + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = mult_term (et, &v2)) != NO_ERROR) + return er; + + if (op == PLUS) + *v1 = *v1 + v2; + else + *v1 = *v1 - v2; + } + if (op == ERROR) + return UNKNOWN_INPUT; + + eval_undo (); + return NO_ERROR; +} + +static eval_error +mult_term (eval_token et, eval_t *v1) +{ + eval_token op; + eval_t v2; + eval_error er; + + if ((er = exp_term (et, v1)) != NO_ERROR) + return er; + + while ((op = eval_lex (&v2)) == TIMES || op == DIVIDE || op == MODULO) + { + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = exp_term (et, &v2)) != NO_ERROR) + return er; + + switch (op) + { + case TIMES: + *v1 = *v1 * v2; + break; + + case DIVIDE: + if (v2 == 0) + return DIVIDE_ZERO; + else if (v2 == -1) + /* Avoid the x86 SIGFPE on INT_MIN / -1. */ + *v1 = -*v1; + else + *v1 = *v1 / v2; + break; + + case MODULO: + if (v2 == 0) + return MODULO_ZERO; + else if (v2 == -1) + /* Avoid the x86 SIGFPE on INT_MIN % -1. */ + *v1 = 0; + else + *v1 = *v1 % v2; + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad operator in mult_term ()")); + abort (); + } + } + if (op == ERROR) + return UNKNOWN_INPUT; + + eval_undo (); + return NO_ERROR; +} + +static eval_error +exp_term (eval_token et, eval_t *v1) +{ + register eval_t result; + eval_t v2; + eval_error er; + + if ((er = unary_term (et, v1)) != NO_ERROR) + return er; + result = *v1; + + while ((et = eval_lex (&v2)) == EXPONENT) + { + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = exp_term (et, &v2)) != NO_ERROR) + return er; + + result = 1; + while (v2-- > 0) + result *= *v1; + *v1 = result; + } + if (et == ERROR) + return UNKNOWN_INPUT; + + eval_undo (); + return NO_ERROR; +} + +static eval_error +unary_term (eval_token et, eval_t *v1) +{ + eval_token et2 = et; + eval_error er; + + if (et == PLUS || et == MINUS) + { + et2 = eval_lex (v1); + if (et2 == ERROR) + return UNKNOWN_INPUT; + + if ((er = simple_term (et2, v1)) != NO_ERROR) + return er; + + if (et == MINUS) + *v1 = -*v1; + } + else + if ((er = simple_term (et, v1)) != NO_ERROR) + return er; + + return NO_ERROR; +} + +static eval_error +simple_term (eval_token et, eval_t *v1) +{ + eval_t v2; + eval_error er; + + switch (et) + { + case LEFTP: + et = eval_lex (v1); + if (et == ERROR) + return UNKNOWN_INPUT; + + if ((er = logical_or_term (et, v1)) != NO_ERROR) + return er; + + et = eval_lex (&v2); + if (et == ERROR) + return UNKNOWN_INPUT; + + if (et != RIGHTP) + return MISSING_RIGHT; + + break; + + case NUMBER: + break; + + default: + return SYNTAX_ERROR; + } + return NO_ERROR; +} diff --git a/src/bin/m4/src/format.c b/src/bin/m4/src/format.c new file mode 100644 index 0000000000..2482041521 --- /dev/null +++ b/src/bin/m4/src/format.c @@ -0,0 +1,298 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006 + Free Software Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* printf like formatting for m4. */ + +#include "m4.h" +#include "xvasprintf.h" + +/* Simple varargs substitute. */ + +#define ARG_INT(argc, argv) \ + ((argc == 0) ? 0 : \ + (--argc, argv++, atoi (TOKEN_DATA_TEXT (argv[-1])))) + +#define ARG_UINT(argc, argv) \ + ((argc == 0) ? 0 : \ + (--argc, argv++, (unsigned int) atoi (TOKEN_DATA_TEXT (argv[-1])))) + +#define ARG_LONG(argc, argv) \ + ((argc == 0) ? 0 : \ + (--argc, argv++, atol (TOKEN_DATA_TEXT (argv[-1])))) + +#define ARG_ULONG(argc, argv) \ + ((argc == 0) ? 0 : \ + (--argc, argv++, (unsigned long) atol (TOKEN_DATA_TEXT (argv[-1])))) + +#define ARG_STR(argc, argv) \ + ((argc == 0) ? "" : \ + (--argc, argv++, TOKEN_DATA_TEXT (argv[-1]))) + +#define ARG_DOUBLE(argc, argv) \ + ((argc == 0) ? 0 : \ + (--argc, argv++, atof (TOKEN_DATA_TEXT (argv[-1])))) + + +/*------------------------------------------------------------------------. +| The main formatting function. Output is placed on the obstack OBS, the | +| first argument in ARGV is the formatting string, and the rest is | +| arguments for the string. | +`------------------------------------------------------------------------*/ + +void +format (struct obstack *obs, int argc, token_data **argv) +{ + char *fmt; /* format control string */ + const char *fstart; /* beginning of current format spec */ + int c; /* a simple character */ + + /* Flags. */ + char flags; /* 1 iff treating flags */ + + /* Precision specifiers. */ + int width; /* minimum field width */ + int prec; /* precision */ + char lflag; /* long flag */ + char hflag; /* short flag */ + + /* Buffer and stuff. */ + char *str; /* malloc'd buffer of formatted text */ + enum {INT, UINT, LONG, ULONG, DOUBLE, STR} datatype; + + fmt = (char *) ARG_STR (argc, argv); + for (;;) + { + while ((c = *fmt++) != '%') + { + if (c == 0) + return; + obstack_1grow (obs, c); + } + + fstart = fmt - 1; + + if (*fmt == '%') + { + obstack_1grow (obs, '%'); + fmt++; + continue; + } + + /* Parse flags. */ + flags = 1; + do + { + switch (*fmt) + { + case '-': /* left justification */ + case '+': /* mandatory sign */ + case ' ': /* space instead of positive sign */ + case '0': /* zero padding */ + case '#': /* alternate output */ + break; + + default: + flags = 0; + break; + } + } + while (flags && fmt++); + + /* Minimum field width. */ + width = -1; + if (*fmt == '*') + { + width = ARG_INT (argc, argv); + fmt++; + } + else if (isdigit (to_uchar (*fmt))) + { + do + { + fmt++; + } + while (isdigit (to_uchar (*fmt))); + } + + /* Maximum precision. */ + prec = -1; + if (*fmt == '.') + { + if (*(++fmt) == '*') + { + prec = ARG_INT (argc, argv); + ++fmt; + } + else if (isdigit (to_uchar (*fmt))) + { + do + { + fmt++; + } + while (isdigit (to_uchar (*fmt))); + } + } + + /* Length modifiers. */ + lflag = (*fmt == 'l'); + hflag = (*fmt == 'h'); + if (lflag || hflag) + fmt++; + + switch (*fmt++) + { + + case '\0': + return; + + case 'c': + datatype = INT; + break; + + case 's': + datatype = STR; + break; + + case 'd': + case 'i': + if (lflag) + { + datatype = LONG; + } + else + { + datatype = INT; + } + break; + + case 'o': + case 'x': + case 'X': + case 'u': + if (lflag) + { + datatype = ULONG; + } + else + { + datatype = UINT; + } + break; + + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + datatype = DOUBLE; + break; + + default: + continue; + } + + c = *fmt; + *fmt = '\0'; + + switch(datatype) + { + case INT: + if (width != -1 && prec != -1) + str = xasprintf (fstart, width, prec, ARG_INT(argc, argv)); + else if (width != -1) + str = xasprintf (fstart, width, ARG_INT(argc, argv)); + else if (prec != -1) + str = xasprintf (fstart, prec, ARG_INT(argc, argv)); + else + str = xasprintf (fstart, ARG_INT(argc, argv)); + break; + + case UINT: + if (width != -1 && prec != -1) + str = xasprintf (fstart, width, prec, ARG_UINT(argc, argv)); + else if (width != -1) + str = xasprintf (fstart, width, ARG_UINT(argc, argv)); + else if (prec != -1) + str = xasprintf (fstart, prec, ARG_UINT(argc, argv)); + else + str = xasprintf (fstart, ARG_UINT(argc, argv)); + break; + + case LONG: + if (width != -1 && prec != -1) + str = xasprintf (fstart, width, prec, ARG_LONG(argc, argv)); + else if (width != -1) + str = xasprintf (fstart, width, ARG_LONG(argc, argv)); + else if (prec != -1) + str = xasprintf (fstart, prec, ARG_LONG(argc, argv)); + else + str = xasprintf (fstart, ARG_LONG(argc, argv)); + break; + + case ULONG: + if (width != -1 && prec != -1) + str = xasprintf (fstart, width, prec, ARG_ULONG(argc, argv)); + else if (width != -1) + str = xasprintf (fstart, width, ARG_ULONG(argc, argv)); + else if (prec != -1) + str = xasprintf (fstart, prec, ARG_ULONG(argc, argv)); + else + str = xasprintf (fstart, ARG_ULONG(argc, argv)); + break; + + case DOUBLE: + if (width != -1 && prec != -1) + str = xasprintf (fstart, width, prec, ARG_DOUBLE(argc, argv)); + else if (width != -1) + str = xasprintf (fstart, width, ARG_DOUBLE(argc, argv)); + else if (prec != -1) + str = xasprintf (fstart, prec, ARG_DOUBLE(argc, argv)); + else + str = xasprintf (fstart, ARG_DOUBLE(argc, argv)); + break; + + case STR: + if (width != -1 && prec != -1) + str = xasprintf (fstart, width, prec, ARG_STR(argc, argv)); + else if (width != -1) + str = xasprintf (fstart, width, ARG_STR(argc, argv)); + else if (prec != -1) + str = xasprintf (fstart, prec, ARG_STR(argc, argv)); + else + str = xasprintf (fstart, ARG_STR(argc, argv)); + break; + + default: + abort(); + } + + *fmt = c; + + /* NULL was returned on failure, such as invalid format string. For + now, just silently ignore that bad specifier. */ + if (str == NULL) + continue; + + obstack_grow (obs, str, strlen (str)); + free (str); + } +} diff --git a/src/bin/m4/src/freeze.c b/src/bin/m4/src/freeze.c new file mode 100644 index 0000000000..7b8a7bc30c --- /dev/null +++ b/src/bin/m4/src/freeze.c @@ -0,0 +1,379 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006 + Free Software Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* This module handles frozen files. */ + +#include "m4.h" + +/*-------------------------------------------------------------------. +| Destructively reverse a symbol list and return the reversed list. | +`-------------------------------------------------------------------*/ + +static symbol * +reverse_symbol_list (symbol *sym) +{ + symbol *result; + symbol *next; + + result = NULL; + while (sym) + { + next = SYMBOL_NEXT (sym); + SYMBOL_NEXT (sym) = result; + result = sym; + sym = next; + } + return result; +} + +/*------------------------------------------------. +| Produce a frozen state to the given file NAME. | +`------------------------------------------------*/ + +void +produce_frozen_state (const char *name) +{ + FILE *file; + int h; + symbol *sym; + const builtin *bp; + + if (file = fopen (name, O_BINARY ? "wb" : "w"), !file) + { + M4ERROR ((warning_status, errno, name)); + return; + } + + /* Write a recognizable header. */ + + fprintf (file, "# This is a frozen state file generated by %s\n", + PACKAGE_STRING); + fprintf (file, "V1\n"); + + /* Dump quote delimiters. */ + + if (strcmp (lquote.string, DEF_LQUOTE) || strcmp (rquote.string, DEF_RQUOTE)) + { + fprintf (file, "Q%d,%d\n", (int) lquote.length, (int) rquote.length); + fputs (lquote.string, file); + fputs (rquote.string, file); + fputc ('\n', file); + } + + /* Dump comment delimiters. */ + + if (strcmp (bcomm.string, DEF_BCOMM) || strcmp (ecomm.string, DEF_ECOMM)) + { + fprintf (file, "C%d,%d\n", (int) bcomm.length, (int) ecomm.length); + fputs (bcomm.string, file); + fputs (ecomm.string, file); + fputc ('\n', file); + } + + /* Dump all symbols. */ + + for (h = 0; h < hash_table_size; h++) + { + + /* Process all entries in one bucket, from the last to the first. + This order ensures that, at reload time, pushdef's will be + executed with the oldest definitions first. */ + + symtab[h] = reverse_symbol_list (symtab[h]); + for (sym = symtab[h]; sym; sym = SYMBOL_NEXT (sym)) + { + switch (SYMBOL_TYPE (sym)) + { + case TOKEN_TEXT: + fprintf (file, "T%d,%d\n", + (int) strlen (SYMBOL_NAME (sym)), + (int) strlen (SYMBOL_TEXT (sym))); + fputs (SYMBOL_NAME (sym), file); + fputs (SYMBOL_TEXT (sym), file); + fputc ('\n', file); + break; + + case TOKEN_FUNC: + bp = find_builtin_by_addr (SYMBOL_FUNC (sym)); + if (bp == NULL) + { + M4ERROR ((warning_status, 0, "\ +INTERNAL ERROR: builtin not found in builtin table!")); + abort (); + } + fprintf (file, "F%d,%d\n", + (int) strlen (SYMBOL_NAME (sym)), + (int) strlen (bp->name)); + fputs (SYMBOL_NAME (sym), file); + fputs (bp->name, file); + fputc ('\n', file); + break; + + default: + M4ERROR ((warning_status, 0, "\ +INTERNAL ERROR: bad token data type in freeze_one_symbol ()")); + abort (); + break; + } + } + + /* Reverse the bucket once more, putting it back as it was. */ + + symtab[h] = reverse_symbol_list (symtab[h]); + } + + /* Let diversions be issued from output.c module, its cleaner to have this + piece of code there. */ + + freeze_diversions (file); + + /* All done. */ + + fputs ("# End of frozen state file\n", file); + if (close_stream (file) != 0) + M4ERROR ((EXIT_FAILURE, errno, "unable to create frozen state")); +} + +/*----------------------------------------------------------------------. +| Issue a message saying that some character is an EXPECTED character. | +`----------------------------------------------------------------------*/ + +static void +issue_expect_message (int expected) +{ + if (expected == '\n') + M4ERROR ((EXIT_FAILURE, 0, "expecting line feed in frozen file")); + else + M4ERROR ((EXIT_FAILURE, 0, "expecting character `%c' in frozen file", + expected)); +} + +/*-------------------------------------------------. +| Reload a frozen state from the given file NAME. | +`-------------------------------------------------*/ + +/* We are seeking speed, here. */ + +void +reload_frozen_state (const char *name) +{ + FILE *file; + int character; + int operation; + char *string[2]; + int allocated[2]; + int number[2]; + const builtin *bp; + +#define GET_CHARACTER \ + (character = getc (file)) + +#define GET_NUMBER(Number) \ + do \ + { \ + (Number) = 0; \ + while (isdigit (character)) \ + { \ + (Number) = 10 * (Number) + character - '0'; \ + GET_CHARACTER; \ + } \ + } \ + while (0) + +#define VALIDATE(Expected) \ + do \ + { \ + if (character != (Expected)) \ + issue_expect_message ((Expected)); \ + } \ + while (0) + + /* Skip comments (`#' at beginning of line) and blank lines, setting + character to the next directive or to EOF. */ + +#define GET_DIRECTIVE \ + do \ + { \ + GET_CHARACTER; \ + if (character == '#') \ + { \ + while (character != EOF && character != '\n') \ + GET_CHARACTER; \ + VALIDATE ('\n'); \ + } \ + } \ + while (character == '\n') + + file = m4_path_search (name, NULL); + if (file == NULL) + M4ERROR ((EXIT_FAILURE, errno, "cannot open %s", name)); + + allocated[0] = 100; + string[0] = xcharalloc ((size_t) allocated[0]); + allocated[1] = 100; + string[1] = xcharalloc ((size_t) allocated[1]); + + /* Validate format version. Only `1' is acceptable for now. */ + GET_DIRECTIVE; + VALIDATE ('V'); + GET_CHARACTER; + GET_NUMBER (number[0]); + if (number[0] > 1) + M4ERROR ((EXIT_MISMATCH, 0, + "frozen file version %d greater than max supported of 1", + number[0])); + else if (number[0] < 1) + M4ERROR ((EXIT_FAILURE, 0, + "ill-formed frozen file, version directive expected")); + VALIDATE ('\n'); + + GET_DIRECTIVE; + while (character != EOF) + { + switch (character) + { + default: + M4ERROR ((EXIT_FAILURE, 0, "ill-formed frozen file")); + + case 'C': + case 'D': + case 'F': + case 'T': + case 'Q': + operation = character; + GET_CHARACTER; + + /* Get string lengths. Accept a negative diversion number. */ + + if (operation == 'D' && character == '-') + { + GET_CHARACTER; + GET_NUMBER (number[0]); + number[0] = -number[0]; + } + else + GET_NUMBER (number[0]); + VALIDATE (','); + GET_CHARACTER; + GET_NUMBER (number[1]); + VALIDATE ('\n'); + + if (operation != 'D') + { + + /* Get first string contents. */ + + if (number[0] + 1 > allocated[0]) + { + free (string[0]); + allocated[0] = number[0] + 1; + string[0] = xcharalloc ((size_t) allocated[0]); + } + + if (number[0] > 0) + if (!fread (string[0], (size_t) number[0], 1, file)) + M4ERROR ((EXIT_FAILURE, 0, "premature end of frozen file")); + + string[0][number[0]] = '\0'; + } + + /* Get second string contents. */ + + if (number[1] + 1 > allocated[1]) + { + free (string[1]); + allocated[1] = number[1] + 1; + string[1] = xcharalloc ((size_t) allocated[1]); + } + + if (number[1] > 0) + if (!fread (string[1], (size_t) number[1], 1, file)) + M4ERROR ((EXIT_FAILURE, 0, "premature end of frozen file")); + + string[1][number[1]] = '\0'; + GET_CHARACTER; + VALIDATE ('\n'); + + /* Act according to operation letter. */ + + switch (operation) + { + case 'C': + + /* Change comment strings. */ + + set_comment (string[0], string[1]); + break; + + case 'D': + + /* Select a diversion and add a string to it. */ + + make_diversion (number[0]); + if (number[1] > 0) + shipout_text (NULL, string[1], number[1]); + break; + + case 'F': + + /* Enter a macro having a builtin function as a definition. */ + + bp = find_builtin_by_name (string[1]); + define_builtin (string[0], bp, SYMBOL_PUSHDEF); + break; + + case 'T': + + /* Enter a macro having an expansion text as a definition. */ + + define_user_macro (string[0], string[1], SYMBOL_PUSHDEF); + break; + + case 'Q': + + /* Change quote strings. */ + + set_quotes (string[0], string[1]); + break; + + default: + + /* Cannot happen. */ + + break; + } + break; + + } + GET_DIRECTIVE; + } + + free (string[0]); + free (string[1]); + errno = 0; + if (ferror (file) || fclose (file) != 0) + M4ERROR ((EXIT_FAILURE, errno, "unable to read frozen state")); + +#undef GET_CHARACTER +#undef GET_DIRECTIVE +#undef GET_NUMBER +#undef VALIDATE +} diff --git a/src/bin/m4/src/input.c b/src/bin/m4/src/input.c new file mode 100644 index 0000000000..6296281bf6 --- /dev/null +++ b/src/bin/m4/src/input.c @@ -0,0 +1,1129 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006 + Free Software Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* Handling of different input sources, and lexical analysis. */ + +#include "m4.h" + +/* Unread input can be either files, that should be read (eg. included + files), strings, which should be rescanned (eg. macro expansion text), + or quoted macro definitions (as returned by the builtin "defn"). + Unread input are organised in a stack, implemented with an obstack. + Each input source is described by a "struct input_block". The obstack + is "current_input". The top of the input stack is "isp". + + The macro "m4wrap" places the text to be saved on another input + stack, on the obstack "wrapup_stack", whose top is "wsp". When EOF + is seen on normal input (eg, when "current_input" is empty), input is + switched over to "wrapup_stack", and the original "current_input" is + freed. A new stack is allocated for "wrapup_stack", which will + accept any text produced by calls to "m4wrap" from within the + wrapped text. This process of shuffling "wrapup_stack" to + "current_input" can continue indefinitely, even generating infinite + loops (e.g. "define(`f',`m4wrap(`f')')f"), without memory leaks. + + Pushing new input on the input stack is done by push_file (), + push_string (), push_wrapup () (for wrapup text), and push_macro () + (for macro definitions). Because macro expansion needs direct access + to the current input obstack (for optimisation), push_string () are + split in two functions, push_string_init (), which returns a pointer + to the current input stack, and push_string_finish (), which return a + pointer to the final text. The input_block *next is used to manage + the coordination between the different push routines. + + The current file and line number are stored in two global + variables, for use by the error handling functions in m4.c. Macro + expansion wants to report the line where a macro name was detected, + rather than where it finished collecting arguments. This also + applies to text resulting from macro expansions. So each input + block maintains its own notion of the current file and line, and + swapping between input blocks updates the global variables + accordingly. */ + +#ifdef ENABLE_CHANGEWORD +#include "regex.h" +#endif + +enum input_type +{ + INPUT_STRING, /* String resulting from macro expansion. */ + INPUT_FILE, /* File from command line or include. */ + INPUT_MACRO /* Builtin resulting from defn. */ +}; + +typedef enum input_type input_type; + +struct input_block +{ + struct input_block *prev; /* previous input_block on the input stack */ + input_type type; /* see enum values */ + const char *file; /* file where this input is from */ + int line; /* line where this input is from */ + union + { + struct + { + char *string; /* remaining string value */ + } + u_s; /* INPUT_STRING */ + struct + { + FILE *fp; /* input file handle */ + bool end : 1; /* true if peek has seen EOF */ + bool close : 1; /* true if we should close file on pop */ + bool advance_line : 1; /* track previous start_of_input_line */ + } + u_f; /* INPUT_FILE */ + builtin_func *func; /* pointer to macro's function */ + } + u; +}; + +typedef struct input_block input_block; + + +/* Current input file name. */ +const char *current_file; + +/* Current input line number. */ +int current_line; + +/* Obstack for storing individual tokens. */ +static struct obstack token_stack; + +/* Obstack for storing file names. */ +static struct obstack file_names; + +/* Wrapup input stack. */ +static struct obstack *wrapup_stack; + +/* Current stack, from input or wrapup. */ +static struct obstack *current_input; + +/* Bottom of token_stack, for obstack_free. */ +static void *token_bottom; + +/* Pointer to top of current_input. */ +static input_block *isp; + +/* Pointer to top of wrapup_stack. */ +static input_block *wsp; + +/* Aux. for handling split push_string (). */ +static input_block *next; + +/* Flag for next_char () to increment current_line. */ +static bool start_of_input_line; + +/* Flag for next_char () to recognize change in input block. */ +static bool input_change; + +#define CHAR_EOF 256 /* character return on EOF */ +#define CHAR_MACRO 257 /* character return for MACRO token */ + +/* Quote chars. */ +STRING rquote; +STRING lquote; + +/* Comment chars. */ +STRING bcomm; +STRING ecomm; + +#ifdef ENABLE_CHANGEWORD + +# define DEFAULT_WORD_REGEXP "[_a-zA-Z][_a-zA-Z0-9]*" + +static char *word_start; +static struct re_pattern_buffer word_regexp; +static int default_word_regexp; +static struct re_registers regs; + +#else /* ! ENABLE_CHANGEWORD */ +# define default_word_regexp 1 +#endif /* ! ENABLE_CHANGEWORD */ + +#ifdef DEBUG_INPUT +static const char *token_type_string (token_type); +#endif + + +/*-------------------------------------------------------------------. +| push_file () pushes an input file on the input stack, saving the | +| current file name and line number. If next is non-NULL, this push | +| invalidates a call to push_string_init (), whose storage is | +| consequently released. If CLOSE, then close FP after EOF is | +| detected. | +`-------------------------------------------------------------------*/ + +void +push_file (FILE *fp, const char *title, bool close) +{ + input_block *i; + + if (next != NULL) + { + obstack_free (current_input, next); + next = NULL; + } + + if (debug_level & DEBUG_TRACE_INPUT) + DEBUG_MESSAGE1 ("input read from %s", title); + + i = (input_block *) obstack_alloc (current_input, + sizeof (struct input_block)); + i->type = INPUT_FILE; + i->file = (char *) obstack_copy0 (&file_names, title, strlen (title)); + i->line = 1; + input_change = true; + + i->u.u_f.fp = fp; + i->u.u_f.end = false; + i->u.u_f.close = close; + i->u.u_f.advance_line = start_of_input_line; + output_current_line = -1; + + i->prev = isp; + isp = i; +} + +/*---------------------------------------------------------------. +| push_macro () pushes a builtin macro's definition on the input | +| stack. If next is non-NULL, this push invalidates a call to | +| push_string_init (), whose storage is consequently released. | +`---------------------------------------------------------------*/ + +void +push_macro (builtin_func *func) +{ + input_block *i; + + if (next != NULL) + { + obstack_free (current_input, next); + next = NULL; + } + + i = (input_block *) obstack_alloc (current_input, + sizeof (struct input_block)); + i->type = INPUT_MACRO; + i->file = current_file; + i->line = current_line; + input_change = true; + + i->u.func = func; + i->prev = isp; + isp = i; +} + +/*------------------------------------------------------------------. +| First half of push_string (). The pointer next points to the new | +| input_block. | +`------------------------------------------------------------------*/ + +struct obstack * +push_string_init (void) +{ + if (next != NULL) + { + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: recursive push_string!")); + abort (); + } + + next = (input_block *) obstack_alloc (current_input, + sizeof (struct input_block)); + next->type = INPUT_STRING; + next->file = current_file; + next->line = current_line; + + return current_input; +} + +/*------------------------------------------------------------------------. +| Last half of push_string (). If next is now NULL, a call to push_file | +| () has invalidated the previous call to push_string_init (), so we just | +| give up. If the new object is void, we do not push it. The function | +| push_string_finish () returns a pointer to the finished object. This | +| pointer is only for temporary use, since reading the next token might | +| release the memory used for the object. | +`------------------------------------------------------------------------*/ + +const char * +push_string_finish (void) +{ + const char *ret = NULL; + + if (next == NULL) + return NULL; + + if (obstack_object_size (current_input) > 0) + { + obstack_1grow (current_input, '\0'); + next->u.u_s.string = (char *) obstack_finish (current_input); + next->prev = isp; + isp = next; + ret = isp->u.u_s.string; /* for immediate use only */ + input_change = true; + } + else + obstack_free (current_input, next); /* people might leave garbage on it. */ + next = NULL; + return ret; +} + +/*------------------------------------------------------------------. +| The function push_wrapup () pushes a string on the wrapup stack. | +| When the normal input stack gets empty, the wrapup stack will | +| become the input stack, and push_string () and push_file () will | +| operate on wrapup_stack. Push_wrapup should be done as | +| push_string (), but this will suffice, as long as arguments to | +| m4_m4wrap () are moderate in size. | +`------------------------------------------------------------------*/ + +void +push_wrapup (const char *s) +{ + input_block *i; + i = (input_block *) obstack_alloc (wrapup_stack, + sizeof (struct input_block)); + i->prev = wsp; + i->type = INPUT_STRING; + i->file = current_file; + i->line = current_line; + i->u.u_s.string = (char *) obstack_copy0 (wrapup_stack, s, strlen (s)); + wsp = i; +} + + +/*-------------------------------------------------------------------------. +| The function pop_input () pops one level of input sources. If the | +| popped input_block is a file, current_file and current_line are reset to | +| the saved values before the memory for the input_block are released. | +`-------------------------------------------------------------------------*/ + +static void +pop_input (void) +{ + input_block *tmp = isp->prev; + + switch (isp->type) + { + case INPUT_STRING: + case INPUT_MACRO: + break; + + case INPUT_FILE: + if (debug_level & DEBUG_TRACE_INPUT) + { + if (tmp) + DEBUG_MESSAGE2 ("input reverted to %s, line %d", + tmp->file, tmp->line); + else + DEBUG_MESSAGE ("input exhausted"); + } + + if (ferror (isp->u.u_f.fp)) + { + M4ERROR ((warning_status, 0, "read error")); + if (isp->u.u_f.close) + fclose (isp->u.u_f.fp); + retcode = EXIT_FAILURE; + } + else if (isp->u.u_f.close && fclose (isp->u.u_f.fp) == EOF) + { + M4ERROR ((warning_status, errno, "error reading file")); + retcode = EXIT_FAILURE; + } + start_of_input_line = isp->u.u_f.advance_line; + output_current_line = -1; + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: input stack botch in pop_input ()")); + abort (); + } + obstack_free (current_input, isp); + next = NULL; /* might be set in push_string_init () */ + + isp = tmp; + input_change = true; +} + +/*------------------------------------------------------------------------. +| To switch input over to the wrapup stack, main () calls pop_wrapup (). | +| Since wrapup text can install new wrapup text, pop_wrapup () returns | +| false when there is no wrapup text on the stack, and true otherwise. | +`------------------------------------------------------------------------*/ + +bool +pop_wrapup (void) +{ + next = NULL; + obstack_free (current_input, NULL); + free (current_input); + + if (wsp == NULL) + { + /* End of the program. Free all memory even though we are about + to exit, since it makes leak detection easier. */ + obstack_free (&token_stack, NULL); + obstack_free (&file_names, NULL); + obstack_free (wrapup_stack, NULL); + free (wrapup_stack); + return false; + } + + current_input = wrapup_stack; + wrapup_stack = (struct obstack *) xmalloc (sizeof (struct obstack)); + obstack_init (wrapup_stack); + + isp = wsp; + wsp = NULL; + input_change = true; + + return true; +} + +/*-------------------------------------------------------------------. +| When a MACRO token is seen, next_token () uses init_macro_token () | +| to retrieve the value of the function pointer. | +`-------------------------------------------------------------------*/ + +static void +init_macro_token (token_data *td) +{ + if (isp->type != INPUT_MACRO) + { + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad call to init_macro_token ()")); + abort (); + } + + TOKEN_DATA_TYPE (td) = TOKEN_FUNC; + TOKEN_DATA_FUNC (td) = isp->u.func; +} + + +/*------------------------------------------------------------------------. +| Low level input is done a character at a time. The function peek_input | +| () is used to look at the next character in the input stream. At any | +| given time, it reads from the input_block on the top of the current | +| input stack. | +`------------------------------------------------------------------------*/ + +static int +peek_input (void) +{ + int ch; + input_block *block = isp; + + while (1) + { + if (block == NULL) + return CHAR_EOF; + + switch (block->type) + { + case INPUT_STRING: + ch = to_uchar (block->u.u_s.string[0]); + if (ch != '\0') + return ch; + break; + + case INPUT_FILE: + ch = getc (block->u.u_f.fp); + if (ch != EOF) + { + ungetc (ch, block->u.u_f.fp); + return ch; + } + block->u.u_f.end = true; + break; + + case INPUT_MACRO: + return CHAR_MACRO; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: input stack botch in peek_input ()")); + abort (); + } + block = block->prev; + } +} + +/*-------------------------------------------------------------------------. +| The function next_char () is used to read and advance the input to the | +| next character. It also manages line numbers for error messages, so | +| they do not get wrong, due to lookahead. The token consisting of a | +| newline alone is taken as belonging to the line it ends, and the current | +| line number is not incremented until the next character is read. | +| 99.9% of all calls will read from a string, so factor that out into a | +| macro for speed. | +`-------------------------------------------------------------------------*/ + +#define next_char() \ + (isp && isp->type == INPUT_STRING && isp->u.u_s.string[0] \ + && !input_change \ + ? to_uchar (*isp->u.u_s.string++) \ + : next_char_1 ()) + +static int +next_char_1 (void) +{ + int ch; + + while (1) + { + if (isp == NULL) + { + current_file = ""; + current_line = 0; + return CHAR_EOF; + } + + if (input_change) + { + current_file = isp->file; + current_line = isp->line; + input_change = false; + } + + switch (isp->type) + { + case INPUT_STRING: + ch = to_uchar (*isp->u.u_s.string++); + if (ch != '\0') + return ch; + break; + + case INPUT_FILE: + if (start_of_input_line) + { + start_of_input_line = false; + current_line = ++isp->line; + } + + /* If stdin is a terminal, calling getc after peek_input + already called it would make the user have to hit ^D + twice to quit. */ + ch = isp->u.u_f.end ? EOF : getc (isp->u.u_f.fp); + if (ch != EOF) + { + if (ch == '\n') + start_of_input_line = true; + return ch; + } + break; + + case INPUT_MACRO: + pop_input (); /* INPUT_MACRO input sources has only one + token */ + return CHAR_MACRO; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: input stack botch in next_char ()")); + abort (); + } + + /* End of input source --- pop one level. */ + pop_input (); + } +} + +/*------------------------------------------------------------------------. +| skip_line () simply discards all immediately following characters, upto | +| the first newline. It is only used from m4_dnl (). | +`------------------------------------------------------------------------*/ + +void +skip_line (void) +{ + int ch; + const char *file = current_file; + int line = current_line; + + while ((ch = next_char ()) != CHAR_EOF && ch != '\n') + ; + if (ch == CHAR_EOF) + /* current_file changed to "" if we see CHAR_EOF, use the + previous value we stored earlier. */ + M4ERROR_AT_LINE ((warning_status, 0, file, line, + "Warning: end of file treated as newline")); + /* On the rare occasion that dnl crosses include file boundaries + (either the input file did not end in a newline, or changeword + was used), calling next_char can update current_file and + current_line, and that update will be undone as we return to + expand_macro. This informs next_char to fix things again. */ + if (file != current_file || line != current_line) + input_change = true; +} + + +/*------------------------------------------------------------------. +| This function is for matching a string against a prefix of the | +| input stream. If the string matches the input and consume is | +| true, the input is discarded; otherwise any characters read are | +| pushed back again. The function is used only when multicharacter | +| quotes or comment delimiters are used. | +`------------------------------------------------------------------*/ + +static bool +match_input (const char *s, bool consume) +{ + int n; /* number of characters matched */ + int ch; /* input character */ + const char *t; + bool result = false; + + ch = peek_input (); + if (ch != to_uchar (*s)) + return false; /* fail */ + + if (s[1] == '\0') + { + if (consume) + (void) next_char (); + return true; /* short match */ + } + + (void) next_char (); + for (n = 1, t = s++; (ch = peek_input ()) == to_uchar (*s++); ) + { + (void) next_char (); + n++; + if (*s == '\0') /* long match */ + { + if (consume) + return true; + result = true; + break; + } + } + + /* Failed or shouldn't consume, push back input. */ + { + struct obstack *h = push_string_init (); + + /* `obstack_grow' may be macro evaluating its arg 1 several times. */ + obstack_grow (h, t, n); + } + push_string_finish (); + return result; +} + +/*--------------------------------------------------------------------. +| The macro MATCH() is used to match a string S against the input. | +| The first character is handled inline, for speed. Hopefully, this | +| will not hurt efficiency too much when single character quotes and | +| comment delimiters are used. If CONSUME, then CH is the result of | +| next_char, and a successful match will discard the matched string. | +| Otherwise, CH is the result of peek_char, and the input stream is | +| effectively unchanged. | +`--------------------------------------------------------------------*/ + +#define MATCH(ch, s, consume) \ + (to_uchar ((s)[0]) == (ch) \ + && (ch) != '\0' \ + && ((s)[1] == '\0' || (match_input ((s) + (consume), consume)))) + + +/*----------------------------------------------------------. +| Inititialise input stacks, and quote/comment characters. | +`----------------------------------------------------------*/ + +void +input_init (void) +{ + current_file = ""; + current_line = 0; + + current_input = (struct obstack *) xmalloc (sizeof (struct obstack)); + obstack_init (current_input); + wrapup_stack = (struct obstack *) xmalloc (sizeof (struct obstack)); + obstack_init (wrapup_stack); + + obstack_init (&file_names); + + /* Allocate an object in the current chunk, so that obstack_free + will always work even if the first token parsed spills to a new + chunk. */ + obstack_init (&token_stack); + obstack_alloc (&token_stack, 1); + token_bottom = obstack_base (&token_stack); + + isp = NULL; + wsp = NULL; + next = NULL; + + start_of_input_line = false; + + lquote.string = xstrdup (DEF_LQUOTE); + lquote.length = strlen (lquote.string); + rquote.string = xstrdup (DEF_RQUOTE); + rquote.length = strlen (rquote.string); + bcomm.string = xstrdup (DEF_BCOMM); + bcomm.length = strlen (bcomm.string); + ecomm.string = xstrdup (DEF_ECOMM); + ecomm.length = strlen (ecomm.string); + +#ifdef ENABLE_CHANGEWORD + set_word_regexp (user_word_regexp); +#endif +} + + +/*------------------------------------------------------------------. +| Functions for setting quotes and comment delimiters. Used by | +| m4_changecom () and m4_changequote (). Pass NULL if the argument | +| was not present, to distinguish from an explicit empty string. | +`------------------------------------------------------------------*/ + +void +set_quotes (const char *lq, const char *rq) +{ + free (lquote.string); + free (rquote.string); + + /* POSIX states that with 0 arguments, the default quotes are used. + POSIX XCU ERN 112 states that behavior is implementation-defined + if there was only one argument, or if there is an empty string in + either position when there are two arguments. We allow an empty + left quote to disable quoting, but a non-empty left quote will + always create a non-empty right quote. See the texinfo for what + some other implementations do. */ + if (!lq) + { + lq = DEF_LQUOTE; + rq = DEF_RQUOTE; + } + else if (!rq || (*lq && !*rq)) + rq = DEF_RQUOTE; + + lquote.string = xstrdup (lq); + lquote.length = strlen (lquote.string); + rquote.string = xstrdup (rq); + rquote.length = strlen (rquote.string); +} + +void +set_comment (const char *bc, const char *ec) +{ + free (bcomm.string); + free (ecomm.string); + + /* POSIX requires no arguments to disable comments. It requires + empty arguments to be used as-is, but this is counter to + traditional behavior, because a non-null begin and null end makes + it impossible to end a comment. An aardvark has been filed: + http://www.opengroup.org/austin/mailarchives/ag-review/msg02168.html + This implementation assumes the aardvark will be approved. See + the texinfo for what some other implementations do. */ + if (!bc) + bc = ec = ""; + else if (!ec || (*bc && !*ec)) + ec = DEF_ECOMM; + + bcomm.string = xstrdup (bc); + bcomm.length = strlen (bcomm.string); + ecomm.string = xstrdup (ec); + ecomm.length = strlen (ecomm.string); +} + +#ifdef ENABLE_CHANGEWORD + +static void +init_pattern_buffer (struct re_pattern_buffer *buf) +{ + buf->translate = NULL; + buf->fastmap = NULL; + buf->buffer = NULL; + buf->allocated = 0; +} + +void +set_word_regexp (const char *regexp) +{ + int i; + char test[2]; + const char *msg; + struct re_pattern_buffer new_word_regexp; + + if (!*regexp || !strcmp (regexp, DEFAULT_WORD_REGEXP)) + { + default_word_regexp = true; + return; + } + + /* Dry run to see whether the new expression is compilable. */ + init_pattern_buffer (&new_word_regexp); + msg = re_compile_pattern (regexp, strlen (regexp), &new_word_regexp); + regfree (&new_word_regexp); + + if (msg != NULL) + { + M4ERROR ((warning_status, 0, + "bad regular expression `%s': %s", regexp, msg)); + return; + } + + /* If compilation worked, retry using the word_regexp struct. + Can't rely on struct assigns working, so redo the compilation. */ + regfree (&word_regexp); + msg = re_compile_pattern (regexp, strlen (regexp), &word_regexp); + re_set_registers (&word_regexp, ®s, regs.num_regs, regs.start, regs.end); + + if (msg != NULL) + { + M4ERROR ((EXIT_FAILURE, 0, + "INTERNAL ERROR: expression recompilation `%s': %s", + regexp, msg)); + } + + default_word_regexp = false; + + if (word_start == NULL) + word_start = (char *) xmalloc (256); + + word_start[0] = '\0'; + test[1] = '\0'; + for (i = 1; i < 256; i++) + { + test[0] = i; + word_start[i] = re_search (&word_regexp, test, 1, 0, 0, NULL) >= 0; + } +} + +#endif /* ENABLE_CHANGEWORD */ + + +/*-------------------------------------------------------------------------. +| Parse and return a single token from the input stream. A token can | +| either be TOKEN_EOF, if the input_stack is empty; it can be TOKEN_STRING | +| for a quoted string; TOKEN_WORD for something that is a potential macro | +| name; and TOKEN_SIMPLE for any single character that is not a part of | +| any of the previous types. | +| | +| Next_token () return the token type, and passes back a pointer to the | +| token data through TD. The token text is collected on the obstack | +| token_stack, which never contains more than one token text at a time. | +| The storage pointed to by the fields in TD is therefore subject to | +| change the next time next_token () is called. | +`-------------------------------------------------------------------------*/ + +token_type +next_token (token_data *td) +{ + int ch; + int quote_level; + token_type type; +#ifdef ENABLE_CHANGEWORD + int startpos; + char *orig_text = NULL; +#endif + const char *file; + int line; + + obstack_free (&token_stack, token_bottom); + + /* Can't consume character until after CHAR_MACRO is handled. */ + ch = peek_input (); + if (ch == CHAR_EOF) + { +#ifdef DEBUG_INPUT + fprintf (stderr, "next_token -> EOF\n"); +#endif + next_char (); + return TOKEN_EOF; + } + if (ch == CHAR_MACRO) + { + init_macro_token (td); + next_char (); +#ifdef DEBUG_INPUT + fprintf (stderr, "next_token -> MACDEF (%s)\n", + find_builtin_by_addr (TOKEN_DATA_FUNC (td))->name); +#endif + return TOKEN_MACDEF; + } + + next_char (); /* Consume character we already peeked at. */ + file = current_file; + line = current_line; + if (MATCH (ch, bcomm.string, true)) + { + obstack_grow (&token_stack, bcomm.string, bcomm.length); + while ((ch = next_char ()) != CHAR_EOF + && !MATCH (ch, ecomm.string, true)) + obstack_1grow (&token_stack, ch); + if (ch != CHAR_EOF) + obstack_grow (&token_stack, ecomm.string, ecomm.length); + else + /* current_file changed to "" if we see CHAR_EOF, use the + previous value we stored earlier. */ + M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, line, + "ERROR: end of file in comment")); + + type = TOKEN_STRING; + } + else if (default_word_regexp && (isalpha (ch) || ch == '_')) + { + obstack_1grow (&token_stack, ch); + while ((ch = peek_input ()) != CHAR_EOF && (isalnum (ch) || ch == '_')) + { + obstack_1grow (&token_stack, ch); + (void) next_char (); + } + type = TOKEN_WORD; + } + +#ifdef ENABLE_CHANGEWORD + + else if (!default_word_regexp && word_start[ch]) + { + obstack_1grow (&token_stack, ch); + while (1) + { + ch = peek_input (); + if (ch == CHAR_EOF) + break; + obstack_1grow (&token_stack, ch); + startpos = re_search (&word_regexp, + (char *) obstack_base (&token_stack), + obstack_object_size (&token_stack), 0, 0, + ®s); + if (startpos != 0 || + regs.end [0] != obstack_object_size (&token_stack)) + { + *(((char *) obstack_base (&token_stack) + + obstack_object_size (&token_stack)) - 1) = '\0'; + break; + } + next_char (); + } + + obstack_1grow (&token_stack, '\0'); + orig_text = (char *) obstack_finish (&token_stack); + + if (regs.start[1] != -1) + obstack_grow (&token_stack,orig_text + regs.start[1], + regs.end[1] - regs.start[1]); + else + obstack_grow (&token_stack, orig_text,regs.end[0]); + + type = TOKEN_WORD; + } + +#endif /* ENABLE_CHANGEWORD */ + + else if (!MATCH (ch, lquote.string, true)) + { + switch (ch) + { + case '(': + type = TOKEN_OPEN; + break; + case ',': + type = TOKEN_COMMA; + break; + case ')': + type = TOKEN_CLOSE; + break; + default: + type = TOKEN_SIMPLE; + break; + } + obstack_1grow (&token_stack, ch); + } + else + { + quote_level = 1; + while (1) + { + ch = next_char (); + if (ch == CHAR_EOF) + /* current_file changed to "" if we see CHAR_EOF, use + the previous value we stored earlier. */ + M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, line, + "ERROR: end of file in string")); + + if (MATCH (ch, rquote.string, true)) + { + if (--quote_level == 0) + break; + obstack_grow (&token_stack, rquote.string, rquote.length); + } + else if (MATCH (ch, lquote.string, true)) + { + quote_level++; + obstack_grow (&token_stack, lquote.string, lquote.length); + } + else + obstack_1grow (&token_stack, ch); + } + type = TOKEN_STRING; + } + + obstack_1grow (&token_stack, '\0'); + + TOKEN_DATA_TYPE (td) = TOKEN_TEXT; + TOKEN_DATA_TEXT (td) = (char *) obstack_finish (&token_stack); +#ifdef ENABLE_CHANGEWORD + if (orig_text == NULL) + orig_text = TOKEN_DATA_TEXT (td); + TOKEN_DATA_ORIG_TEXT (td) = orig_text; +#endif +#ifdef DEBUG_INPUT + fprintf (stderr, "next_token -> %s (%s)\n", + token_type_string (type), TOKEN_DATA_TEXT (td)); +#endif + return type; +} + +/*-----------------------------------------------. +| Peek at the next token from the input stream. | +`-----------------------------------------------*/ + +token_type +peek_token (void) +{ + token_type result; + int ch = peek_input (); + + if (ch == CHAR_EOF) + { + result = TOKEN_EOF; + } + else if (ch == CHAR_MACRO) + { + result = TOKEN_MACDEF; + } + else if (MATCH (ch, bcomm.string, false)) + { + result = TOKEN_STRING; + } + else if ((default_word_regexp && (isalpha (ch) || ch == '_')) +#ifdef ENABLE_CHANGEWORD + || (! default_word_regexp && word_start[ch]) +#endif /* ENABLE_CHANGEWORD */ + ) + { + result = TOKEN_WORD; + } + else if (MATCH (ch, lquote.string, false)) + { + result = TOKEN_STRING; + } + else + switch (ch) + { + case '(': + result = TOKEN_OPEN; + break; + case ',': + result = TOKEN_COMMA; + break; + case ')': + result = TOKEN_CLOSE; + break; + default: + result = TOKEN_SIMPLE; + } + +#ifdef DEBUG_INPUT + fprintf (stderr, "peek_token -> %s\n", token_type_string (result)); +#endif /* DEBUG_INPUT */ + return result; +} + + +#ifdef DEBUG_INPUT + +static const char * +token_type_string (token_type t) +{ + switch (t) + { /* TOKSW */ + case TOKEN_EOF: + return "EOF"; + case TOKEN_STRING: + return "STRING"; + case TOKEN_WORD: + return "WORD"; + case TOKEN_OPEN: + return "OPEN"; + case TOKEN_COMMA: + return "COMMA"; + case TOKEN_CLOSE: + return "CLOSE"; + case TOKEN_SIMPLE: + return "SIMPLE"; + case TOKEN_MACDEF: + return "MACDEF"; + default: + abort (); + } + } + +static void +print_token (const char *s, token_type t, token_data *td) +{ + fprintf (stderr, "%s: ", s); + switch (t) + { /* TOKSW */ + case TOKEN_OPEN: + case TOKEN_COMMA: + case TOKEN_CLOSE: + case TOKEN_SIMPLE: + fprintf (stderr, "char:"); + break; + + case TOKEN_WORD: + fprintf (stderr, "word:"); + break; + + case TOKEN_STRING: + fprintf (stderr, "string:"); + break; + + case TOKEN_MACDEF: + fprintf (stderr, "macro: %p\n", TOKEN_DATA_FUNC (td)); + break; + + case TOKEN_EOF: + fprintf (stderr, "eof\n"); + break; + } + fprintf (stderr, "\t\"%s\"\n", TOKEN_DATA_TEXT (td)); +} + +static void M4_GNUC_UNUSED +lex_debug (void) +{ + token_type t; + token_data td; + + while ((t = next_token (&td)) != TOKEN_EOF) + print_token ("lex", t, &td); +} +#endif diff --git a/src/bin/m4/src/m4.c b/src/bin/m4/src/m4.c new file mode 100644 index 0000000000..98b2582cf8 --- /dev/null +++ b/src/bin/m4/src/m4.c @@ -0,0 +1,581 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006 Free + Software Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "m4.h" + +#include +#include +#include + +static void usage (int); + +/* Enable sync output for /lib/cpp (-s). */ +int sync_output = 0; + +/* Debug (-d[flags]). */ +int debug_level = 0; + +/* Hash table size (should be a prime) (-Hsize). */ +size_t hash_table_size = HASHMAX; + +/* Disable GNU extensions (-G). */ +int no_gnu_extensions = 0; + +/* Prefix all builtin functions by `m4_'. */ +int prefix_all_builtins = 0; + +/* Max length of arguments in trace output (-lsize). */ +int max_debug_argument_length = 0; + +/* Suppress warnings about missing arguments. */ +int suppress_warnings = 0; + +/* If not zero, then value of exit status for warning diagnostics. */ +int warning_status = 0; + +/* Artificial limit for expansion_level in macro.c. */ +int nesting_limit = 1024; + +#ifdef ENABLE_CHANGEWORD +/* User provided regexp for describing m4 words. */ +const char *user_word_regexp = ""; +#endif + +/* The name this program was run with. */ +const char *program_name; + +struct macro_definition +{ + struct macro_definition *next; + int code; /* D, U, s, t, or '\1' */ + const char *arg; +}; +typedef struct macro_definition macro_definition; + +/* Error handling functions. */ + +/*-----------------------. +| Wrapper around error. | +`-----------------------*/ + +void +m4_error (int status, int errnum, const char *format, ...) +{ + va_list args; + va_start (args, format); + verror_at_line (status, errnum, current_line ? current_file : NULL, + current_line, format, args); +} + +/*-------------------------------. +| Wrapper around error_at_line. | +`-------------------------------*/ + +void +m4_error_at_line (int status, int errnum, const char *file, int line, + const char *format, ...) +{ + va_list args; + va_start (args, format); + verror_at_line (status, errnum, line ? file : NULL, line, format, args); +} + +#ifdef USE_STACKOVF + +/*---------------------------------------. +| Tell user stack overflowed and abort. | +`---------------------------------------*/ + +static void +stackovf_handler (void) +{ + M4ERROR ((EXIT_FAILURE, 0, + "ERROR: stack overflow. (Infinite define recursion?)")); +} + +#endif /* USE_STACKOV */ + + +/*---------------------------------------------. +| Print a usage message and exit with STATUS. | +`---------------------------------------------*/ + +static void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, "Try `%s --help' for more information.\n", program_name); + else + { + printf ("Usage: %s [OPTION]... [FILE]...\n", program_name); + fputs ("\ +Process macros in FILEs. If no FILE or if FILE is `-', standard input\n\ +is read.\n\ +", stdout); + fputs ("\ +\n\ +Mandatory or optional arguments to long options are mandatory or optional\n\ +for short options too.\n\ +\n\ +Operation modes:\n\ + --help display this help and exit\n\ + --version output version information and exit\n\ + -E, --fatal-warnings stop execution after first warning\n\ + -i, --interactive unbuffer output, ignore interrupts\n\ + -P, --prefix-builtins force a `m4_' prefix to all builtins\n\ + -Q, --quiet, --silent suppress some warnings for builtins\n\ +", stdout); +#ifdef ENABLE_CHANGEWORD + fputs ("\ + -W, --word-regexp=REGEXP use REGEXP for macro name syntax\n\ +", stdout); +#endif + fputs ("\ +\n\ +Preprocessor features:\n\ + -D, --define=NAME[=VALUE] define NAME as having VALUE, or empty\n\ + -I, --include=DIRECTORY append DIRECTORY to include path\n\ + -s, --synclines generate `#line NUM \"FILE\"' lines\n\ + -U, --undefine=NAME undefine NAME\n\ +", stdout); + fputs ("\ +\n\ +Limits control:\n\ + -G, --traditional suppress all GNU extensions\n\ + -H, --hashsize=PRIME set symbol lookup hash table size [509]\n\ + -L, --nesting-limit=NUMBER change artificial nesting limit [1024]\n\ +", stdout); + fputs ("\ +\n\ +Frozen state files:\n\ + -F, --freeze-state=FILE produce a frozen state on FILE at end\n\ + -R, --reload-state=FILE reload a frozen state from FILE at start\n\ +", stdout); + fputs ("\ +\n\ +Debugging:\n\ + -d, --debug[=FLAGS] set debug level (no FLAGS implies `aeq')\n\ + --debugfile=FILE redirect debug and trace output\n\ + -l, --arglength=NUM restrict macro tracing size\n\ + -t, --trace=NAME trace NAME when it is defined\n\ +", stdout); + fputs ("\ +\n\ +FLAGS is any of:\n\ + a show actual arguments\n\ + c show before collect, after collect and after call\n\ + e show expansion\n\ + f say current input file name\n\ + i show changes in input files\n\ + l say current input line number\n\ + p show results of path searches\n\ + q quote values as necessary, with a or e flag\n\ + t trace for all macro calls, not only traceon'ed\n\ + x add a unique macro call id, useful with c flag\n\ + V shorthand for all of the above flags\n\ +", stdout); + fputs ("\ +\n\ +If defined, the environment variable `M4PATH' is a colon-separated list\n\ +of directories included after any specified by `-I'.\n\ +", stdout); + fputs ("\ +\n\ +Exit status is 0 for success, 1 for failure, 63 for frozen file version\n\ +mismatch, or whatever value was passed to the m4exit macro.\n\ +", stdout); + printf ("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT); + } + exit (status); +} + +/*--------------------------------------. +| Decode options and launch execution. | +`--------------------------------------*/ + +/* For long options that have no equivalent short option, use a + non-character as a pseudo short option, starting with CHAR_MAX + 1. */ +enum +{ + DEBUGFILE_OPTION = CHAR_MAX + 1, /* no short opt */ + DIVERSIONS_OPTION, /* not quite -N, because of message */ + + HELP_OPTION, /* no short opt */ + VERSION_OPTION /* no short opt */ +}; + +static const struct option long_options[] = +{ + {"arglength", required_argument, NULL, 'l'}, + {"debug", optional_argument, NULL, 'd'}, + {"define", required_argument, NULL, 'D'}, + {"error-output", required_argument, NULL, 'o'}, /* FIXME: deprecate in 2.0 */ + {"fatal-warnings", no_argument, NULL, 'E'}, + {"freeze-state", required_argument, NULL, 'F'}, + {"hashsize", required_argument, NULL, 'H'}, + {"include", required_argument, NULL, 'I'}, + {"interactive", no_argument, NULL, 'i'}, + {"nesting-limit", required_argument, NULL, 'L'}, + {"prefix-builtins", no_argument, NULL, 'P'}, + {"quiet", no_argument, NULL, 'Q'}, + {"reload-state", required_argument, NULL, 'R'}, + {"silent", no_argument, NULL, 'Q'}, + {"synclines", no_argument, NULL, 's'}, + {"trace", required_argument, NULL, 't'}, + {"traditional", no_argument, NULL, 'G'}, + {"undefine", required_argument, NULL, 'U'}, + {"word-regexp", required_argument, NULL, 'W'}, + + {"debugfile", required_argument, NULL, DEBUGFILE_OPTION}, + {"diversions", required_argument, NULL, DIVERSIONS_OPTION}, + + {"help", no_argument, NULL, HELP_OPTION}, + {"version", no_argument, NULL, VERSION_OPTION}, + + { NULL, 0, NULL, 0 }, +}; + +/* Global catchall for any errors that should affect final error status, but + where we try to continue execution in the meantime. */ +int retcode; + +/* Process a command line file NAME, and return true only if it was + stdin. */ +static bool +process_file (const char *name) +{ + bool result = false; + if (strcmp (name, "-") == 0) + { + /* If stdin is a terminal, we want to allow 'm4 - file -' + to read input from stdin twice, like GNU cat. Besides, + there is no point closing stdin before wrapped text, to + minimize bugs in syscmd called from wrapped text. */ + push_file (stdin, "stdin", false); + result = true; + } + else + { + char *full_name; + FILE *fp = m4_path_search (name, &full_name); + if (fp == NULL) + { + error (0, errno, "%s", name); + /* Set the status to EXIT_FAILURE, even though we + continue to process files after a missing file. */ + retcode = EXIT_FAILURE; + return false; + } + push_file (fp, full_name, true); + free (full_name); + } + expand_input (); + return result; +} + +/* POSIX requires only -D, -U, and -s; and says that the first two + must be recognized when interspersed with file names. Traditional + behavior also handles -s between files. Starting OPTSTRING with + '-' forces getopt_long to hand back file names as arguments to opt + '\1', rather than reordering the command line. */ +#ifdef ENABLE_CHANGEWORD +#define OPTSTRING "-B:D:EF:GH:I:L:N:PQR:S:T:U:W:d::eil:o:st:" +#else +#define OPTSTRING "-B:D:EF:GH:I:L:N:PQR:S:T:U:d::eil:o:st:" +#endif + +int +main (int argc, char *const *argv, char *const *envp) +{ + macro_definition *head; /* head of deferred argument list */ + macro_definition *tail; + macro_definition *defn; + int optchar; /* option character */ + + macro_definition *defines; + bool read_stdin = false; + bool interactive = false; + bool seen_file = false; + const char *debugfile = NULL; + const char *frozen_file_to_read = NULL; + const char *frozen_file_to_write = NULL; + + program_name = argv[0]; + retcode = EXIT_SUCCESS; + atexit (close_stdout); + + include_init (); + debug_init (); +#ifdef USE_STACKOVF + setup_stackovf_trap (argv, envp, stackovf_handler); +#endif + + /* First, we decode the arguments, to size up tables and stuff. */ + + head = tail = NULL; + + while ((optchar = getopt_long (argc, (char **) argv, OPTSTRING, + long_options, NULL)) != -1) + switch (optchar) + { + default: + usage (EXIT_FAILURE); + + case 'B': + case 'S': + case 'T': + /* Compatibility junk: options that other implementations + support, but which we ignore as no-ops and don't list in + --help. */ + error (0, 0, "Warning: `m4 -%c' may be removed in a future release", + optchar); + break; + + case 'N': + case DIVERSIONS_OPTION: + /* -N became an obsolete no-op in 1.4.x. */ + error (0, 0, "Warning: `m4 %s' is deprecated", + optchar == 'N' ? "-N" : "--diversions"); + + case 'D': + case 'U': + case 's': + case 't': + case '\1': + /* Arguments that cannot be handled until later are accumulated. */ + + defn = (macro_definition *) xmalloc (sizeof (macro_definition)); + defn->code = optchar; + defn->arg = optarg; + defn->next = NULL; + + if (head == NULL) + head = defn; + else + tail->next = defn; + tail = defn; + + break; + + case 'E': + warning_status = EXIT_FAILURE; + break; + + case 'F': + frozen_file_to_write = optarg; + break; + + case 'G': + no_gnu_extensions = 1; + break; + + case 'H': + hash_table_size = atol (optarg); + if (hash_table_size == 0) + hash_table_size = HASHMAX; + break; + + case 'I': + add_include_directory (optarg); + break; + + case 'L': + nesting_limit = atoi (optarg); + break; + + case 'P': + prefix_all_builtins = 1; + break; + + case 'Q': + suppress_warnings = 1; + break; + + case 'R': + frozen_file_to_read = optarg; + break; + +#ifdef ENABLE_CHANGEWORD + case 'W': + user_word_regexp = optarg; + break; +#endif + + case 'd': + debug_level = debug_decode (optarg); + if (debug_level < 0) + { + error (0, 0, "bad debug flags: `%s'", optarg); + debug_level = 0; + } + break; + + case 'e': + error (0, 0, "Warning: `m4 -e' is deprecated, use `-i' instead"); + /* fall through */ + case 'i': + interactive = true; + break; + + case 'l': + max_debug_argument_length = atoi (optarg); + if (max_debug_argument_length <= 0) + max_debug_argument_length = 0; + break; + + case 'o': + /* -o/--error-output are deprecated synonyms of --debugfile, + but don't issue a deprecation warning until autoconf 2.61 + or later is more widely established, as such a warning + would interfere with all earlier versions of autoconf. */ + case DEBUGFILE_OPTION: + /* Don't call debug_set_output here, as it has side effects. */ + debugfile = optarg; + break; + + case VERSION_OPTION: + printf ("%s\n", PACKAGE_STRING); + fputs ("\ +Copyright (C) 2006 Free Software Foundation, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +\n\ +Written by Rene' Seindal.\n\ +", stdout); + exit (EXIT_SUCCESS); + break; + + case HELP_OPTION: + usage (EXIT_SUCCESS); + break; + } + + defines = head; + + /* Do the basic initializations. */ + if (debugfile && !debug_set_output (debugfile)) + M4ERROR ((0, errno, "cannot set debug file `%s'", debugfile)); + + input_init (); + output_init (); + symtab_init (); + include_env_init (); + + if (frozen_file_to_read) + reload_frozen_state (frozen_file_to_read); + else + builtin_init (); + + /* Interactive mode means unbuffered output, and interrupts ignored. */ + + if (interactive) + { + signal (SIGINT, SIG_IGN); + setbuf (stdout, (char *) NULL); + } + + /* Handle deferred command line macro definitions. Must come after + initialization of the symbol table. */ + + while (defines != NULL) + { + macro_definition *next; + symbol *sym; + + switch (defines->code) + { + case 'D': + { + /* defines->arg is read-only, so we need a copy. */ + char *macro_name = xstrdup (defines->arg); + char *macro_value = strchr (macro_name, '='); + if (macro_value) + *macro_value++ = '\0'; + define_user_macro (macro_name, macro_value, SYMBOL_INSERT); + free (macro_name); + } + break; + + case 'U': + lookup_symbol (defines->arg, SYMBOL_DELETE); + break; + + case 't': + sym = lookup_symbol (defines->arg, SYMBOL_INSERT); + SYMBOL_TRACED (sym) = true; + break; + + case 's': + sync_output = 1; + break; + + case '\1': + seen_file = true; + if (process_file (defines->arg)) + read_stdin = true; + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad code in deferred arguments")); + abort (); + } + + next = defines->next; + free (defines); + defines = next; + } + + /* Handle remaining input files. Each file is pushed on the input, + and the input read. Wrapup text is handled separately later. */ + + if (optind == argc && !seen_file) + read_stdin = process_file ("-"); + else + for (; optind < argc; optind++) + if (process_file (defines->arg)) + read_stdin = true; + + /* Now handle wrapup text. */ + + while (pop_wrapup ()) + expand_input (); + + /* Change debug stream back to stderr, to force flushing the debug + stream and detect any errors it might have encountered. Close + stdin if we read from it, to detect any errors. */ + debug_set_output (NULL); + if (read_stdin && fclose (stdin) == EOF) + { + M4ERROR ((warning_status, errno, "error reading file")); + retcode = EXIT_FAILURE; + } + + if (frozen_file_to_write) + produce_frozen_state (frozen_file_to_write); + else + { + make_diversion (0); + undivert_all (); + } + output_exit (); + exit (retcode); +} diff --git a/src/bin/m4/src/m4.h b/src/bin/m4/src/m4.h new file mode 100644 index 0000000000..809da2ec15 --- /dev/null +++ b/src/bin/m4/src/m4.h @@ -0,0 +1,458 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006 Free + Software Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* We use instead of "config.h" so that a compilation + using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h + (which it would do because it found this file in $srcdir). */ + +#include + +#include +#include +#include +#include +#include + +#include "binary-io.h" +#include "clean-temp.h" +#include "cloexec.h" +#include "close-stream.h" +#include "closeout.h" +#include "error.h" +#include "exit.h" +#include "exitfail.h" +#include "obstack.h" +#include "stdio--.h" +#include "stdlib--.h" +#include "unistd--.h" +#include "verror.h" +#include "xalloc.h" +#include "xvasprintf.h" + +/* Canonicalize UNIX recognition macros. */ +#if defined unix || defined __unix || defined __unix__ \ + || defined _POSIX_VERSION || defined _POSIX2_VERSION \ + || defined __NetBSD__ || defined __OpenBSD__ \ + || defined __APPLE__ || defined __APPLE_CC__ +# define UNIX 1 +#endif + +/* Canonicalize Windows recognition macros. */ +#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__ +# define W32_NATIVE 1 +#endif + +/* Canonicalize OS/2 recognition macro. */ +#ifdef __EMX__ +# define OS2 1 +#endif + +/* Used for version mismatch, when -R detects a frozen file it can't parse. */ +#define EXIT_MISMATCH 63 + +/* Various declarations. */ + +struct string + { + char *string; /* characters of the string */ + size_t length; /* length of the string */ + }; +typedef struct string STRING; + +/* Memory allocation. */ +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + +/* Those must come first. */ +typedef struct token_data token_data; +typedef void builtin_func (struct obstack *, int, token_data **); + +/* Take advantage of GNU C compiler source level optimization hints, + using portable macros. */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 6) +# define M4_GNUC_ATTRIBUTE(args) __attribute__(args) +#else +# define M4_GNUC_ATTRIBUTE(args) +#endif /* __GNUC__ */ + +#define M4_GNUC_UNUSED M4_GNUC_ATTRIBUTE((__unused__)) +#define M4_GNUC_PRINTF(fmt, arg) \ + M4_GNUC_ATTRIBUTE((__format__ (__printf__, fmt, arg))) + +/* File: m4.c --- global definitions. */ + +/* Option flags. */ +extern int sync_output; /* -s */ +extern int debug_level; /* -d */ +extern size_t hash_table_size; /* -H */ +extern int no_gnu_extensions; /* -G */ +extern int prefix_all_builtins; /* -P */ +extern int max_debug_argument_length; /* -l */ +extern int suppress_warnings; /* -Q */ +extern int warning_status; /* -E */ +extern int nesting_limit; /* -L */ +#ifdef ENABLE_CHANGEWORD +extern const char *user_word_regexp; /* -W */ +#endif + +/* Error handling. */ +extern int retcode; +extern const char *program_name; + +void m4_error (int, int, const char *, ...) M4_GNUC_PRINTF(3, 4); +void m4_error_at_line (int, int, const char *, int, + const char *, ...) M4_GNUC_PRINTF(5, 6); + +#define M4ERROR(Arglist) (m4_error Arglist) +#define M4ERROR_AT_LINE(Arglist) (m4_error_at_line Arglist) + +#ifdef USE_STACKOVF +void setup_stackovf_trap (char *const *, char *const *, + void (*handler) (void)); +#endif + +/* File: debug.c --- debugging and tracing function. */ + +extern FILE *debug; + +/* The value of debug_level is a bitmask of the following. */ + +/* a: show arglist in trace output */ +#define DEBUG_TRACE_ARGS 1 +/* e: show expansion in trace output */ +#define DEBUG_TRACE_EXPANSION 2 +/* q: quote args and expansion in trace output */ +#define DEBUG_TRACE_QUOTE 4 +/* t: trace all macros -- overrides trace{on,off} */ +#define DEBUG_TRACE_ALL 8 +/* l: add line numbers to trace output */ +#define DEBUG_TRACE_LINE 16 +/* f: add file name to trace output */ +#define DEBUG_TRACE_FILE 32 +/* p: trace path search of include files */ +#define DEBUG_TRACE_PATH 64 +/* c: show macro call before args collection */ +#define DEBUG_TRACE_CALL 128 +/* i: trace changes of input files */ +#define DEBUG_TRACE_INPUT 256 +/* x: add call id to trace output */ +#define DEBUG_TRACE_CALLID 512 + +/* V: very verbose -- print everything */ +#define DEBUG_TRACE_VERBOSE 1023 +/* default flags -- equiv: aeq */ +#define DEBUG_TRACE_DEFAULT 7 + +#define DEBUG_PRINT1(Fmt, Arg1) \ + do \ + { \ + if (debug != NULL) \ + fprintf (debug, Fmt, Arg1); \ + } \ + while (0) + +#define DEBUG_PRINT3(Fmt, Arg1, Arg2, Arg3) \ + do \ + { \ + if (debug != NULL) \ + fprintf (debug, Fmt, Arg1, Arg2, Arg3); \ + } \ + while (0) + +#define DEBUG_MESSAGE(Fmt) \ + do \ + { \ + if (debug != NULL) \ + { \ + debug_message_prefix (); \ + fprintf (debug, Fmt); \ + putc ('\n', debug); \ + } \ + } \ + while (0) + +#define DEBUG_MESSAGE1(Fmt, Arg1) \ + do \ + { \ + if (debug != NULL) \ + { \ + debug_message_prefix (); \ + fprintf (debug, Fmt, Arg1); \ + putc ('\n', debug); \ + } \ + } \ + while (0) + +#define DEBUG_MESSAGE2(Fmt, Arg1, Arg2) \ + do \ + { \ + if (debug != NULL) \ + { \ + debug_message_prefix (); \ + fprintf (debug, Fmt, Arg1, Arg2); \ + putc ('\n', debug); \ + } \ + } \ + while (0) + +void debug_init (void); +int debug_decode (const char *); +void debug_flush_files (void); +bool debug_set_output (const char *); +void debug_message_prefix (void); + +void trace_prepre (const char *, int); +void trace_pre (const char *, int, int, token_data **); +void trace_post (const char *, int, int, token_data **, const char *); + +/* File: input.c --- lexical definitions. */ + +/* Various different token types. */ +enum token_type +{ + TOKEN_EOF, /* end of file */ + TOKEN_STRING, /* a quoted string or comment */ + TOKEN_WORD, /* an identifier */ + TOKEN_OPEN, /* ( */ + TOKEN_COMMA, /* , */ + TOKEN_CLOSE, /* ) */ + TOKEN_SIMPLE, /* any other single character */ + TOKEN_MACDEF /* a macro's definition (see "defn") */ +}; + +/* The data for a token, a macro argument, and a macro definition. */ +enum token_data_type +{ + TOKEN_VOID, + TOKEN_TEXT, + TOKEN_FUNC +}; + +struct token_data +{ + enum token_data_type type; + union + { + struct + { + char *text; +#ifdef ENABLE_CHANGEWORD + char *original_text; +#endif + } + u_t; + builtin_func *func; + } + u; +}; + +#define TOKEN_DATA_TYPE(Td) ((Td)->type) +#define TOKEN_DATA_TEXT(Td) ((Td)->u.u_t.text) +#ifdef ENABLE_CHANGEWORD +# define TOKEN_DATA_ORIG_TEXT(Td) ((Td)->u.u_t.original_text) +#endif +#define TOKEN_DATA_FUNC(Td) ((Td)->u.func) + +typedef enum token_type token_type; +typedef enum token_data_type token_data_type; + +void input_init (void); +token_type peek_token (void); +token_type next_token (token_data *); +void skip_line (void); + +/* push back input */ +void push_file (FILE *, const char *, bool); +void push_macro (builtin_func *); +struct obstack *push_string_init (void); +const char *push_string_finish (void); +void push_wrapup (const char *); +bool pop_wrapup (void); + +/* current input file, and line */ +extern const char *current_file; +extern int current_line; + +/* left and right quote, begin and end comment */ +extern STRING bcomm, ecomm; +extern STRING lquote, rquote; + +#define DEF_LQUOTE "`" +#define DEF_RQUOTE "\'" +#define DEF_BCOMM "#" +#define DEF_ECOMM "\n" + +void set_quotes (const char *, const char *); +void set_comment (const char *, const char *); +#ifdef ENABLE_CHANGEWORD +void set_word_regexp (const char *); +#endif + +/* File: output.c --- output functions. */ +extern int current_diversion; +extern int output_current_line; + +void output_init (void); +void output_exit (void); +void shipout_text (struct obstack *, const char *, int); +void make_diversion (int); +void insert_diversion (int); +void insert_file (FILE *); +void freeze_diversions (FILE *); + +/* File symtab.c --- symbol table definitions. */ + +/* Operation modes for lookup_symbol (). */ +enum symbol_lookup +{ + SYMBOL_LOOKUP, + SYMBOL_INSERT, + SYMBOL_DELETE, + SYMBOL_PUSHDEF, + SYMBOL_POPDEF +}; + +/* Symbol table entry. */ +struct symbol +{ + struct symbol *next; + bool traced : 1; + bool shadowed : 1; + bool macro_args : 1; + bool blind_no_args : 1; + bool deleted : 1; + int pending_expansions; + + char *name; + token_data data; +}; + +#define SYMBOL_NEXT(S) ((S)->next) +#define SYMBOL_TRACED(S) ((S)->traced) +#define SYMBOL_SHADOWED(S) ((S)->shadowed) +#define SYMBOL_MACRO_ARGS(S) ((S)->macro_args) +#define SYMBOL_BLIND_NO_ARGS(S) ((S)->blind_no_args) +#define SYMBOL_DELETED(S) ((S)->deleted) +#define SYMBOL_PENDING_EXPANSIONS(S) ((S)->pending_expansions) +#define SYMBOL_NAME(S) ((S)->name) +#define SYMBOL_TYPE(S) (TOKEN_DATA_TYPE (&(S)->data)) +#define SYMBOL_TEXT(S) (TOKEN_DATA_TEXT (&(S)->data)) +#define SYMBOL_FUNC(S) (TOKEN_DATA_FUNC (&(S)->data)) + +typedef enum symbol_lookup symbol_lookup; +typedef struct symbol symbol; +typedef void hack_symbol (symbol *, void *); + +#define HASHMAX 509 /* default, overridden by -Hsize */ + +extern symbol **symtab; + +void free_symbol (symbol *sym); +void symtab_init (void); +symbol *lookup_symbol (const char *, symbol_lookup); +void hack_all_symbols (hack_symbol *, void *); + +/* File: macro.c --- macro expansion. */ + +void expand_input (void); +void call_macro (symbol *, int, token_data **, struct obstack *); + +/* File: builtin.c --- builtins. */ + +struct builtin +{ + const char *name; + bool gnu_extension : 1; + bool groks_macro_args : 1; + bool blind_if_no_args : 1; + builtin_func *func; +}; + +struct predefined +{ + const char *unix_name; + const char *gnu_name; + const char *func; +}; + +typedef struct builtin builtin; +typedef struct predefined predefined; + +void builtin_init (void); +void define_builtin (const char *, const builtin *, symbol_lookup); +void define_user_macro (const char *, const char *, symbol_lookup); +void undivert_all (void); +void expand_user_macro (struct obstack *, symbol *, int, token_data **); +void m4_placeholder (struct obstack *, int, token_data **); + +const builtin *find_builtin_by_addr (builtin_func *); +const builtin *find_builtin_by_name (const char *); + +/* File: path.c --- path search for include files. */ + +void include_init (void); +void include_env_init (void); +void add_include_directory (const char *); +FILE *m4_path_search (const char *, char **); + +/* File: eval.c --- expression evaluation. */ + +/* eval_t and unsigned_eval_t should be at least 32 bits. */ +typedef int eval_t; +typedef unsigned int unsigned_eval_t; + +bool evaluate (const char *, eval_t *); + +/* File: format.c --- printf like formatting. */ + +void format (struct obstack *, int, token_data **); + +/* File: freeze.c --- frozen state files. */ + +void produce_frozen_state (const char *); +void reload_frozen_state (const char *); + +/* Debugging the memory allocator. */ + +#ifdef WITH_DMALLOC +# define DMALLOC_FUNC_CHECK +# include +#endif + +/* Other debug stuff. */ + +#ifdef DEBUG +# define DEBUG_INCL 1 +# define DEBUG_INPUT 1 +# define DEBUG_MACRO 1 +# define DEBUG_OUTPUT 1 +# define DEBUG_STKOVF 1 +# define DEBUG_SYM 1 +#endif + +/* Convert a possibly-signed character to an unsigned character. This is + a bit safer than casting to unsigned char, since it catches some type + errors that the cast doesn't. */ +#if HAVE_INLINE +static inline unsigned char to_uchar (char ch) { return ch; } +#else +# define to_uchar(C) ((unsigned char) (C)) +#endif diff --git a/src/bin/m4/src/m4.rdef b/src/bin/m4/src/m4.rdef new file mode 100644 index 0000000000..0c2ad9a581 --- /dev/null +++ b/src/bin/m4/src/m4.rdef @@ -0,0 +1,11 @@ +resource app_version +{ + major = 1, + middle = 4, + minor = 8, + variety = B_APPV_FINAL, + internal = 0, + short_info = "m4", + long_info = "m4 ©2006 The Free Software Foundation" +}; + diff --git a/src/bin/m4/src/macro.c b/src/bin/m4/src/macro.c new file mode 100644 index 0000000000..a017ece231 --- /dev/null +++ b/src/bin/m4/src/macro.c @@ -0,0 +1,385 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006 Free Software + Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* This file contains the functions, that performs the basic argument + parsing and macro expansion. */ + +#include "m4.h" + +static void expand_macro (symbol *); +static void expand_token (struct obstack *, token_type, token_data *); + +/* Current recursion level in expand_macro (). */ +int expansion_level = 0; + +/* The number of the current call of expand_macro (). */ +static int macro_call_id = 0; + +/* The shared stack of collected arguments for macro calls; as each + argument is collected, it is finished and its location stored in + argv_stack. Normally, this stack can be used simultaneously by + multiple macro calls; the exception is when an outer macro has + generated some text, then calls a nested macro, in which case the + nested macro must use a local stack to leave the unfinished text + alone. Too bad obstack.h does not provide an easy way to reopen a + finished object for further growth, but in practice this does not + hurt us too much. */ +static struct obstack argc_stack; + +/* The shared stack of pointers to collected arguments for macro + calls. This object is never finished; we exploit the fact that + obstack_blank is documented to take a negative size to reduce the + size again. */ +static struct obstack argv_stack; + +/*----------------------------------------------------------------------. +| This function read all input, and expands each token, one at a time. | +`----------------------------------------------------------------------*/ + +void +expand_input (void) +{ + token_type t; + token_data td; + + obstack_init (&argc_stack); + obstack_init (&argv_stack); + + while ((t = next_token (&td)) != TOKEN_EOF) + expand_token ((struct obstack *) NULL, t, &td); + + obstack_free (&argc_stack, NULL); + obstack_free (&argv_stack, NULL); +} + + +/*------------------------------------------------------------------------. +| Expand one token, according to its type. Potential macro names | +| (TOKEN_WORD) are looked up in the symbol table, to see if they have a | +| macro definition. If they have, they are expanded as macros, otherwise | +| the text are just copied to the output. | +`------------------------------------------------------------------------*/ + +static void +expand_token (struct obstack *obs, token_type t, token_data *td) +{ + symbol *sym; + + switch (t) + { /* TOKSW */ + case TOKEN_EOF: + case TOKEN_MACDEF: + break; + + case TOKEN_OPEN: + case TOKEN_COMMA: + case TOKEN_CLOSE: + case TOKEN_SIMPLE: + case TOKEN_STRING: + shipout_text (obs, TOKEN_DATA_TEXT (td), strlen (TOKEN_DATA_TEXT (td))); + break; + + case TOKEN_WORD: + sym = lookup_symbol (TOKEN_DATA_TEXT (td), SYMBOL_LOOKUP); + if (sym == NULL || SYMBOL_TYPE (sym) == TOKEN_VOID + || (SYMBOL_TYPE (sym) == TOKEN_FUNC + && SYMBOL_BLIND_NO_ARGS (sym) + && peek_token () != TOKEN_OPEN)) + { +#ifdef ENABLE_CHANGEWORD + shipout_text (obs, TOKEN_DATA_ORIG_TEXT (td), + strlen (TOKEN_DATA_ORIG_TEXT (td))); +#else + shipout_text (obs, TOKEN_DATA_TEXT (td), + strlen (TOKEN_DATA_TEXT (td))); +#endif + } + else + expand_macro (sym); + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad token type in expand_token ()")); + abort (); + } +} + + +/*-------------------------------------------------------------------------. +| This function parses one argument to a macro call. It expects the first | +| left parenthesis, or the separating comma to have been read by the | +| caller. It skips leading whitespace, and reads and expands tokens, | +| until it finds a comma or an right parenthesis at the same level of | +| parentheses. It returns a flag indicating whether the argument read are | +| the last for the active macro call. The argument are build on the | +| obstack OBS, indirectly through expand_token (). | +`-------------------------------------------------------------------------*/ + +static bool +expand_argument (struct obstack *obs, token_data *argp) +{ + token_type t; + token_data td; + char *text; + int paren_level; + const char *file = current_file; + int line = current_line; + + TOKEN_DATA_TYPE (argp) = TOKEN_VOID; + + /* Skip leading white space. */ + do + { + t = next_token (&td); + } + while (t == TOKEN_SIMPLE && isspace (to_uchar (*TOKEN_DATA_TEXT (&td)))); + + paren_level = 0; + + while (1) + { + + switch (t) + { /* TOKSW */ + case TOKEN_COMMA: + case TOKEN_CLOSE: + if (paren_level == 0) + { + /* The argument MUST be finished, whether we want it or not. */ + obstack_1grow (obs, '\0'); + text = (char *) obstack_finish (obs); + + if (TOKEN_DATA_TYPE (argp) == TOKEN_VOID) + { + TOKEN_DATA_TYPE (argp) = TOKEN_TEXT; + TOKEN_DATA_TEXT (argp) = text; + } + return t == TOKEN_COMMA; + } + /* fallthru */ + case TOKEN_OPEN: + case TOKEN_SIMPLE: + text = TOKEN_DATA_TEXT (&td); + + if (*text == '(') + paren_level++; + else if (*text == ')') + paren_level--; + expand_token (obs, t, &td); + break; + + case TOKEN_EOF: + /* current_file changed to "" if we see TOKEN_EOF, use the + previous value we stored earlier. */ + M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, line, + "ERROR: end of file in argument list")); + break; + + case TOKEN_WORD: + case TOKEN_STRING: + expand_token (obs, t, &td); + break; + + case TOKEN_MACDEF: + if (obstack_object_size (obs) == 0) + { + TOKEN_DATA_TYPE (argp) = TOKEN_FUNC; + TOKEN_DATA_FUNC (argp) = TOKEN_DATA_FUNC (&td); + } + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad token type in expand_argument ()")); + abort (); + } + + t = next_token (&td); + } +} + +/*-------------------------------------------------------------------------. +| Collect all the arguments to a call of the macro SYM. The arguments are | +| stored on the obstack ARGUMENTS and a table of pointers to the arguments | +| on the obstack ARGPTR. | +`-------------------------------------------------------------------------*/ + +static void +collect_arguments (symbol *sym, struct obstack *argptr, + struct obstack *arguments) +{ + token_data td; + token_data *tdp; + bool more_args; + bool groks_macro_args = SYMBOL_MACRO_ARGS (sym); + + TOKEN_DATA_TYPE (&td) = TOKEN_TEXT; + TOKEN_DATA_TEXT (&td) = SYMBOL_NAME (sym); + tdp = (token_data *) obstack_copy (arguments, &td, sizeof td); + obstack_ptr_grow (argptr, tdp); + + if (peek_token () == TOKEN_OPEN) + { + next_token (&td); /* gobble parenthesis */ + do + { + more_args = expand_argument (arguments, &td); + + if (!groks_macro_args && TOKEN_DATA_TYPE (&td) == TOKEN_FUNC) + { + TOKEN_DATA_TYPE (&td) = TOKEN_TEXT; + TOKEN_DATA_TEXT (&td) = (char *) ""; + } + tdp = (token_data *) obstack_copy (arguments, &td, sizeof td); + obstack_ptr_grow (argptr, tdp); + } + while (more_args); + } +} + + +/*------------------------------------------------------------------------. +| The actual call of a macro is handled by call_macro (). call_macro () | +| is passed a symbol SYM, whose type is used to call either a builtin | +| function, or the user macro expansion function expand_user_macro () | +| (lives in builtin.c). There are ARGC arguments to the call, stored in | +| the ARGV table. The expansion is left on the obstack EXPANSION. Macro | +| tracing is also handled here. | +`------------------------------------------------------------------------*/ + +void +call_macro (symbol *sym, int argc, token_data **argv, + struct obstack *expansion) +{ + switch (SYMBOL_TYPE (sym)) + { + case TOKEN_FUNC: + (*SYMBOL_FUNC (sym)) (expansion, argc, argv); + break; + + case TOKEN_TEXT: + expand_user_macro (expansion, sym, argc, argv); + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: bad symbol type in call_macro ()")); + abort (); + } +} + +/*-------------------------------------------------------------------------. +| The macro expansion is handled by expand_macro (). It parses the | +| arguments, using collect_arguments (), and builds a table of pointers to | +| the arguments. The arguments themselves are stored on a local obstack. | +| Expand_macro () uses call_macro () to do the call of the macro. | +| | +| Expand_macro () is potentially recursive, since it calls expand_argument | +| (), which might call expand_token (), which might call expand_macro (). | +`-------------------------------------------------------------------------*/ + +static void +expand_macro (symbol *sym) +{ + struct obstack arguments; /* Alternate obstack if argc_stack is busy. */ + unsigned argv_base; /* Size of argv_stack on entry. */ + bool use_argc_stack = true; /* Whether argc_stack is safe. */ + token_data **argv; + int argc; + struct obstack *expansion; + const char *expanded; + bool traced; + int my_call_id; + + /* Report errors at the location where the open parenthesis (if any) + was found, but after expansion, restore global state back to the + location of the close parenthesis. This is safe since we + guarantee that macro expansion does not alter the state of + current_file/current_line (dnl, include, and sinclude are special + cased in the input engine to ensure this fact). */ + const char *loc_open_file = current_file; + int loc_open_line = current_line; + const char *loc_close_file; + int loc_close_line; + + SYMBOL_PENDING_EXPANSIONS (sym)++; + expansion_level++; + if (nesting_limit > 0 && expansion_level > nesting_limit) + M4ERROR ((EXIT_FAILURE, 0, + "ERROR: recursion limit of %d exceeded, use -L to change it", + nesting_limit)); + + macro_call_id++; + my_call_id = macro_call_id; + + traced = (debug_level & DEBUG_TRACE_ALL) || SYMBOL_TRACED (sym); + + argv_base = obstack_object_size (&argv_stack); + if (obstack_object_size (&argc_stack) > 0) + { + /* We cannot use argc_stack if this is a nested invocation, and an + outer invocation has an unfinished argument being + collected. */ + obstack_init (&arguments); + use_argc_stack = false; + } + + if (traced && (debug_level & DEBUG_TRACE_CALL)) + trace_prepre (SYMBOL_NAME (sym), my_call_id); + + collect_arguments (sym, &argv_stack, + use_argc_stack ? &argc_stack : &arguments); + + argc = ((obstack_object_size (&argv_stack) - argv_base) + / sizeof (token_data *)); + argv = (token_data **) ((char *) obstack_base (&argv_stack) + argv_base); + + loc_close_file = current_file; + loc_close_line = current_line; + current_file = loc_open_file; + current_line = loc_open_line; + + if (traced) + trace_pre (SYMBOL_NAME (sym), my_call_id, argc, argv); + + expansion = push_string_init (); + call_macro (sym, argc, argv, expansion); + expanded = push_string_finish (); + + if (traced) + trace_post (SYMBOL_NAME (sym), my_call_id, argc, argv, expanded); + + current_file = loc_close_file; + current_line = loc_close_line; + + --expansion_level; + --SYMBOL_PENDING_EXPANSIONS (sym); + + if (SYMBOL_DELETED (sym)) + free_symbol (sym); + + if (use_argc_stack) + obstack_free (&argc_stack, argv[0]); + else + obstack_free (&arguments, NULL); + obstack_blank (&argv_stack, -argc * sizeof (token_data *)); +} diff --git a/src/bin/m4/src/output.c b/src/bin/m4/src/output.c new file mode 100644 index 0000000000..d482bf7504 --- /dev/null +++ b/src/bin/m4/src/output.c @@ -0,0 +1,815 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006 Free + Software Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "m4.h" + +#include +#include + +#include "gl_avltree_oset.h" + +/* Size of initial in-memory buffer size for diversions. Small diversions + would usually fit in. */ +#define INITIAL_BUFFER_SIZE 512 + +/* Maximum value for the total of all in-memory buffer sizes for + diversions. */ +#define MAXIMUM_TOTAL_SIZE (512 * 1024) + +/* Size of buffer size to use while copying files. */ +#define COPY_BUFFER_SIZE (32 * 512) + +/* Output functions. Most of the complexity is for handling cpp like + sync lines. + + This code is fairly entangled with the code in input.c, and maybe it + belongs there? */ + +typedef struct temp_dir m4_temp_dir; + +/* When part of diversion_table, each struct m4_diversion either + represents an open file (zero size, non-NULL u.file), an in-memory + buffer (non-zero size, non-NULL u.buffer), or an unused placeholder + diversion (zero size, u is NULL, non-zero used indicates that a + file has been created). When not part of diversion_table, u.next + is a pointer to the free_list chain. */ + +typedef struct m4_diversion m4_diversion; + +struct m4_diversion + { + union + { + FILE *file; /* diversion file on disk */ + char *buffer; /* in-memory diversion buffer */ + m4_diversion *next; /* free-list pointer */ + } u; + int divnum; /* which diversion this represents */ + int size; /* usable size before reallocation */ + int used; /* used length in characters */ + }; + +/* Table of diversions 1 through INT_MAX. */ +static gl_oset_t diversion_table; + +/* Diversion 0 (not part of diversion_table). */ +static m4_diversion div0; + +/* Linked list of reclaimed diversion storage. */ +static m4_diversion *free_list; + +/* Obstack from which diversion storage is allocated. */ +static struct obstack diversion_storage; + +/* Total size of all in-memory buffer sizes. */ +static int total_buffer_size; + +/* The number of the currently active diversion. This variable is + maintained for the `divnum' builtin function. */ +int current_diversion; + +/* Current output diversion, NULL if output is being currently discarded. */ +static m4_diversion *output_diversion; + +/* Values of some output_diversion fields, cached out for speed. */ +static FILE *output_file; /* current value of (file) */ +static char *output_cursor; /* current value of (buffer + used) */ +static int output_unused; /* current value of (size - used) */ + +/* Number of input line we are generating output for. */ +int output_current_line; + +/* Temporary directory holding all spilled diversion files. */ +static m4_temp_dir *output_temp_dir; + + + +/*------------------------. +| Output initialization. | +`------------------------*/ + +/* Callback for comparing list elements ELT1 and ELT2 for order in + diversion_table. */ +static int +cmp_diversion_CB (const void *elt1, const void *elt2) +{ + const m4_diversion *d1 = (const m4_diversion *) elt1; + const m4_diversion *d2 = (const m4_diversion *) elt2; + /* No need to worry about overflow, since we don't create diversions + with negative divnum. */ + return d1->divnum - d2->divnum; +} + +/* Callback for comparing list element ELT against THRESHOLD. */ +static bool +threshold_diversion_CB (const void *elt, const void *threshold) +{ + const m4_diversion *div = (const m4_diversion *) elt; + /* No need to worry about overflow, since we don't create diversions + with negative divnum. */ + return div->divnum >= *(const int *) threshold; +} + +void +output_init (void) +{ + diversion_table = gl_oset_create_empty (GL_AVLTREE_OSET, cmp_diversion_CB); + div0.u.file = stdout; + output_diversion = &div0; + output_file = stdout; + obstack_init (&diversion_storage); +} + +void +output_exit (void) +{ + /* Order is important, since we may have registered cleanup_tmpfile + as an atexit handler, and it must not traverse stale memory. */ + gl_oset_t table = diversion_table; + diversion_table = NULL; + gl_oset_free (table); + obstack_free (&diversion_storage, NULL); +} + +/* Clean up any temporary directory. Designed for use as an atexit + handler, where it is not safe to call exit() recursively; so this + calls _exit if a problem is encountered. */ +static void +cleanup_tmpfile (void) +{ + /* Close any open diversions. */ + bool fail = false; + + if (diversion_table) + { + const void *elt; + gl_oset_iterator_t iter = gl_oset_iterator (diversion_table); + while (gl_oset_iterator_next (&iter, &elt)) + { + m4_diversion *diversion = (m4_diversion *) elt; + if (!diversion->size && diversion->u.file + && close_stream_temp (diversion->u.file) != 0) + { + M4ERROR ((0, errno, + "cannot clean temporary file for diversion")); + fail = true; + } + } + gl_oset_iterator_free (&iter); + } + + /* Clean up the temporary directory. */ + if (cleanup_temp_dir (output_temp_dir) != 0) + fail = true; + if (fail) + _exit (exit_failure); +} + +/* Convert DIVNUM into a temporary file name for use in m4_tmp*. */ +static const char * +m4_tmpname (int divnum) +{ + static char *buffer; + static char *tail; + if (buffer == NULL) + { + tail = xasprintf ("%s/m4-%d", output_temp_dir->dir_name, INT_MAX); + buffer = obstack_copy0 (&diversion_storage, tail, strlen (tail)); + free (tail); + tail = strrchr (buffer, '-') + 1; + } + sprintf (tail, "%d", divnum); + return buffer; +} + +/* Create a temporary file for diversion DIVNUM open for reading and + writing in a secure temp directory. The file will be automatically + closed and deleted on a fatal signal. The file can be closed and + reopened with m4_tmpclose and m4_tmpopen; when finally done with + the file, close it with m4_tmpremove. Exits on failure, so the + return value is always an open file. */ +static FILE * +m4_tmpfile (int divnum) +{ + const char *name; + FILE *file; + + if (output_temp_dir == NULL) + { + errno = 0; + output_temp_dir = create_temp_dir ("m4-", NULL, true); + if (output_temp_dir == NULL) + M4ERROR ((EXIT_FAILURE, errno, + "cannot create temporary file for diversion")); + atexit (cleanup_tmpfile); + } + name = m4_tmpname (divnum); + register_temp_file (output_temp_dir, name); + errno = 0; + file = fopen_temp (name, O_BINARY ? "wb+" : "w+"); + if (file == NULL) + { + unregister_temp_file (output_temp_dir, name); + M4ERROR ((EXIT_FAILURE, errno, + "cannot create temporary file for diversion")); + } + else if (set_cloexec_flag (fileno (file), true) != 0) + M4ERROR ((warning_status, errno, + "Warning: cannot protect diversion across forks")); + return file; +} + +/* Reopen a temporary file for diversion DIVNUM for reading and + writing in a secure temp directory. Exits on failure, so the + return value is always an open file. */ +static FILE * +m4_tmpopen (int divnum) +{ + const char *name = m4_tmpname (divnum); + FILE *file; + + errno = 0; + file = fopen_temp (name, O_BINARY ? "ab+" : "a+"); + if (file == NULL) + M4ERROR ((EXIT_FAILURE, errno, + "cannot create temporary file for diversion")); + else if (set_cloexec_flag (fileno (file), true) != 0) + M4ERROR ((warning_status, errno, + "Warning: cannot protect diversion across forks")); + return file; +} + +/* Close, but don't delete, a temporary FILE. */ +static int +m4_tmpclose (FILE *file) +{ + return close_stream_temp (file); +} + +/* Delete a closed temporary FILE for diversion DIVNUM. */ +static int +m4_tmpremove (int divnum) +{ + return cleanup_temp_file (output_temp_dir, m4_tmpname (divnum)); +} + +/*-----------------------------------------------------------------------. +| Reorganize in-memory diversion buffers so the current diversion can | +| accomodate LENGTH more characters without further reorganization. The | +| current diversion buffer is made bigger if possible. But to make room | +| for a bigger buffer, one of the in-memory diversion buffers might have | +| to be flushed to a newly created temporary file. This flushed buffer | +| might well be the current one. | +`-----------------------------------------------------------------------*/ + +static void +make_room_for (int length) +{ + int wanted_size; + m4_diversion *selected_diversion = NULL; + + /* Compute needed size for in-memory buffer. Diversions in-memory + buffers start at 0 bytes, then 512, then keep doubling until it is + decided to flush them to disk. */ + + output_diversion->used = output_diversion->size - output_unused; + + for (wanted_size = output_diversion->size; + wanted_size < output_diversion->used + length; + wanted_size = wanted_size == 0 ? INITIAL_BUFFER_SIZE : wanted_size * 2) + ; + + /* Check if we are exceeding the maximum amount of buffer memory. */ + + if (total_buffer_size - output_diversion->size + wanted_size + > MAXIMUM_TOTAL_SIZE) + { + int selected_used; + char *selected_buffer; + m4_diversion *diversion; + int count; + gl_oset_iterator_t iter; + const void *elt; + + /* Find out the buffer having most data, in view of flushing it to + disk. Fake the current buffer as having already received the + projected data, while making the selection. So, if it is + selected indeed, we will flush it smaller, before it grows. */ + + selected_diversion = output_diversion; + selected_used = output_diversion->used + length; + + iter = gl_oset_iterator (diversion_table); + while (gl_oset_iterator_next (&iter, &elt)) + { + diversion = (m4_diversion *) elt; + if (diversion->used > selected_used) + { + selected_diversion = diversion; + selected_used = diversion->used; + } + } + gl_oset_iterator_free (&iter); + + /* Create a temporary file, write the in-memory buffer of the + diversion to this file, then release the buffer. Zero the + diversion before doing anything that can exit () (including + m4_tmpfile), so that the atexit handler doesn't try to close + a garbage pointer as a file. */ + + selected_buffer = selected_diversion->u.buffer; + total_buffer_size -= selected_diversion->size; + selected_diversion->size = 0; + selected_diversion->u.file = NULL; + selected_diversion->u.file = m4_tmpfile (selected_diversion->divnum); + + if (selected_diversion->used > 0) + { + count = fwrite (selected_buffer, (size_t) selected_diversion->used, + 1, selected_diversion->u.file); + if (count != 1) + M4ERROR ((EXIT_FAILURE, errno, + "ERROR: cannot flush diversion to temporary file")); + } + + /* Reclaim the buffer space for other diversions. */ + + free (selected_buffer); + selected_diversion->used = 1; + } + + /* Reload output_file, just in case the flushed diversion was current. */ + + if (output_diversion == selected_diversion) + { + /* The flushed diversion was current indeed. */ + + output_file = output_diversion->u.file; + output_cursor = NULL; + output_unused = 0; + } + else + { + /* Close any selected file since it is not the current diversion. */ + if (selected_diversion) + { + FILE *file = selected_diversion->u.file; + selected_diversion->u.file = 0; + if (m4_tmpclose (file) != 0) + M4ERROR ((0, errno, "cannot close temporary file for diversion")); + } + + /* The current buffer may be safely reallocated. */ + output_diversion->u.buffer + = xrealloc (output_diversion->u.buffer, (size_t) wanted_size); + + total_buffer_size += wanted_size - output_diversion->size; + output_diversion->size = wanted_size; + + output_cursor = output_diversion->u.buffer + output_diversion->used; + output_unused = wanted_size - output_diversion->used; + } +} + +/*------------------------------------------------------------------------. +| Output one character CHAR, when it is known that it goes to a diversion | +| file or an in-memory diversion buffer. | +`------------------------------------------------------------------------*/ + +#define OUTPUT_CHARACTER(Char) \ + if (output_file) \ + putc ((Char), output_file); \ + else if (output_unused == 0) \ + output_character_helper ((Char)); \ + else \ + (output_unused--, *output_cursor++ = (Char)) + +static void +output_character_helper (int character) +{ + make_room_for (1); + + if (output_file) + putc (character, output_file); + else + { + *output_cursor++ = character; + output_unused--; + } +} + +/*------------------------------------------------------------------------. +| Output one TEXT having LENGTH characters, when it is known that it goes | +| to a diversion file or an in-memory diversion buffer. | +`------------------------------------------------------------------------*/ + +static void +output_text (const char *text, int length) +{ + int count; + + if (!output_file && length > output_unused) + make_room_for (length); + + if (output_file) + { + count = fwrite (text, length, 1, output_file); + if (count != 1) + M4ERROR ((EXIT_FAILURE, errno, "ERROR: copying inserted file")); + } + else + { + memcpy (output_cursor, text, (size_t) length); + output_cursor += length; + output_unused -= length; + } +} + +/*-------------------------------------------------------------------------. +| Add some text into an obstack OBS, taken from TEXT, having LENGTH | +| characters. If OBS is NULL, rather output the text to an external file | +| or an in-memory diversion buffer. If OBS is NULL, and there is no | +| output file, the text is discarded. | +| | +| If we are generating sync lines, the output have to be examined, because | +| we need to know how much output each input line generates. In general, | +| sync lines are output whenever a single input lines generates several | +| output lines, or when several input lines does not generate any output. | +`-------------------------------------------------------------------------*/ + +void +shipout_text (struct obstack *obs, const char *text, int length) +{ + static bool start_of_output_line = true; + char line[20]; + const char *cursor; + + /* If output goes to an obstack, merely add TEXT to it. */ + + if (obs != NULL) + { + obstack_grow (obs, text, length); + return; + } + + /* Do nothing if TEXT should be discarded. */ + + if (output_diversion == NULL) + return; + + /* Output TEXT to a file, or in-memory diversion buffer. */ + + if (!sync_output) + switch (length) + { + + /* In-line short texts. */ + + case 8: OUTPUT_CHARACTER (*text); text++; + case 7: OUTPUT_CHARACTER (*text); text++; + case 6: OUTPUT_CHARACTER (*text); text++; + case 5: OUTPUT_CHARACTER (*text); text++; + case 4: OUTPUT_CHARACTER (*text); text++; + case 3: OUTPUT_CHARACTER (*text); text++; + case 2: OUTPUT_CHARACTER (*text); text++; + case 1: OUTPUT_CHARACTER (*text); + case 0: + return; + + /* Optimize longer texts. */ + + default: + output_text (text, length); + } + else + for (; length-- > 0; text++) + { + if (start_of_output_line) + { + start_of_output_line = false; + output_current_line++; + +#ifdef DEBUG_OUTPUT + printf ("DEBUG: cur %d, cur out %d\n", + current_line, output_current_line); +#endif + + /* Output a `#line NUM' synchronization directive if needed. + If output_current_line was previously given a negative + value (invalidated), rather output `#line NUM "FILE"'. */ + + if (output_current_line != current_line) + { + sprintf (line, "#line %d", current_line); + for (cursor = line; *cursor; cursor++) + OUTPUT_CHARACTER (*cursor); + if (output_current_line < 1 && current_file[0] != '\0') + { + OUTPUT_CHARACTER (' '); + OUTPUT_CHARACTER ('"'); + for (cursor = current_file; *cursor; cursor++) + OUTPUT_CHARACTER (*cursor); + OUTPUT_CHARACTER ('"'); + } + OUTPUT_CHARACTER ('\n'); + output_current_line = current_line; + } + } + OUTPUT_CHARACTER (*text); + if (*text == '\n') + start_of_output_line = true; + } +} + +/* Functions for use by diversions. */ + +/*--------------------------------------------------------------------------. +| Make a file for diversion DIVNUM, and install it in the diversion table. | +| Grow the size of the diversion table as needed. | +`--------------------------------------------------------------------------*/ + +/* The number of possible diversions is limited only by memory and + available file descriptors (each overflowing diversion uses one). */ + +void +make_diversion (int divnum) +{ + m4_diversion *diversion = NULL; + + if (current_diversion == divnum) + return; + + if (output_diversion) + { + if (!output_diversion->size && !output_diversion->u.file) + { + if (!gl_oset_remove (diversion_table, output_diversion)) + error (EXIT_FAILURE, 0, "INTERNAL ERROR: make_diversion failed"); + output_diversion->u.next = free_list; + output_diversion->used = 0; + free_list = output_diversion; + } + else if (output_diversion->size) + output_diversion->used = output_diversion->size - output_unused; + else if (output_diversion->used) + { + FILE *file = output_diversion->u.file; + output_diversion->u.file = NULL; + if (m4_tmpclose (file) != 0) + M4ERROR ((0, errno, "cannot close temporary file for diversion")); + } + output_diversion = NULL; + output_file = NULL; + output_cursor = NULL; + output_unused = 0; + } + + current_diversion = divnum; + + if (divnum < 0) + return; + + if (divnum == 0) + diversion = &div0; + else + { + const void *elt; + if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB, + &divnum, &elt)) + { + m4_diversion *temp = (m4_diversion *) elt; + if (temp->divnum == divnum) + diversion = temp; + } + } + if (diversion == NULL) + { + /* First time visiting this diversion. */ + if (free_list) + { + diversion = free_list; + free_list = diversion->u.next; + } + else + { + diversion = (m4_diversion *) obstack_alloc (&diversion_storage, + sizeof *diversion); + diversion->size = 0; + diversion->used = 0; + } + diversion->u.file = NULL; + diversion->divnum = divnum; + gl_oset_add (diversion_table, diversion); + } + + output_diversion = diversion; + if (output_diversion->size) + { + output_cursor = output_diversion->u.buffer + output_diversion->used; + output_unused = output_diversion->size - output_diversion->used; + } + else + { + if (!output_diversion->u.file && output_diversion->used) + output_diversion->u.file = m4_tmpopen (output_diversion->divnum); + output_file = output_diversion->u.file; + } + output_current_line = -1; +} + +/*-------------------------------------------------------------------. +| Insert a FILE into the current output file, in the same manner | +| diversions are handled. This allows files to be included, without | +| having them rescanned by m4. | +`-------------------------------------------------------------------*/ + +void +insert_file (FILE *file) +{ + char buffer[COPY_BUFFER_SIZE]; + size_t length; + + /* Optimize out inserting into a sink. */ + + if (!output_diversion) + return; + + /* Insert output by big chunks. */ + + for (;;) + { + length = fread (buffer, 1, COPY_BUFFER_SIZE, file); + if (ferror (file)) + M4ERROR ((EXIT_FAILURE, errno, "ERROR: reading inserted file")); + if (length == 0) + break; + output_text (buffer, length); + } +} + +/*-------------------------------------------------------------------. +| Insert DIVERSION (but not div0) into the current output file. The | +| diversion is NOT placed on the expansion obstack, because it must | +| not be rescanned. When the file is closed, it is deleted by the | +| system. | +`-------------------------------------------------------------------*/ + +static void +insert_diversion_helper (m4_diversion *diversion) +{ + /* Effectively undivert only if an output stream is active. */ + if (output_diversion) + { + if (diversion->size) + output_text (diversion->u.buffer, diversion->used); + else + { + if (!diversion->u.file && diversion->used) + diversion->u.file = m4_tmpopen (diversion->divnum); + if (diversion->u.file) + { + rewind (diversion->u.file); + insert_file (diversion->u.file); + } + } + + output_current_line = -1; + } + + /* Return all space used by the diversion. */ + if (diversion->size) + { + free (diversion->u.buffer); + diversion->size = 0; + diversion->used = 0; + } + else + { + if (diversion->u.file) + { + FILE *file = diversion->u.file; + diversion->u.file = NULL; + diversion->used = 0; + if (m4_tmpclose (file) != 0) + M4ERROR ((0, errno, "cannot clean temporary file for diversion")); + } + if (m4_tmpremove (diversion->divnum) != 0) + M4ERROR ((0, errno, "cannot clean temporary file for diversion")); + } + gl_oset_remove (diversion_table, diversion); + diversion->u.next = free_list; + free_list = diversion; +} + +/*-------------------------------------------------------------------------. +| Insert diversion number DIVNUM into the current output file. The | +| diversion is NOT placed on the expansion obstack, because it must not be | +| rescanned. When the file is closed, it is deleted by the system. | +`-------------------------------------------------------------------------*/ + +void +insert_diversion (int divnum) +{ + const void *elt; + + /* Do not care about nonexistent diversions, and undiverting stdout + or self is a no-op. */ + if (divnum <= 0 || current_diversion == divnum) + return; + if (gl_oset_search_atleast (diversion_table, threshold_diversion_CB, + &divnum, &elt)) + { + m4_diversion *diversion = (m4_diversion *) elt; + if (diversion->divnum == divnum) + insert_diversion_helper (diversion); + } +} + +/*-------------------------------------------------------------------------. +| Get back all diversions. This is done just before exiting from main (), | +| and from m4_undivert (), if called without arguments. | +`-------------------------------------------------------------------------*/ + +void +undivert_all (void) +{ + const void *elt; + gl_oset_iterator_t iter = gl_oset_iterator (diversion_table); + while (gl_oset_iterator_next (&iter, &elt)) + { + m4_diversion *diversion = (m4_diversion *) elt; + if (diversion->divnum != current_diversion) + insert_diversion_helper (diversion); + } + gl_oset_iterator_free (&iter); +} + +/*-------------------------------------------------------------. +| Produce all diversion information in frozen format on FILE. | +`-------------------------------------------------------------*/ + +void +freeze_diversions (FILE *file) +{ + int saved_number; + int last_inserted; + gl_oset_iterator_t iter; + const void *elt; + + saved_number = current_diversion; + last_inserted = 0; + make_diversion (0); + output_file = file; /* kludge in the frozen file */ + + iter = gl_oset_iterator (diversion_table); + while (gl_oset_iterator_next (&iter, &elt)) + { + m4_diversion *diversion = (m4_diversion *) elt;; + if (diversion->size || diversion->u.file) + { + if (diversion->size) + fprintf (file, "D%d,%d\n", diversion->divnum, diversion->used); + else + { + struct stat file_stat; + fflush (diversion->u.file); + if (fstat (fileno (diversion->u.file), &file_stat) < 0) + M4ERROR ((EXIT_FAILURE, errno, "cannot stat diversion")); + if (file_stat.st_size < 0 + || file_stat.st_size != (unsigned long int) file_stat.st_size) + M4ERROR ((EXIT_FAILURE, 0, "diversion too large")); + fprintf (file, "D%d,%lu", diversion->divnum, + (unsigned long int) file_stat.st_size); + } + + insert_diversion_helper (diversion); + putc ('\n', file); + + last_inserted = diversion->divnum; + } + } + gl_oset_iterator_free (&iter); + + /* Save the active diversion number, if not already. */ + + if (saved_number != last_inserted) + fprintf (file, "D%d,0\n\n", saved_number); +} diff --git a/src/bin/m4/src/path.c b/src/bin/m4/src/path.c new file mode 100644 index 0000000000..634b1ec641 --- /dev/null +++ b/src/bin/m4/src/path.c @@ -0,0 +1,193 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 2004, 2006 Free Software + Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* Handling of path search of included files via the builtins "include" + and "sinclude". */ + +#include "m4.h" + +struct includes +{ + struct includes *next; /* next directory to search */ + const char *dir; /* directory */ + int len; +}; + +typedef struct includes includes; + +static includes *dir_list; /* the list of path directories */ +static includes *dir_list_end; /* the end of same */ +static int dir_max_length; /* length of longest directory name */ + + +void +include_init (void) +{ + dir_list = NULL; + dir_list_end = NULL; + dir_max_length = 0; +} + +void +include_env_init (void) +{ + char *path; + char *path_end; + char *env_path; + + if (no_gnu_extensions) + return; + + env_path = getenv ("M4PATH"); + if (env_path == NULL) + return; + + env_path = xstrdup (env_path); + path = env_path; + + do + { + path_end = strchr (path, ':'); + if (path_end) + *path_end = '\0'; + add_include_directory (path); + path = path_end + 1; + } + while (path_end); + free (env_path); +} + +void +add_include_directory (const char *dir) +{ + includes *incl; + + if (no_gnu_extensions) + return; + + if (*dir == '\0') + dir = "."; + + incl = (includes *) xmalloc (sizeof (struct includes)); + incl->next = NULL; + incl->len = strlen (dir); + incl->dir = xstrdup (dir); + + if (incl->len > dir_max_length) /* remember len of longest directory */ + dir_max_length = incl->len; + + if (dir_list_end == NULL) + dir_list = incl; + else + dir_list_end->next = incl; + dir_list_end = incl; + +#ifdef DEBUG_INCL + fprintf (stderr, "add_include_directory (%s);\n", dir); +#endif +} + +/* Search for FILE, first in `.', then according to -I options. If + successful, return the open file, and if RESULT is not NULL, set + *RESULT to a malloc'd string that represents the file found with + respect to the current working directory. */ + +FILE * +m4_path_search (const char *file, char **result) +{ + FILE *fp; + includes *incl; + char *name; /* buffer for constructed name */ + int e; + + if (result) + *result = NULL; + + /* Reject empty file. */ + if (!*file) + { + errno = ENOENT; + return NULL; + } + + /* Look in current working directory first. */ + fp = fopen (file, "r"); + if (fp != NULL) + { + if (set_cloexec_flag (fileno (fp), true) != 0) + M4ERROR ((warning_status, errno, + "Warning: cannot protect input file across forks")); + if (result) + *result = xstrdup (file); + return fp; + } + + /* If file not found, and filename absolute, fail. */ + if (*file == '/' || no_gnu_extensions) + return NULL; + e = errno; + + name = (char *) xmalloc (dir_max_length + 1 + strlen (file) + 1); + + for (incl = dir_list; incl != NULL; incl = incl->next) + { + strncpy (name, incl->dir, incl->len); + name[incl->len] = '/'; + strcpy (name + incl->len + 1, file); + +#ifdef DEBUG_INCL + fprintf (stderr, "m4_path_search (%s) -- trying %s\n", file, name); +#endif + + fp = fopen (name, "r"); + if (fp != NULL) + { + if (debug_level & DEBUG_TRACE_PATH) + DEBUG_MESSAGE2 ("path search for `%s' found `%s'", file, name); + if (set_cloexec_flag (fileno (fp), true) != 0) + M4ERROR ((warning_status, errno, + "Warning: cannot protect input file across forks")); + if (result) + *result = name; + else + free (name); + errno = e; + return fp; + } + } + free (name); + errno = e; + return fp; +} + +#ifdef DEBUG_INCL + +static void M4_GNUC_UNUSED +include_dump (void) +{ + includes *incl; + + fprintf (stderr, "include_dump:\n"); + for (incl = dir_list; incl != NULL; incl = incl->next) + fprintf (stderr, "\t%s\n", incl->dir); +} + +#endif /* DEBUG_INCL */ diff --git a/src/bin/m4/src/stackovf.c b/src/bin/m4/src/stackovf.c new file mode 100644 index 0000000000..2aae507643 --- /dev/null +++ b/src/bin/m4/src/stackovf.c @@ -0,0 +1,415 @@ +/* Detect stack overflow (when getrlimit and sigaction or sigvec are available) + Copyright (C) 1993, 1994, 2006 Free Software Foundation, Inc. + Jim Avera , October 1993. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + */ + +/* Compiled only when USE_STACKOVF is defined, which itself requires + getrlimit with the RLIMIT_STACK option, and support for alternate + signal stacks using either SVR4 or BSD interfaces. + + This should compile on ANY system which supports either sigaltstack() + or sigstack(), with or without or another way to determine + the fault address. + + There is no completely portable way to determine if a SIGSEGV signal + indicates a stack overflow. The fault address can be used to infer + this. However, the fault address is passed to the signal handler in + different ways on various systems. One of three methods are used to + determine the fault address: + + 1. The siginfo parameter (with siginfo.h, i.e., SVR4). + + 2. 4th "addr" parameter (assumed if struct sigcontext is defined, + i.e., SunOS 4.x/BSD). + + 3. None (if no method is available). This case just prints a + message before aborting with a core dump. That way the user at + least knows that it *might* be a recursion problem. + + Jim Avera writes, on Tue, 5 Oct 93 19:27 PDT: + + "I got interested finding out how a program could catch and + diagnose its own stack overflow, and ended up modifying m4 to do + this. Now it prints a nice error message and exits. + + How it works: SIGSEGV is caught using a separate signal stack. The + signal handler declares a stack overflow if the fault address is + near the end of the stack region, or if the maximum VM address + space limit has been reached. Otherwise, it returns to re-execute + the instruction with SIG_DFL set, so that any real bugs cause a + core dump as usual." + + Jim Avera writes, on Fri, 24 Jun 94 12:14 PDT: + + "The stack-overflow detection code would still be needed to avoid a + SIGSEGV abort if swap space was exhausted at the moment the stack + tried to grow. This is probably unlikely to occur with the + explicit nesting limit option of GNU m4." + + Jim Avera writes, on Wed, 6 Jul 1994 14:41 PDT: + + "When a stack overflow occurs, a SIGSEGV signal is sent, which by + default aborts the process with a core dump. + + The code in stackovf.c catches SIGSEGV using a separate signal + stack. The signal handler determines whether or not the SIGSEGV + arose from a stack overflow. If it is a stack overflow, an + external function is called (which, in m4, prints a message an + exits). Otherwise the SIGSEGV represents an m4 bug, and the signal + is re-raised with SIG_DFL set, which results in an abort and core + dump in the usual way. It seems important (to me) that internal m4 + bugs not be reported as user recursion errors, or vice-versa." */ + +#include "m4.h" /* stdlib.h, xmalloc() */ + +#include +#include +#include +#include + +#if HAVE_SIGINFO_H +# include +#endif + +#ifndef SA_RESETHAND +# define SA_RESETHAND 0 +#endif +#ifndef SA_SIGINFO +# define SA_SIGINFO 0 +#endif + +#ifndef SIGSTKSZ +# define SIGSTKSZ 8192 +#endif + +/* If the trap address is within STACKOVF_DETECT bytes of the calculated + stack limit, we diagnose a stack overflow. This must be large enough + to cover errors in our estimatation of the limit address, and to + account for the maximum size of local variables (the amount the + trapping reference might exceed the stack limit). Also, some machines + may report an arbitrary address within the same page frame. + If the value is too large, we might call some other SIGSEGV a stack + overflow, masking a bug. */ + +#ifndef STACKOVF_DETECT +# define STACKOVF_DETECT 16384 +#endif + +typedef void (*handler_t) (void); + +static const char *stackbot; +static const char *stackend; +static const char *arg0; +static handler_t stackovf_handler; + +/* The following OS-independent procedure is called from the SIGSEGV + signal handler. The signal handler obtains information about the trap + in an OS-dependent manner, and passes a parameter with the meanings as + explained below. + + If the OS explicitly identifies a stack overflow trap, either pass + PARAM_STACKOVF if a stack overflow, or pass PARAM_NOSTACKOVF if not + (id est, it is a random bounds violation). Otherwise, if the fault + address is available, pass the fault address. Otherwise (if no + information is available), pass NULL. + + Not given an explicit indication, we compare the fault address with + the estimated stack limit, and test to see if overall VM space is + exhausted. + + If a stack overflow is identified, then the external *stackovf_handler + function is called, which should print an error message and exit. If + it is NOT a stack overflow, then we silently abort with a core dump by + returning to re-raise the SIGSEGV with SIG_DFL set. If indeterminate, + then we do not call *stackovf_handler, but instead print an ambiguous + message and abort with a core dump. This only occurs on systems which + provide no information, but is better than nothing. */ + +#define PARAM_STACKOVF ((const char *) (1 + STACKOVF_DETECT)) +#define PARAM_NOSTACKOVF ((const char *) (2 + STACKOVF_DETECT)) + +static void +process_sigsegv (int signo, const char *p) +{ + long diff; + diff = (p - stackend); + +#ifdef DEBUG_STKOVF + { + char buf[140]; + + sprintf (buf, "process_sigsegv: p=%#lx stackend=%#lx diff=%ld bot=%#lx\n", + (long) p, (long) stackend, (long) diff, (long) stackbot); + write (2, buf, strlen (buf)); + } +#endif + + if (p != PARAM_NOSTACKOVF) + { + if ((long) sbrk (8192) == (long) -1) + { + + /* sbrk failed. Assume the RLIMIT_VMEM prevents expansion even + if the stack limit has not been reached. */ + + write (2, "VMEM limit exceeded?\n", 21); + p = PARAM_STACKOVF; + } + if (diff >= -STACKOVF_DETECT && diff <= STACKOVF_DETECT) + { + + /* The fault address is "sufficiently close" to the stack lim. */ + + p = PARAM_STACKOVF; + } + if (p == PARAM_STACKOVF) + { + + /* We have determined that this is indeed a stack overflow. */ + + (*stackovf_handler) (); /* should call exit() */ + } + } + if (p == NULL) + { + const char *cp; + + cp = "\ +Memory bounds violation detected (SIGSEGV). Either a stack overflow\n\ +occurred, or there is a bug in "; + write (2, cp, strlen (cp)); + write (2, arg0, strlen (arg0)); + cp = ". Check for possible infinite recursion.\n"; + write (2, cp, strlen (cp)); + } + + /* Return to re-execute the instruction which caused the trap with + SIGSEGV set to SIG_DFL. An abort with core dump should occur. */ + + signal (signo, SIG_DFL); +} + +#if HAVE_STRUCT_SIGACTION_SA_SIGACTION + +/* POSIX. */ + +static void +sigsegv_handler (int signo, siginfo_t *ip, void *context) +{ + process_sigsegv + (signo, (ip != NULL + && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL)); +} + +#elif HAVE_SIGINFO_T + +/* SVR4. */ + +static void +sigsegv_handler (int signo, siginfo_t *ip) +{ + process_sigsegv + (signo, (ip != NULL + && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL)); +} + +#elif HAVE_SIGCONTEXT + +/* SunOS 4.x (and BSD?). (not tested) */ + +static void +sigsegv_handler (int signo, int code, struct sigcontext *scp, char *addr) +{ + process_sigsegv (signo, addr); +} + +#else /* not HAVE_SIGCONTEXT */ + +/* OS provides no information. */ + +static void +sigsegv_handler (int signo) +{ + process_sigsegv (signo, NULL); +} + +#endif /* not HAVE_SIGCONTEXT */ + +/* Arrange to trap a stack-overflow and call a specified handler. The + call is on a dedicated signal stack. + + argv and envp are as passed to main(). + + If a stack overflow is not detected, then the SIGSEGV is re-raised + with action set to SIG_DFL, causing an abort and coredump in the usual + way. + + Detection of a stack overflow depends on the trap address being near + the stack limit address. The stack limit can not be directly + determined in a portable way, but we make an estimate based on the + address of the argv and environment vectors, their contents, and the + maximum stack size obtained using getrlimit. */ + +void +setup_stackovf_trap (char *const *argv, char *const *envp, handler_t handler) +{ + struct rlimit rl; + rlim_t stack_len; + int grows_upward; + register char *const *v; + register char *p; +#if HAVE_SIGACTION && defined SA_ONSTACK + struct sigaction act; +#elif HAVE_SIGVEC && defined SV_ONSTACK + struct sigvec vec; +#else + +Error - Do not know how to set up stack-ovf trap handler... + +#endif + + arg0 = argv[0]; + stackovf_handler = handler; + + /* Calculate the approximate expected addr for a stack-ovf trap. */ + + if (getrlimit (RLIMIT_STACK, &rl) < 0) + error (EXIT_FAILURE, errno, "getrlimit"); + stack_len = (rl.rlim_cur < rl.rlim_max ? rl.rlim_cur : rl.rlim_max); + stackbot = (char *) argv; + grows_upward = ((char *) &stack_len > stackbot); + if (grows_upward) + { + + /* Grows toward increasing addresses. */ + + for (v = argv; (p = (char *) *v) != NULL; v++) + { + if (p < stackbot) + stackbot = p; + } + if ((char *) envp < stackbot) + stackbot = (char *) envp; + for (v = envp; (p = (char *) *v) != NULL; v++) + { + if (p < stackbot) + stackbot = p; + } + stackend = stackbot + stack_len; + } + else + { + + /* The stack grows "downward" (toward decreasing addresses). */ + + for (v = argv; (p = (char *) *v) != NULL; v++) + { + if (p > stackbot) + stackbot = p; + } + if ((char *) envp > stackbot) + stackbot = (char *) envp; + for (v = envp; (p = (char *) *v) != NULL; v++) + { + if (p > stackbot) + stackbot = p; + } + stackend = stackbot - stack_len; + } + + /* Allocate a separate signal-handler stack. */ + +#if HAVE_SIGALTSTACK && (HAVE_SIGINFO_T || ! HAVE_SIGSTACK) + + /* Use sigaltstack only if siginfo_t is available, unless there is no + choice. */ + + { + stack_t ss; + + ss.ss_size = SIGSTKSZ; + ss.ss_sp = xmalloc ((unsigned) ss.ss_size); + ss.ss_flags = 0; + if (sigaltstack (&ss, NULL) < 0) + { + /* Oops - sigaltstack exists but doesn't work. We can't + install the overflow detector, but should gracefully treat + it as though sigaltstack doesn't exist. For example, this + happens when compiled with Linux 2.1 headers but run + against Linux 2.0 kernel. */ + free (ss.ss_sp); + if (errno == ENOSYS) + return; + error (EXIT_FAILURE, errno, "sigaltstack"); + } + } + +#elif HAVE_SIGSTACK + + { + struct sigstack ss; + char *stackbuf = xmalloc (2 * SIGSTKSZ); + + ss.ss_sp = stackbuf + SIGSTKSZ; + ss.ss_onstack = 0; + if (sigstack (&ss, NULL) < 0) + { + /* Oops - sigstack exists but doesn't work. We can't install + the overflow detector, but should gracefully treat it as + though sigstack doesn't exist. For example, this happens + when compiled with Linux 2.1 headers but run against Linux + 2.0 kernel. */ + free (stackbuf); + if (errno == ENOSYS) + return; + error (EXIT_FAILURE, errno, "sigstack"); + } + } + +#else /* not HAVE_SIGSTACK */ + +Error - Do not know how to set up stack-ovf trap handler... + +#endif /* not HAVE_SIGSTACK */ + + /* Arm the SIGSEGV signal handler. */ + +#if HAVE_SIGACTION && defined SA_ONSTACK + + sigaction (SIGSEGV, NULL, &act); +# if HAVE_STRUCT_SIGACTION_SA_SIGACTION + act.sa_sigaction = sigsegv_handler; +# else /* ! HAVE_STRUCT_SIGACTION_SA_SIGACTION */ + act.sa_handler = (RETSIGTYPE (*) (int)) sigsegv_handler; +# endif /* ! HAVE_STRUCT_SIGACTION_SA_SIGACTION */ + sigemptyset (&act.sa_mask); + act.sa_flags = (SA_ONSTACK | SA_RESETHAND | SA_SIGINFO); + if (sigaction (SIGSEGV, &act, NULL) < 0) + error (EXIT_FAILURE, errno, "sigaction"); + +#else /* ! HAVE_SIGACTION */ + + vec.sv_handler = (RETSIGTYPE (*) (int)) sigsegv_handler; + vec.sv_mask = 0; + vec.sv_flags = (SV_ONSTACK | SV_RESETHAND); + if (sigvec (SIGSEGV, &vec, NULL) < 0) + error (EXIT_FAILURE, errno, "sigvec"); + +#endif /* ! HAVE_SIGACTION */ + +} diff --git a/src/bin/m4/src/symtab.c b/src/bin/m4/src/symtab.c new file mode 100644 index 0000000000..4fadf884ac --- /dev/null +++ b/src/bin/m4/src/symtab.c @@ -0,0 +1,403 @@ +/* GNU m4 -- A simple macro processor + + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2003, 2006 Free + Software Foundation, Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +/* This file handles all the low level work around the symbol table. The + symbol table is a simple chained hash table. Each symbol is described + by a struct symbol, which is placed in the hash table based upon the + symbol name. Symbols that hash to the same entry in the table are + kept on a list, sorted by name. As a special case, to facilitate the + "pushdef" and "popdef" builtins, a symbol can be several times in the + symbol table, one for each definition. Since the name is the same, + all the entries for the symbol will be on the same list, and will + also, because the list is sorted, be adjacent. All the entries for a + name are simply ordered on the list by age. The current definition + will then always be the first found. */ + +#include "m4.h" +#include + +#ifdef DEBUG_SYM +/* When evaluating hash table performance, this profiling code shows + how many collisions were encountered. */ + +struct profile +{ + int entry; /* Number of times lookup_symbol called with this mode. */ + int comparisons; /* Number of times strcmp was called. */ + int misses; /* Number of times strcmp did not return 0. */ + long long bytes; /* Number of bytes compared. */ +}; + +static struct profile profiles[5]; +static symbol_lookup current_mode; + +/* On exit, show a profile of symbol table performance. */ +static void +show_profile (void) +{ + int i; + for (i = 0; i < 5; i++) + { + fprintf(stderr, "m4: lookup mode %d called %d times, %d compares, " + "%d misses, %lld bytes\n", + i, profiles[i].entry, profiles[i].comparisons, + profiles[i].misses, profiles[i].bytes); + } +} + +/* Like strcmp (S1, S2), but also track profiling statistics. */ +static int +profile_strcmp (const char *s1, const char *s2) +{ + int i = 1; + int result; + while (*s1 && *s1 == *s2) + { + s1++; + s2++; + i++; + } + result = (unsigned char) *s1 - (unsigned char) *s2; + profiles[current_mode].comparisons++; + if (result != 0) + profiles[current_mode].misses++; + profiles[current_mode].bytes += i; + return result; +} + +# define strcmp profile_strcmp +#endif /* DEBUG_SYM */ + + +/*----------------------------------------------------------------------. +| Initialise the symbol table, by allocating the necessary storage, and | +| zeroing all the entries. | +`----------------------------------------------------------------------*/ + +/* Pointer to symbol table. */ +symbol **symtab; + +void +symtab_init (void) +{ + size_t i; + symbol **s; + + s = symtab = (symbol **) xnmalloc (hash_table_size, sizeof (symbol *)); + + for (i = 0; i < hash_table_size; i++) + s[i] = NULL; + +#ifdef DEBUG_SYM + { + int e = atexit(show_profile); + if (e != 0) + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: unable to show symtab profile")); + } +#endif /* DEBUG_SYM */ +} + +/*--------------------------------------------------. +| Return a hashvalue for a string, from GNU-emacs. | +`--------------------------------------------------*/ + +static size_t +hash (const char *s) +{ + register size_t val = 0; + + register const char *ptr = s; + register char ch; + + while ((ch = *ptr++) != '\0') + val = (val << 7) + (val >> (sizeof (val) * CHAR_BIT - 7)) + ch; + return val; +} + +/*--------------------------------------------. +| Free all storage associated with a symbol. | +`--------------------------------------------*/ + +void +free_symbol (symbol *sym) +{ + if (SYMBOL_PENDING_EXPANSIONS (sym) > 0) + SYMBOL_DELETED (sym) = true; + else + { + free (SYMBOL_NAME (sym)); + if (SYMBOL_TYPE (sym) == TOKEN_TEXT) + free (SYMBOL_TEXT (sym)); + free (sym); + } +} + +/*-------------------------------------------------------------------. +| Search in, and manipulation of the symbol table, are all done by | +| lookup_symbol (). It basically hashes NAME to a list in the | +| symbol table, and searches this list for the first occurrence of a | +| symbol with the name. | +| | +| The MODE parameter determines what lookup_symbol () will do. It | +| can either just do a lookup, do a lookup and insert if not | +| present, do an insertion even if the name is already in the list, | +| delete the first occurrence of the name on the list, or delete all | +| occurrences of the name on the list. | +`-------------------------------------------------------------------*/ + +symbol * +lookup_symbol (const char *name, symbol_lookup mode) +{ + size_t h; + int cmp = 1; + symbol *sym, *prev; + symbol **spp; + +#if DEBUG_SYM + current_mode = mode; + profiles[mode].entry++; +#endif /* DEBUG_SYM */ + + h = hash (name); + sym = symtab[h % hash_table_size]; + + for (prev = NULL; sym != NULL; prev = sym, sym = sym->next) + { + cmp = strcmp (SYMBOL_NAME (sym), name); + if (cmp >= 0) + break; + } + + /* If just searching, return status of search. */ + + if (mode == SYMBOL_LOOKUP) + return cmp == 0 ? sym : NULL; + + /* Symbol not found. */ + + spp = (prev != NULL) ? &prev->next : &symtab[h % hash_table_size]; + + switch (mode) + { + + case SYMBOL_INSERT: + + /* If the name was found in the table, check whether it is still in + use by a pending expansion. If so, replace the table element with + a new one; if not, just return the symbol. If not found, just + insert the name, and return the new symbol. */ + + if (cmp == 0 && sym != NULL) + { + if (SYMBOL_PENDING_EXPANSIONS (sym) > 0) + { + symbol *old = sym; + SYMBOL_DELETED (old) = true; + + sym = (symbol *) xmalloc (sizeof (symbol)); + SYMBOL_TYPE (sym) = TOKEN_VOID; + SYMBOL_TRACED (sym) = SYMBOL_TRACED (old); + SYMBOL_NAME (sym) = xstrdup (name); + SYMBOL_SHADOWED (sym) = false; + SYMBOL_MACRO_ARGS (sym) = false; + SYMBOL_BLIND_NO_ARGS (sym) = false; + SYMBOL_DELETED (sym) = false; + SYMBOL_PENDING_EXPANSIONS (sym) = 0; + + SYMBOL_NEXT (sym) = SYMBOL_NEXT (old); + SYMBOL_NEXT (old) = NULL; + (*spp) = sym; + } + return sym; + } + /* Fall through. */ + + case SYMBOL_PUSHDEF: + + /* Insert a name in the symbol table. If there is already a symbol + with the name, insert this in front of it, and mark the old + symbol as "shadowed". */ + + sym = (symbol *) xmalloc (sizeof (symbol)); + SYMBOL_TYPE (sym) = TOKEN_VOID; + SYMBOL_TRACED (sym) = false; + SYMBOL_NAME (sym) = xstrdup (name); + SYMBOL_SHADOWED (sym) = false; + SYMBOL_MACRO_ARGS (sym) = false; + SYMBOL_BLIND_NO_ARGS (sym) = false; + SYMBOL_DELETED (sym) = false; + SYMBOL_PENDING_EXPANSIONS (sym) = 0; + + SYMBOL_NEXT (sym) = *spp; + (*spp) = sym; + + if (mode == SYMBOL_PUSHDEF && cmp == 0) + { + SYMBOL_SHADOWED (SYMBOL_NEXT (sym)) = true; + SYMBOL_TRACED (sym) = SYMBOL_TRACED (SYMBOL_NEXT (sym)); + } + return sym; + + case SYMBOL_DELETE: + case SYMBOL_POPDEF: + + /* Delete occurrences of symbols with NAME. SYMBOL_DELETE kills + all definitions, SYMBOL_POPDEF kills only the first. + However, if the last instance of a symbol is marked for + tracing, reinsert a placeholder in the table. And if the + definition is still in use, let the caller free the memory + after it is done with the symbol. */ + + if (cmp != 0 || sym == NULL) + return NULL; + { + bool traced = false; + if (SYMBOL_NEXT (sym) != NULL + && SYMBOL_SHADOWED (SYMBOL_NEXT (sym)) + && mode == SYMBOL_POPDEF) + { + SYMBOL_SHADOWED (SYMBOL_NEXT (sym)) = false; + SYMBOL_TRACED (SYMBOL_NEXT (sym)) = SYMBOL_TRACED (sym); + } + else + traced = SYMBOL_TRACED (sym); + do + { + *spp = SYMBOL_NEXT (sym); + free_symbol (sym); + sym = *spp; + } + while (*spp != NULL && SYMBOL_SHADOWED (*spp) + && mode == SYMBOL_DELETE); + if (traced) + { + sym = (symbol *) xmalloc (sizeof (symbol)); + SYMBOL_TYPE (sym) = TOKEN_VOID; + SYMBOL_TRACED (sym) = true; + SYMBOL_NAME (sym) = xstrdup (name); + SYMBOL_SHADOWED (sym) = false; + SYMBOL_MACRO_ARGS (sym) = false; + SYMBOL_BLIND_NO_ARGS (sym) = false; + SYMBOL_DELETED (sym) = false; + SYMBOL_PENDING_EXPANSIONS (sym) = 0; + + SYMBOL_NEXT (sym) = *spp; + (*spp) = sym; + } + } + return NULL; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: invalid mode to symbol_lookup ()")); + abort (); + } +} + +/*-----------------------------------------------------------------. +| The following function is used for the cases where we want to do | +| something to each and every symbol in the table. The function | +| hack_all_symbols () traverses the symbol table, and calls a | +| specified function FUNC for each symbol in the table. FUNC is | +| called with a pointer to the symbol, and the DATA argument. | +| | +| FUNC may safely call lookup_symbol with mode SYMBOL_POPDEF or | +| SYMBOL_LOOKUP, but any other mode can break the iteration. | +`-----------------------------------------------------------------*/ + +void +hack_all_symbols (hack_symbol *func, void *data) +{ + size_t h; + symbol *sym; + symbol *next; + + for (h = 0; h < hash_table_size; h++) + { + /* We allow func to call SYMBOL_POPDEF, which can invalidate + sym, so we must grab the next element to traverse before + calling func. */ + for (sym = symtab[h]; sym != NULL; sym = next) + { + next = SYMBOL_NEXT (sym); + func (sym, data); + } + } +} + +#ifdef DEBUG_SYM + +static void symtab_print_list (int i); + +static void M4_GNUC_UNUSED +symtab_debug (void) +{ + token_data td; + const char *text; + symbol *s; + int delete; + static int i; + + while (next_token (&td) == TOKEN_WORD) + { + text = TOKEN_DATA_TEXT (&td); + if (*text == '_') + { + delete = 1; + text++; + } + else + delete = 0; + + s = lookup_symbol (text, SYMBOL_LOOKUP); + + if (s == NULL) + printf ("Name `%s' is unknown\n", text); + + if (delete) + (void) lookup_symbol (text, SYMBOL_DELETE); + else + (void) lookup_symbol (text, SYMBOL_INSERT); + } + symtab_print_list (i++); +} + +static void +symtab_print_list (int i) +{ + symbol *sym; + size_t h; + + printf ("Symbol dump #%d:\n", i); + for (h = 0; h < hash_table_size; h++) + for (sym = symtab[h]; sym != NULL; sym = sym->next) + printf ("\tname %s, bucket %lu, addr %p, next %p, " + "flags%s%s%s, pending %d\n", + SYMBOL_NAME (sym), + (unsigned long int) h, sym, SYMBOL_NEXT (sym), + SYMBOL_TRACED (sym) ? " traced" : "", + SYMBOL_SHADOWED (sym) ? " shadowed" : "", + SYMBOL_DELETED (sym) ? " deleted" : "", + SYMBOL_PENDING_EXPANSIONS (sym)); +} + +#endif /* DEBUG_SYM */