uucp-1.07/0000777000076400007640000000000007665533774006220 5uucp-1.07/README0000664000076400007640000002070107665321754007007 This is the README file for version 1.07 of the Taylor UUCP package. It was written by Ian Lance Taylor. I can be reached at ian@airs.com. There is a mailing list for discussion of the package. The list is hosted by Eric Schnoebelen at cirr.com. To join (or get off) the list, send mail to taylor-uucp-request@gnu.org. Mail to this address is answered by the majordomo program. To join the list, send the message ``subscribe ADDRESS'' where ADDRESS is your e-mail address. To send a message to the list, send it to taylor-uucp@gnu.org. There is an archive of all messages sent to the mailing list at http://lists.cirr.com. This package is covered by the Gnu Public License. See the file COPYING for details. If you would like to do something with this package that you feel is reasonable but you feel is prohibited by the license, contact me to see if we can work it out. The most recent version may be obtained from any Gnu archive site. The canonical site is prep.ai.mit.edu. There are many mirror sites, including ftp.uu.net and wuarchive.wustl.edu. WHAT IT IS This is the complete source code for a Unix UUCP package. It provides everything you need to make a UUCP connection. It includes versions of uucico, uusched, uuxqt, uux, uucp, uustat, uulog, uuname, uuto, uupick, and cu, as well as uuchk (a program to check configuration files), uuconv (a program to convert from one type of configuration file to another) and tstuu (a test harness for the package). This is the standard UUCP package of the Free Software Foundation. The package currently supports the 'f', 'g' (in all window and packet sizes), 'G', 't' and 'e' protocols, as well a Zmodem protocol, the FX UUCICO 'y' protocol, and two new bidirectional protocols. If you have a Berkeley sockets library, it can make TCP connections. If you have TLI libraries, it can make TLI connections. It supports a new configuration file mechanism which I like (but other people dislike). The package has a few advantages over regular UUCP: You get the source code. It uses significantly less CPU time than many UUCP packages. You can specify a chat script to run when a system calls in, allowing adjustment of modem parameters on a per system basis. You can specify failure strings for chat scripts, allowing the chat script to fail immediately if the modem returns ``BUSY''. If you are talking to another instance of the package, you can use the new bidirectional protocol for rapid data transfer in both directions at once. You can also restrict file transfers by size based on the time of day and who placed the call. On the other hand: It only runs on Unix. The code is carefully divided into system dependent and system independent portions, so it should be possible to port it to other systems. It would not be trivial. You don't get uuclean, uusend, uuq, uusnap, uumonitor, uutry, uupoll, etc. If you have current copies of these programs, you may be able to use them. Shell scripts versions of uuclean and uutry are provided, with most, if not all, of the functionality of the usual programs. I believe the supplied uustat program allows you to do everything that uuq, uusnap and uumonitor do. uupoll could be written as a shell script. The package does not read modemcap or acucap files, although you can use V2 configuration files with a BNU Dialers file or a dialer file written in my new configuration file format. The package cannot use SCO dialer programs directly, although it can with a simple shell script interface. If you start using this package, I suggest that you join the mailing list (see above) to keep up to date on patches and new versions. I am also open to suggestions for improvements and modifications. DOCUMENTATION The documentation is in the file uucp.texi, which is a Texinfo file. Texinfo is a format used by the Free Software Foundation. You can print the documentation using TeX in combination with the file texinfo.tex. See the TODO file for things which should be done. Please feel free to do them, although you may want to check with me first. Send me suggestions for new things to do. The compilation instructions are in uucp.texi. Here is a summary. Type ``sh configure''. You can pass a number of arguments in the environment (using bash or sh, enter something like ``CC=gcc configure''; using csh, enter something like ``setenv CC gcc; sh configure''): CC: C compiler to use; default is gcc if it exists, else cc CFLAGS: Flags to pass to $CC when compiling; default -g LDFLAGS: Flags to pass to $CC when only linking; default none LIBS: Library arguments to pass to $CC; default none INSTALL: Install program; default install -c or cp The configure script will compile a number of test programs to see what is available on your system, so if your system is at all unusual you will need to pass in $CC and $LIBS correctly. You can also pass other arguments on the command line. Use configure --help for a complete list. Of particular interest: --prefix=DIRNAME The directory under which all files are installed. Default /usr/local. --with-newconfigdir=DIRNAME The directory in which to find new style configuration files. Default PREFIX/conf/uucp. --with-oldconfigdir=DIRNAME The directory in which to find old style configuration files. Default /usr/lib/uucp The configure script will create config.h from config.h.in and Makefile from Makefile.in. It will also create config.status, which is a shell script which actually creates the files. Please report any configuration problems, so that they can be fixed in later versions. Igor V. Semenyuk provided this (lightly edited) note about ISC Unix 3.0. The configure script will default to passing -posix to gcc. However, using -posix changes the environment to POSIX, and on ISC 3.0, at least, the default for POSIX_NO_TRUNC is 1. This means nothing for uucp, but can lead to a problem when uuxqt executes rmail. IDA sendmail has dbm configuration files named mailertable.{dir,pag}. Notice these names are 15 characters long. When uuxqt compiled with -posix executes rmail, which in turn executes sendmail, the later is run under POSIX environment too! This leads to sendmail bombing out with 'error opening 'M' database: name too long' (mailertable.dir). It's rather obscure behaviour, and it took me a day to find out the cause. I don't use -posix, instead I run gcc with -D_POSIX_SOURCE, and add -lcposix to LIBS. On some versions of BSDI there is a bug in the shell which causes the default value for CFLAGS to be set incorrectly. If ``echo ${CFLAGS--g}'' echoes ``g'' rather than ``-g'', then you must set CFLAGS in the environment before running configure. There is a patch available from BSDI for this bug. (Reported by David Vrona). On AIX 3.2.5, and possibly other versions, cc -E does not work, reporting ``Option NOROCONST is not valid.'' Test this before running configure by doing something like touch /tmp/foo.c cc -E /tmp/foo.c This may give a warning about the file being empty, but it should not give the ``Option NOROCONST'' warning. The workaround is to remove the ",noroconst" entry from the "options" clause in the "cc" stanza in /etc/xlc.cfg. (Reported by Chris Lewis). Examine config.h and Makefile to make sure they're right. Edit policy.h for your local system. Type ``make''. Use ``uuchk'' to check configuration files. You can use ``uuconv'' to convert between configuration file formats. Type ``make install'' to install. Note that by default the programs are compiled with debugging information, and they are not stripped when they are installed. Read the man page for strip for more information. On older System V based systems which do not have the setreuid system call, problems may arise if ordinary users can start an execution of uuxqt, perhaps indirectly via uucp or uux. UUCP jobs may wind up executing with a real user ID of the user who invoked uuxqt, which can cause problems if the UUCP job checks the real user ID for security purposes. On such systems, it is safest to put ``run-uuxqt never'' in the `config' file, so that uucico never starts uuxqt, and invoke uuxqt directly from cron. uucp-1.07/stamp-h.in0000664000076400007640000000001207665532201010010 timestamp uucp-1.07/AUTHORS0000664000076400007640000000037307665321754007202 Taylor UUCP was written by Ian Lance Taylor . Many people have contributed patches; see the ChangeLog file. Particularly large or significant patches came from Chip Salzenberg, Francois Pinard, Doug Evans, Marc Boucher, and Jorge Cwik. uucp-1.07/COPYING0000664000076400007640000004311007665321754007161 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General Public License instead of this License. uucp-1.07/ChangeLog0000664000076400007640000062166107665533732007716 2003-05-29 Ian Lance Taylor * Released version 1.07. * Makefile.am (dist-hook): New target. * Makefile.in: Rebuild. 2003-05-28 Ian Lance Taylor * uucico.c (uhelp): Make capitalization consistent. * proty.c: Add prototype declaration for fyxchg_syncs. 2002-03-07 Ian Lance Taylor * configure.in: Substitute NEWCONFIGDIR and OLDCONFIGDIR rather than defining them. * uuconf/Makefile.am (AM_CFLAGS): Define NEWCONFIGLIB and OLDCONFIGLIB. * configure, config.h.in, Makefile.in, */Makefile.in: Rebuild. 2002-03-06 Ian Lance Taylor * uucp.texi (Compilation): Use @option in table of configure options. (Configuration Files): The newconfigdir variable is now set using a configure option. * configure.in: Don't assume that ac_cv_func_getline will be defined, since we no longer always test for the presence of getline. * configure, config.h.in: Rebuild. 2002-03-05 Ian Lance Taylor * Released beta version 1.07. * configure.in: Don't define SBINDIR. * unix/Makefile.am (AM_CFLAGS): Define SBINDIR. * configure, config.h.in, unix/Makefile.in: Rebuild. * Makefile.am (EXTRA_DIST): Define. * Makefile.in: Rebuild. * Makefile.am (UUHEADERS): Add sysdep.h. * unix/Makefile.am (libunix_a_SOURCES): Add fsusg.h. * uuconf/Makefile.am (libuuconf_a_SOURCES): Add alloc.h, syshdr.h, and uucnfi.h. * Makefile.in, unix/Makefile.in, uuconf/Makefile.in: Rebuild. * uucico.c (fcall): If a port was specified on the command line, and the current alternate does not use that port, but a later alternate does, then skip to the later alternate. * uucp.texi: Use new Texinfo commands @command, @option, @email, and @url. * uucp.texi: Update version number to 1.07. * uucico.8: Don't specify debugging file location, just mention uulog -D. * uuxqt.8: Likewise. * uux.1: Mention /var/spool/uucppublic as possible public directory location. * cu.1: Remove FILES section. Update version number to 1.07. * uucp.1: Likewise. * uustat.1: Likewise. * uux.1: Likewise. * uucico.8: Likewise. * uuxqt.8: Likewise. * policy.h: Add some comments for modern systems. (HAVE_UNBLOCKED_WRITES): Default to 1. 2002-03-02 Ian Lance Taylor * trans.h (FEATURE_QUOTES): Define. * lib/quote.c: New file. * lib/quotes.c: New file. * lib/parse.c (ulunquote_cmd): New static function. (fparse_cmd): Call it before returning. * unix/splcmd.c (zsysdep_spool_commands): Quote command if necessary. Remove whitespace check. * send.c (flocal_send_request): Quote command if necessary. (fsend_exec_file_init): Quote execution file if necessary. (usadd_exec_line): Add fquote parameter. * rec.c (flocal_rec_send_request): Quote command if necessary. (frec_file_end): Quote execution file if necessary. * xcmd.c (flocal_xcmd_request): Quote command if necessary. * uucico.c (fdo_call): Add FEATURE_QUOTES to -N option. (faccept_call): Add FEATURE_QUOTES to ROKN argument. * uuxqt.c (fQquoted): New static variable. (asQcmds): Add "Q". (uqdo_xqt_file): Initialize and check fQquoted. * uux.c (fXquote, fXquote_output): New static variables. (main): Don't check for whitespace in notification address--revert patch of 2002-02-16. Set fXquote if quoting is needed. (uxadd_xqt_line): If fXquote, quote strings. * uucp.c (main): Don't check for whitespace in notification address--revert patch of 2002-02-16. * uudefs.h: Declare fparse_cmd, fcmd_needs_quotes, uquote_cmd, ufree_quoted_cmd, and zquote_cmd_string. * uucp.texi (Execution File Format): Document "Q" command. (The Initial Handshake): Document feature bitmask 040-- FEATURE_QUOTES. (UUCP Protocol Commands): Document -q option for all commands. * lib/Makefile.am (libuucp_a_SOURCES): Add quote.c and quotes.c. * lib/Makefile.in: Rebuild. 2002-02-26 Ian Lance Taylor * Peter Rye: contrib/xchat.c: (do_script): Correct brace placement. * contrib/xc-conf.h-dist: Include "../config.h" rather than "../conf.h". * contrib/Ringback.Hayes: New file. * uuconf.h (struct uuconf_tcp_port): Add uuconf_iversion field. * unix/tcp.c (ftcp_set_hints): New static function. (ftcp_open): Use ftcp_set_hints. Check version when falling back to IPv4 code. Use IPV6_BINDV6ONLY option if it is defined. (ftcp_dial): Use ftcp_set_hints. Check version when falling back to IPv4 code. * uuconf/tportc.c (asPtcp_cmds): Add "version". (_uuconf_iport_cmd): Initialize uuconf_iversion field. * uuconf/hport.c (uuconf_hdb_find_port): Initialize uuconf_iversion field. * uuconf/vsinfo.c (_uuconf_iv2_system_internal): Likewise. * uuchk.c (ikshow_port): Report TCP port version number. * uucp.texi (port File): Document new "version" command. 2002-02-24 Ian Lance Taylor * conn.c (fconn_lock): Add fuser parameter. Change all callers. (fconn_open): Likewise. * conn.h (struct sconncmds): Add fuser parameter to pflock and pfopen. (fconn_lock, fconn_open): Update declarations. * cu.c (struct sconninfo): Add fdirect field. (main): Initialize sinfo.fdirect. * unix/pipe.c (fspipe_open): Add fuser parameter. * unix/serial.c (fsserial_lock, fsserial_open): Likewise. (fsstdin_open, fsmodem_open, fsdirect_open): Likewise. * unix/tcp.c (ftcp_open): Likewise. * unix/tli.c (ftli_open): Likewise. * uux.c: Include if available. Define macros from if not defined. Change all calls to exit to use EX_* macros. Change all calls to fsysdep_exit to use exit. (main): Pass uxfatal rather than uxabort to ulog_fatal_fn. Do it at the start of the function. (uxfatal): New static function. (uxabort): Add istat parameter. Change all callers. * unix/splcmd.c (zsysdep_spool_commands): Add pftemp parameter. Change call callers. * system.h (zsysdep_spool_commands): Update declaration. * uucp.texi (uux Description): Document uux exit status. * uux.1: Likewise. * unix/walk.c (iswalk_dir): Mark qstat as unused. 2002-02-22 Ian Lance Taylor * uucp.c (uccopy): When doing a local copy, copy the file mode as well. * configure.in: Only check for getline if getdelim is present. * configure: Rebuild. * unix/serial.c (asSbaud_table): Add more entries. * uucp.c (uchelp): Correct text: -u is like --user, not --usage. * John Hughes: uucp.c (asClongopts): Correct --noexpand to -W. * uuto.in: Add support for --version and --help options. * Makefile.am (uuto): Substitute version number for @VERS@. * Makefile.in: Rebuild. * Marcus Shang: unix/xqtsub.c (fsysdep_execute): If the output file is in the spool directory, create subdirectories if necessary. * uucico.c (fconn_call): If all matching ports are in use, set system status to STATUS_PORT_FAILED. * uuchk.c (ukhelp): Update copyright. Add bug reporting address. * uucico.c (uhelp): Likewise. * uucp.c (uchelp): Likewise. * uulog.c (ulhelp): Likewise. * uuname.c (unhelp): Likewise. * uustat.c (ushelp): Likewise. * uux.c (uxhelp): Likewise. * uuxqt.c (uqhelp): Likewise. * cu.c (ucuhelp): Likewise. Output to stdout. * uuconv.c (uvhelp): Likewise. * uupick.c (uphelp): Likewise. * uuchk.c (main): Update version statement to current GNU standard. Update copyright in version statement. * uucico.c (main): Likewise. * uucp.c (main): Likewise. * uulog.c (main): Likewise. * uuname.c (main): Likewise. * uustat.c (main): Likewise. * uux.c (main): Likewise. * uuxqt.c (main): Likewise. * cu.c (main): Likewise. Output version to stdout. * uuconv.c (main): Likewise. * uupick.c (main): Likewise. * Tim Pozar: uucp.texi (Acknowledgements): Note that gnuucp was based on uuslave. * Michael Ju. Tokarev: unix/serial.c (fsserial_hardflow): Add support for CRTSXOFF used on Solaris. * unix/serial.c (fsserial_lockfile): In HAVE_SCO_LOCKFILES case, only lower case the last letter in the lock file name. * uucp.texi (UUCP Lock Files): Document this change. * unix/serial.c (fsblock): Test for EINVAL when changing the write descriptor as we do when changing the read descriptor. * unix/seq.c (ixsysdep_get_sequence): If SPOOLDIR_HDB or SPOOLDIR_SVR4, use .SQFILE for the name of the sequence directory, to avoid colliding with the command sequence number file used by fscmd_seq. It's not compatible at the file level, but it might work. * uucico.c (fspawn_uuxqt): Ignore the zsys parameter. * Godfrey van der Linden: unix/serial.c (fsysdep_conn_io): Limit the delay waiting for a write to the number of bytes we are prepared to read. 2002-02-17 Ian Lance Taylor * unix/serial.c (fsysdep_conn_io): If FD_ZERO is defined, use fd_set for the select argument. * Don Phillips: uustat.c (fsquery_show): Add space in the 0X case to make everything line up. * unix/status.c (fsysdep_get_status): Correct removal of trailing quote. * unix/serial.c (fsserial_open): Make sure that we can not set the ibaud field to zero for a terminal. (fsysdep_conn_io): Only use baud rate in delay calculations on a terminal. 2002-02-16 Ian Lance Taylor * configure.in: When checking for , only include if it exists. * configure: Rebuild. Raymond Nijssen: Support for hardware flow control on HP/UX. * unix/serial.c: Include if the system has it. (fsserial_hardflow): If HAVE_SYS_TERMIOX_H, use TCGETX/TCSETX to enable and disable hardware flow control. * configure.in: Check for existence of . * configure, config.h.in: Rebuild. * uucp.c (main): Check for unsupported whitespace in notification address. * uux.c (main): Likewise. * unix/splcmd.c (zsysdep_spool_commands): Check for unsupported use of white space. 2002-02-14 Ian Lance Taylor * unix/uid.c (fsuser_perms): Add piegid parameter. Change all callers. (fsuucp_perms): Add iegid parameter. Change all callers. * sysdep.h (fsuser_perms, fsuucp_perms): Update declarations. * John Hughes: uuconf.h (struct uuconf_system): Add uuconf_cmax_file_time field. * uuconf/syssub.c (SYSTEM_INTEGERS): Add cmax_file_time. * uuconf/tsinfo.c (asIcmds): Add max-file-time command. * trans.c (floop): Check maximum file send time. * uuchk.c (ukshow): Print maximum file send time. * uucp.texi (File Transfer Control): Document max-file-time. * send.c (flocal_send_await_reply): Only seek to the end of the file if we are already sending it. * trans.c (utransfree): If the file is open, close it. * rec.c (flocal_rec_await_reply): Don't bother closing the file on error. (fremote_send_reply): Likewise. Set the e field to EFILECLOSED after closing it. (frec_file_end): Likewise. * send.c (fremote_rec_reply): Set the e field to EFILECLOSED after closing it. (fsend_await_confirm): Likewise. * John Hughes: trans.c (struct sreceive_ack): Move definition before local function prototypes. (utfree_queue): New static function. (uclear_queue): Use utfree_queue and utfree_acked. (utfree_receive_ack): New static function. (uwindow_acked): Use utfree_receive_ack. (utfree_acked): New static function. * lib/buffer.c: Add MALLOC_BUFFERS case to use malloc/free for each buffer, to permit easy use of debugging malloc. * chat.c (fcsend): Permit \M and \m in any type of chat script. * uucp.texi (Chat Scripts): Document \M and \m here. (dial File): Don't document \M and \m here. * uuconf/chatc.c (_uuconf_ichat_cmd): Treat chat-program used with no arguments as specifying no chat-program. 2002-02-08 Ian Lance Taylor * install-sh: Replace with current version from automake. * configure.in: Don't put AC_MSG_WARN for ftime on the same line as AC_MSG_RESULT. * configure: Rebuild. * uuxqt.c (uqdo_xqt_file): If uucp is not a permitted command, then reject a uucp command which does not have two arguments. (fqforward): Don't crash if zfile is NULL. * log.c (ulog): Add code to HDB_LOGGING case to protect the administrator against foolishness. * Meno Abels: uux.c (main): Correct off-by-one error in memory allocation. * Vadim Radionov: copy.c (fcopy_open_file): Check for read errors. * Michael Ju. Tokarev: unix/serial.c (fsserial_open): Force port into blocking mode, in case the chat program cares. * unix/umode.c: New file. * unix/access.c (fsysdep_daemon_access): Change error message if stat returns EACCES. * uucp.c (uccopy): Use ixsysdep_user_file_mode to get the file mode. * system.h (ixsysdep_user_file_mode): Declare. * unix/Makefile.am (libunix_a_SOURCES): Add umode.c. * unix/Makefile.in: Rebuild. * Roland McGrath: unix/work.c (fsysdep_get_work_init): Add cmax parameter. Change all callers. (fsysdep_get_work): Likewise. * system.h (fsysdep_get_work_init): Update declaration. (fsysdep_get_work): Likewise. * unix/tcp.c: Use getaddrinfo if available to decide how to call the socket functions. * configure.in: Check for getaddrinfo and struct sockaddr_storage. * configure, config.h.in: Rebuild. * unix/mkdirs.c (fsysdep_make_dirs): Correct error message in case where we don't have permission to create the directory. 2002-02-07 Ian Lance Taylor * John Hughes: uuxqt.c (uqcleanup): Don't unlock the execution file until after it has been removed, to avoid a race which may lead to it's being executed twice. * unix/xqtsub.c (fsysdep_lock_uuxqt_dir): Call fclean_uuxqt_dir. (fsysdep_unlock_uuxqt_dir): Move directory cleaning code into fclean_uuxqt_dir, and call it. (fclean_uuxqt_dir): New static function. * unix/xqtsub.c (fsysdep_copy_uuxqt_files): Rename from fsysdep_move_uuxqt_files. Remove fto parameter. Use link instead of rename. Don't bother moving files back on failure. * uuxqt.c (uqdo_xqt_file): Call fsysdep_copy_uuxqt_files rather than fsysdep_move_uuxqt_files. Remove call to fsysdep_move_uuxqt_files after failure. * system.h (fsysdep_copy_uuxqt_files): Rename declaration from fsysdep_move_uuxqt_files. * Ignatios Souvatzis: uucp.texi (Execution File Format): Correct example of X. file. * uucp.c (uccopy): Add fforcelocal option. Change all callers. * protz.c (fzwait): Remove unreachable return statement. * unix/lock.c (fsdo_lock): Test for error when removing read only stale lock file. 2002-02-06 Ian Lance Taylor * uuxqt.c (uqdo_xqt_file): Check for long versions of prohibited options. * uucp.c (asClongopts): Add comment indicating that changes here must be reflected in uuxqt.c. * MANIFEST, */MANIFEST: Remove. These files are not used by automake. * uuconf/cnfnms.c: New file. * uuconf.h (struct uuconf_config_file_names): Define. (uuconf_config_files): Declare. * uuchk.c (main): Dump configuration file names. (ukshow_names): New static function. * uuconf/Makefile.am (libuuconf_a_SOURCES): Add cnfnms.c. * uuconf/Makefile.in: Rebuild. 2002-01-17 Ian Lance Taylor * Many files: Change types and add casts and attributes to avoid warnings. * uudefs.h: Use attribute macro in ulog declaration. * uucp.h: Add GCC attribute macros. * configure.in: Add --enable-build-warnings option. * Makefile.am (AM_CFLAGS): Add $(WARN_CFLAGS). * lib/Makefile.am (AM_CFLAGS): Likewise. * unix/Makefile.am (AM_CFLAGS): Likewise. * uuconf/Makefile.am (AM_CFLAGS): Likewise. * configure, Makefile.in: Rebuild. * lib/Makefile.in, unix/Makefile.in, uuconf/Makefile.in: Rebuild. 2002-01-16 Ian Lance Taylor * Makefile.am: New file. * lib/Makefile.am: New file. * uuconf/Makefile.am: New file. * unix/Makefile.am: New file. * configure.in: Call AM_INIT_AUTOMAKE. Change AC_CONFIG_HEADER to AM_CONFIG_HEADER. Call AM_MAINTAINER_MODE. Call AC_ARG_WITH for user name and configuration directories. Define SBINDIR. Don't call AC_PROG_INSTALL, AC_PROG_MAKE_SET, AC_C_CROSS. Add descriptions to all AC_DEFINE calls. Don't set or substitute CFLAGS or LDFLAGS. Add UUDIR conditional. Don't create stamp-h in AC_OUTPUT. * Makefile.in: Build using automake. * lib/Makefile.in: Likewise. * uuconf/Makefile.in: Likewise. * unix/Makefile.in: Likewise. * aclocal.m4: Build using aclocal. * config.h.in: Build using autoheader. * configure: Rebuild. * stamp-h.in: New timestamp file. * sysdep.h: Rename from sysh.unx. A non-Unix port looks unlikely. * uuconf/syshdr.h: Rename from uuconf/syshdr.unx. * AUTHORS: New file, required by automake --gnu. * INSTALL: Likewise. Generic version at least for now. * compile: New file, required by automake --gnu. * depcomp: Likewise. * missing: Likewise. * mkinstalldirs: Likewise. * texinfo.tex: Likewise. * .cvsignore: Ignore uucp.info* files. Mon Sep 16 21:29:10 1996 Ian Lance Taylor * trans.c (floop): Don't request a hangup if we are waiting for data for a job and we only have one channel. Sun Aug 20 15:12:36 1995 Ian Lance Taylor * Released version 1.06.1. * uux.c (main): Make sure that the grade is between '0' and '9', or 'a' and 'z', or 'A' and 'Z', in case isalnum accepts other characters. * uucp.c (main): Likewise. Sat Aug 19 23:15:21 1995 Ian Lance Taylor * configure.in: Check for seteuid. * configure: Rebuild. * config.h.in (HAVE_SETEUID): Define. * unix/uid.c: If HAVE_SETEUID is defined, use seteuid rather than setuid. * policy.h: Change HAVE_BROKEN_SETREUID comment to mention that it does not work on 4.4BSD-Lite and NetBSD. * Andrey A. Chernov: uuconf.h (UUCONF_GRADE_LEGAL): Use BUCHAR. Wed Aug 16 21:23:39 1995 Ian Lance Taylor * uucico.c (flogin_prompt): Add pzsystem parameter, and pass it to faccept_call. Change all callers. * trans.c (qtransalc): Clear zlog field. * Makefile.in (VERSION): Change to 1.06.1. Thu Aug 10 22:42:53 1995 Ian Lance Taylor * Released version 1.06. * uuconf/tsinfo.c (_uuconf_itaylor_system_internal): Don't set uuconf_fcall merely because uuconf_qtimegrade is not set. Check values against _uuconf_unset, not NULL. * trans.c (fgot_data): Only log when data comes in if fsendfile is FALSE. Wed Aug 9 20:52:29 1995 Ian Lance Taylor * Makefile.in (VERSION): Change to 1.06. Tue Aug 1 20:13:36 1995 Ian Lance Taylor * uuconf/tsinfo.c (_uuconf_itaylor_system_internal): Set the fcall field for the first alternate if it has some way of selecting a port. * Ard van Breemen: unix/serial.c (fsserial_hardflow): Add support for NCR Tower using IRTS. * policy.h (FSYNC_ON_CLOSE): Define. * system.h (fsysdep_sync): Declare. * uucp.h (fstdiosync): Define, twice. * unix/sync.c: New file. * copy.c (fcopy_open_file): Call fsysdep_sync. * cu.c (icutake): Likewise. * rec.c (frec_file_end): Likewise. (frec_file_end): Call fstdiosync. * uucp.c (uccopy): Likewise. * uux.c (main): Likewise. (uxadd_send_file): Likewise. * unix/splcmd.c (zsysdep_spool_commands): Likewise. * unix/Makefile.in (OBJS): Add sync.o. (sync.o): New target. * Peter Wemm: unix/filnam.c (fscmd_seq): Increase the delay each time an attempt to lock LCK..SEQ fails. * Peter Wemm: uustat.c (fsnotify): Escape a leading "From " when including standard input in a mail message. Mon Jul 31 22:45:23 1995 Ian Lance Taylor * unix/serial.c (fsserial_lockfile): In the HAVE_SVR4_LOCKFILES case, increase the space allocated to print the major and minor numbers. Sun Jul 30 22:30:51 1995 Ian Lance Taylor * trans.c (fgot_data): Log a message as soon as anything comes in, not just for file data. * prote.c (feprocess_data): Handle a zero length file. Wed Jul 19 00:14:46 1995 Ian Lance Taylor * unix/xqtfil.c (fSone_dir): New static variable. (fsysdep_get_xqt_init): Add zsystem argument. (zsysdep_get_xqt): Likewise. (usysdep_get_xqt_free): Likewise. * system.h (fsysdep_get_xqt_init): Update declaration. (zsysdep_get_xqt): Likewise. (usysdep_get_xqt_free): Likewise. * uuxqt.c (main): Pass zdosys for system to get_xqt routines. * uustat.c (fsexecutions): Pass NULL for system to get_xqt routines. (fsquery): Likewise. * uux.c (main): If we are not starting uucico, and we queued a local execution, start uuxqt. Tue Jul 18 22:33:08 1995 Ian Lance Taylor * uuxqt.c (REMOVE_QINPUT): Define. (uqdo_xqt_file): Set REMOVE_QINPUT if zQinput is in the spool directory. (isave_files): If REMOVE_QINPUT is set, save the input file. (uqcleanup): If REMOVE_QINPUT and REMOVE_NEEDED are set, remove the input file. Mon Jul 17 20:59:20 1995 Ian Lance Taylor * uuchk.c (ukshow): Add missing \n. Sun Jul 16 12:02:03 1995 Ian Lance Taylor * Makefile.in (TEXI2HTML): Define. (doc-dist): Depend upon uucp.html; put HTML files in documentation distribution. (html, uucp.html): New targets. Mon Jul 10 20:24:48 1995 Ian Lance Taylor * unix/lcksys.c: Rewrite to not truncate the system name in the lock file name, unless HAVE_LONG_FILE_NAMES is 0. Sat Jul 8 13:41:26 1995 Ian Lance Taylor * Andrey A. Chernov: cu.c (ucuhelp): Mention -E. (fcudo_cmd): Use BUCHAR rather than casting to unsigned int. (icuunrecogvar): Likewise. (uculist_vars): Likewise. (icuunrecogfn): Likewise. * lib/getopt.c (_getopt_internal): Likewise. * Andrey A. Chernov: Makefile.in (infodir): Changed commented out setting from share/doc to share/info. * Godfrey van der Linden: unix/serial.c (fsysdep_conn_io): Base select or alarm timeout on connection speed. * configure.in: Move AC_PREFIX_PROGRAM before AC_PROG_CC. * configure: Rebuild. * policy.h (HAVE_BROKEN_SETLKW): Add new define. * unix/filnam.c (USE_POSIX_LOCKS): Don't set if HAVE_BROKEN_SETLKW is set. (fscmd_seq): If the F_SETLKW call returns EINVAL, revert to using lock files. * unix/spawn.c (ixsspawn): If setreuid is available, use it, rather than setuid, to set the real user ID to the effective user ID. * Thomas Mechtersheimer: uuchk.c (main): Correct typo (iret for iint). * Bob Thrush: unix/serial.c (fsserial_hardflow): Correct typo (c_cflags for c_cflag). * configure.in: Check for sys/statvfs.h. Check for memset, memcmp, and memcpy with the correct argument types (apparently some systems require this). * config.h.in (HAVE_SYS_STATVFS_H): Define. * configure: Rebuild. Mon Jul 3 00:26:50 1995 Ian Lance Taylor * Released version beta 1.06. Sun Jul 2 10:39:40 1995 Ian Lance Taylor * configure.in: Add AC_OUTPUT code to touch stamp-h when rebuilding config.h. * Makefile.in (stamp-h): Remove ``echo > stamp-h''. * Makefile.in (dist): Fix for srcdir != objdir. * unix/Makefile.in (dist): Likewise. * uuconf/Makefile.in (dist): Likewise. * lib/Makefile.in (dist): Likewise. Sat Jul 1 13:42:35 1995 Ian Lance Taylor * configure.in: Check for termios.h. * config.h.in (HAVE_TERMIOS_H): Mention. * policy.h: Set HAVE_POSIX_TERMIOS if HAVE_TERMIOS_H. Fri Jun 30 09:24:13 1995 Ian Lance Taylor * uux.c (zXxqt_name): Remove. (fXxqtlocal): New static variable; replaces local fxqtlocal. (sXxqtsys): New static variable; replaces local sxqtsys. (zXxqtloc): New static variable; replaces local zxqtloc. (bXgrade): New static variable; replaces local bgrade. (abXxqt_tname): New static variable; replaces local abxqt_tname. (abXxqt_xname): New static variable; replaces local abxqt_xname. (main): Use new static variables instead of locals. Don't set zXxqt_name. (uxadd_xqt_line): Get file name here before opening file. (uxadd_send_file): Remove parameters qxqtsys, zxqtloc, and bgrade. Change all callers. * unix/filnam.c (usput62): New static function. (zscmd_file): When SPOOLDIR_TAYLOR, use a different algorithm which does not read the sequence file. * unix/splcmd.c (zsysdep_spool_commands): Return value of zscmd_file might already exist; handle that case. * unix/jobid.c (zsfile_to_jobid): Change jobid format when SPOOLDIR_TAYLOR, since sequence number is now much longer. (zsjobid_to_file): Corresponding change. * unix/work.c (fswork_file): When SPOOLDIR_TAYLOR, don't require the the file name to be 7 characters long. (bsgrade): Change algorithm used when SPOOLDIR_TAYLOR. * uustat.c (fsworkfile_show): Correct kill prompt to put program name at start of line. Add trailing space after question mark. (fsexecutions): Likewise. * unix/work.c (fsysdep_get_work): Don't call fsysdep_get_work_init if no more jobs are found. Thu Jun 29 15:27:31 1995 Ian Lance Taylor * fsusg.c: Update with changes from fileutils 3.12. * configure.in: Update filesystem space tests to fileutils 3.12. * config.h.in: Corresponding changes. * unix/filnam.c (fscmd_seq): If F_SETLKW fails with ENOMEM, ENOSPC, or ENOLCK, sleep and try again. * Makefile.in (MORECFLAGS): Remove unused SBINDIR definition. * unix/Makefile.in (run.o): Add dependency on Makefile and ../Makefile, so that it is rebuilt if sbindir changes. * uuconf/Makefile.in (callin.o): Add dependency on Makefile and ../Makefile, so that it is rebuilt if NEWCONFIGLIB or OLDCONFIGLIB change. (hinit.o, hrmunk.o, maxuxq.o, rdperm.o, tinit.o): Likewise. (vinit.o): Likewise. * cu.c (main, ucuhelp): Update copyright. * tstuu.c (main): Likewise. * uuchk.c (main, ukhelp): Likewise. * uucico.c (main, uhelp): Likewise. * uuconv.c (main, uvhelp): Likewise. * uucp.c (main, uchelp): Likewise. * uulog.c (main, ulhelp): Likewise. * uuname.c (main, unhelp): Likewise. * uupick.c (main, uphelp): Likewise. * uustat.c (main, ushelp): Likewise. * uux.c (main, uxhelp): Likewise. * uuxqt.c (main, uqhelp): Likewise. * Makefile.in (VERSION): Update to beta1.06. Wed Jun 28 10:36:35 1995 Ian Lance Taylor * trans.c (struct scharge, sTsend, sTreceive): Remove. (qTtiming_rec, iTrecsecs, iTrecmicros): New static variables. (fqueue_receive): If adding something to an empty receive queue, update iTrecsecs and iTrecmicros. (utransfree): Remove references to sTsend and sTreceive. If freeing qTtiming_rec, clear it. (ftcharge): Remove. Remove all calls to it. (fttime): New static function. (uclear_queue): Clear qTtiming_rec. (floop): Add timing code to replace ftcharge. (fgot_data): Likewise. (ftadd_cmd): Set s.bcmd to 'H' for a hangup request, for better debugging information. (ufailed): Remove setting of iTchecktime and calls to ftcharge. * uuconf.h (UUCONF_STRIP_LOGIN): Define. (UUCONF_STRIP_PROTO): Define. (uuconf_strip): Declare. * uuconf/strip.c: New file, definining uuconf_strip. * uuconf/Makefile.in (OBJS): Add strip.o. * uuconf/uucnfi.h (struct sprocess): Add new fields fstrip_login and fstrip_proto. * uuconf/iniglb.c (_uuconf_iinit_global): Initialize new fields. * uuconf/tinit.c (asCmds): Add "strip-login" and "strip-proto". * uucico.c (fdo_call): Check whether protocol commands should be stripped, and pass information to zget_uucp_cmd. (faccept_call): Likewise. (flogin_prompt): Check whether login commands should be stripped, and pass information to zget_typed_line. (zget_uucp_cmd): Add fstrip argument, and use it to control whether incoming characters are stripped. (zget_typed_line): Likewise. * uuchk.c (main): Report uuconf_strip information. Mon Jun 26 17:57:14 1995 Ian Lance Taylor * uuchk.c (main): Report global configuration information. * uux.c (main): If an attempt is made to execute an empty command, just create a poll file instead. * uupick.c (main): Mention 'd' as a possible command. Wed Jun 21 16:16:13 1995 Ian Lance Taylor * unix/filnam.c (fscmd_seq): If available, use POSIX style locking for the sequence file. Use IPRIVATE_FILE_MODE when creating the sequene file. * copy.c (fcopy_file): Add fsignals argument, and pass it to fcopy_open_file. Update all callers. (fcopy_open_file): Add fsignals argument; if TRUE, check for signals while copying the file. Update all callers. * uudefs.h (fcopy_file, fcopy_open_file): Update prototypes. Thu Jun 15 20:34:00 1995 Ian Lance Taylor * Rob Janssen: getopt.h: Don't fail if P is not defined because this file got included by something other than uucp.h. Tue Jun 6 13:17:14 1995 Ian Lance Taylor * log.c (ulog): Always open the log file even if zmsg is NULL. * unix/detach.c (usysdep_detach): Call ulog with NULL before closing stderr. * log.c (ulog): Print file name correctly under HDB_LOGGING if an error occurs. * Andrey A. Chernov: uucp.h: Use off_t rather than long when casting arguments to lseek. * proti.c (IMAXPACKSIZE): Subtract one. (fijstart): Correct iIforced_remote_packsize check. * unix/filnam.c (zsfile_name): Only use remote system name in local filename for SPOOLDIR_SVR4, not for SPOOLDIR_HDB. * Peter da Silva: cu.c (asCulongopts): Add "escape". (main): Handle -E/--escape. * cu.1: Document -E/--escape. * unix/cusub.c (fsysdep_cu): Don't treat \0 as an escape character. * Takatoshi Ikeda: log.c (ustats): Base 'M' vs. 'S' in HDB_LOGGING on fcaller, not on fmaster. (zldate_and_time): In HDB_LOGGING, don't zero fill the hour. * uudefs.h (ustats): Change argument name in prototype. * send.c (fsend_await_confirm): Pass fcaller rather than fmaster to ustats. * rec.c (frec_file_end): Likewise. * trans.c (ufailed): Likewise. * unix/tcp.c: Include . (ftcp_dial): If gethostbyname fails, try inet_addr. * Jim Brownfield: unix/detach.c (usysdep_detach): Call TIOCNOTTY on /dev/tty, not on file descriptor 0. * uuchk.c (ukshow): If the system will never be called, say so explicitly. * unix/pause.c (usysdep_pause): When using poll, clear the sdummy structure before passing it down. * unix/bytfre.c (csysdep_bytes_free): Check for overflow. * C.A. Lademann: cu.c (asCulongopts): Add "nostop". (main): Handle --nostop. (ucuhelp): Mention --nostop. * cu.1: Document --nostop. * Dean Edmonds: cu.c (fcuset_var): Copy string before calling uuconf_cmd_args, and handle UUCONF_CMDTABRET_KEEP correctly. * Jorge Cwik: Add support for 'y' protocol. * proty.c: New file, written by Jorge Cwik. * prot.h: Declare 'y' protocol functions. * uucico.c (asProtocols): Add entry for 'y' protocol. * Makefile.in (UUOBJS): Add proty.o. (ALLOBJS): Add proty.o. (proty.o): New target. Mon Jun 5 12:05:22 1995 Ian Lance Taylor * cu.c (main): Don't clobber user specified ibaud when a specific system is being called. * unix/serial.c (fsysdep_stdin_init): chmod /dev/tty to 0600 to prevent other users from writing to it. * uux.c (main): If the argument is quoted by parentheses, don't break it at shell separator characters. * protg.c (fgprocess_data): Don't treat a duplicate RR as an RJ if we are retransmitting packets. Corrects change of May 15, 1993. * unix/lock.c (fsdo_lock): Log an error message if the LOCKFILES define appears to not match the contents of the lock file. Sun Jun 4 14:25:43 1995 Ian Lance Taylor * Makefile.in (uucp.info): Use explicit $(srcdir) rather than $<. (uucp.dvi): Likewise. (uucp.ps): Use uucp.dvi rather than $<. * Scott Guthridge: cu.c (icutake): Skip \r characters unless fCuvar_binary is set. * unix/status.c (fsysdep_get_status): Ignore double quotes around description string in status file. (fsysdep_set_status): If SPOOLDIR_SVR4, put double quotes around description string. * uustat.c (fsxqt_file_read): Change second argument from file name to open file. (fsworkfile_show): Make sure execution file can be opened before displaying information in execution file format. (fsexecutions): Open file before passing it to fsxqt_file_read. * uucp.h (ffileioerror): Rename from ffilereaderror. * cu.c (icuput): Corresponding change. * trans.c (floop): Likewise. (fgot_data): Use ffileioerror to decide whether to print message using errno. * uuconv.c (uvwrite_taylor_port): Fix handling of dialer-sequence for a modem port to actually print ``dialer-sequence''. * uuconf/hsinfo.c (_uuconf_ihdb_system_internal): Treat a syntax error in the time field as equivalent to ``never''. * uuconf/vsinfo.c (_uuconf_iv2_system_internal): Likewise. * uuchk.c (ukshow): Print max-retries information. * Don Phillips: Add support for new called-timegrade command. * uuconf.h (struct uuconf_system): Add uuconf_qcalledtimegrade field. * uuconf/tsinfo.c (asIcmds): Add "called-timegrade". * uuconf/syssub.c (SYSTEM_TIMESPANS): Add uuconf_qcalledtimegrade. * trans.c (fqueue): Check uuconf_qcalledtimegrade. * uuchk.c (ukshow): Print called-timegrade and success-wait information. * uuconv.c (uvwrite_taylor_system): Handled called-timegrade. * unix/xqtsub.c (fsysdep_lock_uuxqt_dir): Accept EISDIR. * Damon: unix/mkdirs.c (fsysdep_make_dirs): Accept EROFS. Sat May 27 09:55:38 1995 Ian Lance Taylor * unix/detach.c (usysdep_detach): Mention routine name in debugging message. * tstuu.c (main): Exit with an error message if not compiled with HAVE_TAYLOR_CONFIG. * Jim Avera: Makefile.in (install-info): Get the info files from the source directory if they aren't in the object directory. * uuchk.c (main): Accept -s to only print information for a specific system. (asKlongopts): Add --system as a synonym for -s. (ukusage): Mention -s. (ukhelp): Likewise. * Makefile.in (uuconf/libuuconf.a): Depend upon config.h. (unix/libunix.a, lib/libuucp.a): Likewise. * Matthias Urlichs and Olaf Kirch: If an execution fails, save the files. * sysh.unx (FAILEDDIR): Define. * system.h (zsysdep_save_failed_file): Declare. * uuxqt.c (uqdo_xqt_file): Call isave_files if an execution fails. Move the execution files out of the execution directory on any failure, not just a temporary one. (isave_files): New function; pass all the execution files to zsysdep_save_failed_file, and send mail to OWNER. * unix/failed.c: New file. * unix/Makefile.in (OBJS): Add failed.o. (failed.o): New target. * Johannes Stille: Remove various race conditions, as follows: * rec.c (flocal_rec_send_request): Don't free qtrans if pfsendcmd fails. (fremote_send_reply): Likewise. (fremote_send_fail_send): Move pfsendcmd call to the end of the routine, after everything else has been done. (frec_file_send_confirm): Likewise. * send.c (struct ssendinfo): Add new fields fnever and zconfirm. (flocal_send_fail): Remove first argument; change all callers. (usfree_send): Free new zconfirm field. (flocal_send_file_init): Initialize new zconfirm field. (fremote_rec_file_init): Likewise. (flocal_send_request): Free qtrans explicitly, rather than expecting flocal_send_fail to do it. (flocal_send_request): Don't free qtrans if pfsendcmd fails. (flocal_send_await_reply): Use new fnever field rather than overloading fsent. (flocal_send_cancelled): Likewise. (flocal_send_await_reply): Set fcmd field to TRUE. (fremote_rec_reply): Set fcmd field to TRUE. Set qtrans->zlog before calling pfsendcmd. Don't free qtrans if pfsendcmd fails. (fremote_rec_fail_send): Move pfsendcmd call to the end of the routine, after everything else has been done. (fsend_file_end): If zconfirm is set, call fsend_await_confirm. Don't bother to set fcmd here. (fsend_await_confirm): If fsent is not set, just save command in zconfirm and return. (fsend_exec_file_init): Free and clear new zconfirm field. * xcmd.c (flocal_xcmd_request): Don't free qtrans if pfsendcmd fails. * unix/pause.c: Prefer any of the other choices to nap, since the meaning of nap argument varies from system to system, and the user may not set HAVE_HUNDREDTHS_NAP correctly. * unix/cusub.c (fsysdep_shell): When starting up an interactive shell, use the environment variable SHELL rather than /bin/sh. Fri May 26 00:03:46 1995 Ian Lance Taylor * Chip Salzenberg: uulog.c (main): Set cluser, not clsys, when using HAVE_HDB_LOGGING. * Robert Joop: uuconf/callin.c (uuconf_callin): Improve handling of Unix style passwd files. * uupick.c (main): Don't fail if we can't get system information, since uupick is not setuid. * Stephen Harris: uudefs.h (struct sstatus): Add new field zstring. * unix/status.c (fsysdep_get_status): Set zstring field. * uustat.c (fsquery_show): Print and free zstring field, if set, rather than indexing off type. (fsmachines): Likewise. * uucico.c (fcall): Free sstat.zstring. * proti.c (cIwindow_timeout): New static variable. (fijstart): Initialize cIwindow_timeout based on the window size and connection speed. Increment both timeouts in callee. (fishutdown): Initialize cIwindow_timeout. (fiwindow_wait): Use cIwindow_timeout rather than cItimeout. * unix/serial.c (fsysdep_conn_io): Try using select before doing a blocking write, and use an alarm if we fall back to the blocking write, to try to avoid deadlock when both systems fill the pipe. * Trever Miller: unix/lock.c (fsdo_lock): Fix use of inid where inme was intended, in HAVE_QNX_LOCKFILES code. * unix/lock.c (fsdo_lock): Treat an empty lock file as stale. Thu May 25 11:46:32 1995 Ian Lance Taylor * Kenji Rikitake: unix/serial.c (fsserial_hardflow): Handle CRTS_IFLOW as used on BSDI. * Gerriet M. Denkmann: uuconf/vsnams.c (uuconf_v2_system_names): Accept continuation lines in L.sys. * uuconf/vsinfo.c (_uuconf_iv2_system_internal): Skip comment lines in L.cmds. Ignore everything after comma in L.cmds lines. * Gerriet M. Denkmann: unix/ftw.c (ftw_dir): Initialize newlev to avoid warning. * Paul Pryor: unix/tli.c (ftli_open): Swap real and effective user ID's when running as a server, so that root can bind privileged ports. * unix/tcp.c (ftcp_open): Call fsuser_perms and fsuucp_perms rather than having the same code inline. * sysh.unx (fsuser_perms, fsuucp_perms): Declare. * unix/ufopen.c (fsuser_perms, fsuucp_perms): Move into uid.c. * unix/uid.c: New file. * unix/Makefile.in (OBJS): Add uid.o. (uid.o): New target. * unix/tcp.c: Move tcp.c into unix subdirectory. * unix/tli.c: Likewise for tli.c. * Makefile.in (UUOBJS, CUOBJS, ALLOBJS): Remove tcp.o and tli.o. (tcp.o, tli.o): Remove. * unix/Makefile.in (OBJS): Add tcp.o and tli.o. (tcp.o, tli.o): New targets. * Paul Pryor: configure.in: Check for -lnsl_s before -lnsl. * Makefile.in (prefix): Use @prefix@, not /usr/local. (exec_prefix): New variable, set to @exec_prefix@. (sbindir, bindir): Use $(exec_prefix) rather than $(prefix). (CPPFLAGS): New variable, set to @CPPFLAGS@. (@SET_MAKE@): New macro used by autoconf 2. (MORECFLAGS): Put -I. before -I$(srcdir). (MDEFINES): Pass down CPPFLAGS. (.c.o): Use CPPFLAGS. (uuconv.o): Likewise. (distclean): Remove stamp-h, config.log and config.cache. (maintainer-clean): New synonym for realclean. (configure, config.h, stamp-h, Makefile, config.status): Rewrite as suggested by autoconf 2.3. (.PHONY): Add maintainer-clean. * lib/Makefile.in (CPPFLAGS): New variable, set to @CPPFLAGS@. (MORECFLAGS): Put `-I..' before `-I$(srcdir)/..'. (maintainer-clean): New synonym for realclean. (.c.o): Use CPPFLAGS. (Makefile): Just rebuild this Makefile, not the others. (.PHONY): Add maintainer-clean. * unix/Makefile.in (CPPFLAGS): New variable, set to @CPPFLAGS@. (prefix): Removed. (sbindir): Use @exec_prefix@ rather than $(prefix). (MORECFLAGS): Put `-I..' before `-I$(srcdir)/..'. (maintainer-clean): New synonym for realclean. (.c.o): Use CPPFLAGS. (Makefile): Just rebuild this Makefile, not the others. (.PHONY): Add maintainer-clean. * uuconf/Makefile.in (prefix): Set to @prefix@, not /usr/local. (CPPFLAGS): New variable, set to @CPPFLAGS@. (MORECFLAGS): Put `-I.' before `-I$(srcdir)' and `-I..' before `-I$(srcdir)/..'. (maintainer-clean): New synonym for realclean. (.c.o): Use CPPFLAGS. (Makefile): Just rebuild this Makefile, not the others. (.PHONY): Add maintainer-clean. * configure.in: Extensive changes for autoconf 2.3. * configure: Regenerate using autoconf 2.3. * install-sh: New file. Mon May 22 22:18:59 1995 Ian Lance Taylor * Mister Flash: chat.c (fcsend): Don't get forget to add 10 in \x. Tue Jan 17 22:49:05 1995 Ian Lance Taylor * unix/mkdirs.c (fsysdep_make_dirs): Don't get confused by a double slash. Wed Aug 10 09:25:10 1994 Ian Lance Taylor (ian@airs.com) * uuconf/hport.c (uuconf_hdb_find_port): Set iret to UUCONF_SUCCESS when a port is found. Tue Aug 2 08:57:05 1994 Ian Lance Taylor (ian@airs.com) * trans.c (ftadd_cmd): Report a 'P' (poll) command as garbled. Wed Jul 20 21:53:03 1994 Ian Lance Taylor (ian@airs.com) * rec.c (flocal_rec_file_init): Set name of local system after calling uuconf_system_local. * uustat.c (fsexecutions): Likewise. Sat Jul 16 16:54:12 1994 Ian Lance Taylor (ian@airs.com) * log.c (ulog): Use strerror in message about being unable to open the log file. Wed Jul 13 00:44:33 1994 Ian Lance Taylor (ian@airs.com) * rec.c (fremote_send_reply): Set qinfo->freplied before sending the command. Sun Jul 10 23:25:23 1994 Ian Lance Taylor (ian@airs.com) * Tin Le: lib/debug.c (udebug_buffer): Only compile if DEBUG > 1. Mon Jun 27 21:06:29 1994 Ian Lance Taylor (ian@airs.com) * uuconf.h (UUCONF_CMDTABFLAG_NOCOMMENTS): Define. * uuconf/cmdlin.c (uuconf_cmd_line): Handle NOCOMMENTS. * uuxqt.c (uqdo_xqt_file): Pass NOCOMMENTS to uuconf_cmd_file. Thu May 19 22:50:37 1994 Ian Lance Taylor (ian@airs.com) * Ollivier Robert: unix/mkdirs.c: Permit EISDIR. Wed May 18 23:15:36 1994 Ian Lance Taylor (ian@airs.com) * uucico.c (fcall): Reset sDaemon fields each time through the loop, not just once. Thu May 5 23:15:11 1994 Ian Lance Taylor (ian@airs.com) * Released version 1.05. * Makefile.in (doc-dist): Put uucp.ps in uucp-doc-$(VERSION). Sun May 1 23:41:49 1994 Ian Lance Taylor (ian@airs.com) * uuchk.c (ikshow_port): Show reliability information. (ukshow_dialer): Likewise. (ukshow_reliable): New function. Sat Apr 16 22:28:10 1994 Ian Lance Taylor (ian@airs.com) * Andrew A. Chernov: uucico.c (main): Pass 'z' to getopt. (uhelp): Mention -z aka --try-next. * log.c (ustats): Report failed transfers when HAVE_HDB_LOGGING. Wed Apr 13 23:07:20 1994 Ian Lance Taylor (ian@airs.com) * prot.c (fsend_data): If no room in receive buffer, just write the data out, don't call fconn_io. Tue Apr 12 21:55:32 1994 Ian Lance Taylor (ian@airs.com) * Spider Boardman: unix/serial.c (fsysdep_modem_end_dial): Set terminal characteristics of reopened port. Sun Apr 10 18:05:34 1994 Ian Lance Taylor (ian@airs.com) * send.c (flocal_send_fail): Always call fsysdep_did_work. (flocal_send_await_reply): Don't call flocal_send_fail if we are going to call fsend_exec_file_init. Only call fsend_exec_file_init if fnever is TRUE. Pass fnever to flocal_send_cancelled using the qinfo->fsent flag. (flocal_send_cancelled): Only call fsend_exec_file_init if qinfo->fsent is TRUE. * unix/statsb.c (fsysdep_lock_status): If HAVE_QNX_LOCKFILES, initialize painid to NULL. Tue Apr 5 23:09:00 1994 Ian Lance Taylor (ian@airs.com) * Released version gamma 1.05. * Makefile.in (VERSION): Changed to gamma1.05. * uucico.c (fcall): Return TRUE if -C was used and no call was made because there was no work. Mon Apr 4 20:29:30 1994 Ian Lance Taylor (ian@airs.com) * Chris Lewis: unix/serial.c: Include if HAVE_TXADDCD. Check for HAVE_TXADDCD rather than ifdef TXADDCD or TXDELCD. * configure.in: Check for TXADDCD in . * config.h.in (HAVE_TXADDCD): New configuration macro. Sun Apr 3 14:05:30 1994 Ian Lance Taylor (ian@airs.com) * send.c (flocal_send_request): Queue stransfer structure up before sending any command or data, because sending data may cause data to be received for this stransfer, and we must be prepared to handle it correctly. (fremote_rec_reply): Likewise. * rec.c (flocal_rec_send_request, fremote_send_reply): Likewise. (fremote_send_fail_send): Likewise. * xcmd.c (flocal_xcmd_request): Likewise. * Chris Lewis: unix/serial.c (fsserial_hardflow): Add support for AIX TXADDCD and 3b1 CTSCD. Sat Apr 2 00:04:30 1994 Ian Lance Taylor (ian@airs.com) * policy.h (USE_TRADITIONAL_STATUS): Permit this to be defined. * lib/status.c: Control initialization of azStatus based on USE_TRADITIONAL_STATUS rather than SPOOLDIR_HDB || SPOOLDIR_SVR4. Fri Apr 1 23:52:09 1994 Ian Lance Taylor (ian@airs.com) * log.c (ulog): When using HAVE_HDB_LOGGING, force the program name to lower case when using it as a file name. * send.c (flocal_send_await_reply): Correct code to really not decrement number of channels to one. * rec.c (flocal_rec_await_reply): Likewise. Wed Mar 30 22:57:30 1994 Ian Lance Taylor (ian@airs.com) * lib/buffer.c (ubuffree): Change ioff from size_t to int to avoid HP/UX compiler bug. * configure.in: Make sure that defines struct utimbuf before assuming that it is present. Tue Mar 29 23:00:15 1994 Ian Lance Taylor (ian@airs.com) * uuconf/filnam.c: Use UUCONF_CONST, not const, to match declaration in uuconf.h. Mon Mar 28 20:06:00 1994 Ian Lance Taylor (ian@airs.com) * Andrew A. Chernov: policy.h: For several macros, add commented out values appropriate for some free BSD distributions. * Makefile.in: Likewise. * uucico.c (icallin_cmp): Use pointer, not void *. * uuconf/callin.c (struct sinfo, uuconf_callin): Likewise. * Chris Lewis: uuconv.c (fvperm_string_cmp, fvperm_array_cmp): AIX 3.2.5 cc can't handle conditional expressions in if conditions. Sun Mar 27 15:04:27 1994 Ian Lance Taylor (ian@airs.com) * send.c (flocal_send_fail): Don't assume that qtrans is not NULL. * Jeff Ross, Stephen J. Walick: Makefile.in (uusched): Substitute for @SBINDIR@, not @BINDIR@. * configure.in: Make sure that defines struct dirent before assuming that it is present. * Benoit Grange: unix/detach.c (usysdep_detach): Correct type of HAVE_BSD_SETPGRP for HAVE_BSD_PGRP. Sat Mar 26 12:59:36 1994 Ian Lance Taylor (ian@airs.com) * Andrew A. Chernov: uucico.c (asLongopts): Add --try-next as synonym for -z. (main): If -z, call fcall with ftrynext as TRUE. (fcall): Add ftrynext argument. If ftrynext is TRUE, try the next alternate if a call fails. Fri Mar 25 22:37:51 1994 Ian Lance Taylor (ian@airs.com) * lib/parse.c (fparse_cmd): If we get a decimal 666 or 777 for the mode, turn it into an octal 0666 or 0777. * send.c (flocal_send_fail): Accept qdaemon argument rather than qsys. Changed all callers. If we are going to send an execution file, don't call fsysdep_did_work. * protg.c (fgstart): Say ``sending'' and ``receiving'' instead of ``remote'' and ``local'' in log message. * proti.c (fijstart): Likewise. Thu Mar 24 22:40:49 1994 Ian Lance Taylor (ian@airs.com) * Gert Doering: uuchk.c (ikshow_port): Don't use qtli for a TCP port. * Makefile.in (uusched, uuto): Fix typo in sed command. * unix/mail.c (fsysdep_mail): Add casts to avoid warnings. * uuconf/runuxq.c (uuconf_runuuxqt): Likewise. * Emmanuel Mogenet: unix/pipe.c (fspipe_dial): Make consistently static. * unix/serial.c (fsserial_open): Only strip /dev/ from the start of a device name, rather than dropping everything before the last slash. * sysh.unx (ftw): Change stat argument to not be const pointer. * unix/ftw.c (ftw_dir, ftw): Change stat argument to func argument to not be const pointer. * unix/srmdir.c (isremove_dir): Change stat argument to not be const pointer. * unix/walk.c (iswalk_dir): Likewise. Wed Mar 23 20:02:26 1994 Ian Lance Taylor (ian@airs.com) * conn.c (fconn_break): Remove incorrect indirection of function pointer. * unix/mkdirs.c (fsysdep_make_dirs): Some systems can return EACCES, not EEXIST, when a directory exists. * configure.in: Fix STAT_STATFS2_FSIZE test. * configure: Regenerated. Tue Mar 22 01:32:21 1994 Ian Lance Taylor (ian@airs.com) * uucico.c (main): Skip a leading dash in argv[0] which is probably the result of being invoked by the Unix login program. * configure.in: Check for sys/time.h. * config.h.in (HAVE_SYS_TIME_H): Define. * unix/serial.c (fsysdep_modem_begin_dial): Correct type of q for qsysdep. * uux.c (main): Check for zXnames being NULL. Sat Mar 19 14:07:31 1994 Ian Lance Taylor (ian@airs.com) * Released version beta 1.05. * Makefile.in (uucp.info): Use -o argument to force info files to be created in objdir. (doc-dist): Get README-DOC from $(srcdir). * lib/debug.c (iDebug, azDebug_names, idebug_parse): Only compile if DEBUG > 1. Mon Feb 14 22:46:49 1994 Ian Lance Taylor (ian@airs.com) * lib/strtou.c: New file, for strtoul. * lib/MANIFEST: List strtou.c. * configure.in: Check for strtoul, add strtou.o to LIBOBJS if not there. * config.h.in (HAVE_STRTOUL): Define. * uucp.h (strtoul): Declare. Mon Jan 31 20:17:30 1994 Ian Lance Taylor (ian@airs.com) * Makefile.in, lib/Makefile.in, unix/Makefile.in, uuconf/Makefile.in: Use $(CFLAGS) after all other flags. Sun Jan 30 14:34:51 1994 Ian Lance Taylor (ian@airs.com) * Makefile.in (clean, distclean, dist, doc-dist): Remove .tar.gz file, not .tar.Z one. (dist, doc-dist): Use gzip --best, not compress. * Makefile.in (VERSION): Set to beta1.05. * cu.c, uuchk.c, uucico.c, uuconv.c, uucp.c, uulog.c, uuname.c, uupick.c, uustat.c, uux.c, uuxqt.c: Updated copyright date. * conn.c (fconn_init): Added third argument: type of standard input port. * conn.h (fconn_init): Updated declaration. * uucico.c (asLongopts): Added --stdin, synonym for -i. (main): Accept -i TLI to set standard input to be of type TLI. Pass appropriate additional argument to fconn_init. (uhelp): Updated. (fconn_call, iuport_lock): Changed all calls to fconn_init. * cu.c: Changed all calls to fconn_init. * prot.c, protj.c: Include uuconf.h before conn.h. * Makefile.in (prot.o, protj.o): Updated. * unix/serial.c (fsysdep_conn_read): Permit up to two EWOULDBLOCK error returns from read before quitting. Sat Jan 22 16:48:41 1994 Ian Lance Taylor (ian@airs.com) * uuconf/hinit.c: Don't treat lines with leading whitespace as comments in Sysfiles. * log.c: Don't require ANSI C to use vfprintf, just require stdarg.h and prototypes. Required for Alpha cc support. * configure.in: Check for prototype support. Check for stdarg.h. When looking for socket and t_open check for "-lsocket -lnsl" after plain "-lnsl". * config.h.in (HAVE_PROTOTYPES, HAVE_STDARG_H): New macros. * uucp.h: Demand that an ANSI C compiler support prototypes. If HAVE_PROTOTYPES is 1 for Classic C, defined P(x) to be x. * configure: Upgraded to autoconf 1.7. * protg.c (fgstart): Ensure that window size is reasonable. * protg.c (fvstart): Change default packet size from 512 to 1024. * trans.h (struct sdaemon): Added zconfig, irunuuxqt, and cxfiles_received fields. (fspawn_uuxqt): Declare. * uucico.c (fcall, flogin_prompt, faccept_call): Added zconfig and fuuxqt arguments; changed all callers. (main): Use fspawn_uuxqt to invoke uuxqt, and only do it if uuconf_runuuxqt returns UUCONF_RUNUUXQT_ONCE. (fcall, faccept_call): Initialize new struct sdaemon fields. Spawn uuxqt if uuconf_runuuxqt returned UUCONF_RUNUUXQT_PERCALL or if it returned a positive number and execution files have arrived since the last time uuxqt was spawned. (fspawn_uuxqt): New function. * rec.c (frec_file_end): Spawn uuxqt if enough execution files have been received. * uuconf.h (UUCONF_RUNUUXQT_NEVER, UUCONF_RUNUUXQT_ONCE, UUCONF_RUNUUXQT_PERCALL): New #define constants. (uuconf_runuuxqt): Declare. * uuconf/runuxq.c: New file. * uuconf/uucnfi.h (struct sprocess); Added zrunuuxqt field. * uuconf/tinit.c (asCmds): Added "run-uuxqt". * uuconf/iniglb.c (_uuconf_iinit_global): Initialize zrunuuxqt field. * uuconf/MANIFEST, uuconf/Makefile.in: Handle runuxq.c. * system.h (fsysdep_run): Added ffork argument. * unix/run.c (fsysdep_run): Added ffork argument. * uucico.c (main), uux.c (main), uucp.c (main): Changed calls to fsysdep_run to pass ffork argument as FALSE. Fri Jan 14 19:40:20 1994 Ian Lance Taylor (ian@airs.com) * Chip Salzenberg: unix/splcmd.c (zsysdep_spool_commands): More fitting value for size of abtempfile. Mon Jan 10 22:46:52 1994 Ian Lance Taylor (ian@airs.com) * unix/recep.c (fsysdep_remember_reception): Create directory with mode of 0755, not 0777. Mon Jan 3 20:34:35 1994 Ian Lance Taylor (ian@airs.com) * protg.c (fgprocess_data): Don't believe the ACK of an out of order packet. * uucico.c (asProtocols): Added 'v'. * prot.h (fvstart): Declare. * protg.c (fvstart): New function. Sun Jan 2 15:34:12 1994 Ian Lance Taylor (ian@airs.com) * uucico.c (main), uucp.c (main), uux.c (main): Pass -I argument to invoked program. * uustat.c (JOB_REJUVENATE): Define. (asLongopts): Add "rejuvenate-all". (main, ususage): Handle -R. (fsworkfile_show, fsexecutions): Handle JOB_REJUVENATE. * system.h (fsysdep_touch_file): Declare. * unix/statsb.c (issettime): Rename from ussettime. (fsysdep_touch_file): Create. * Jim Avera: system.h: (INIT_NOCLOSE): Define. * unix/init.c (usysdep_initialize): If INIT_NOCLOSE is set, don't close all open descriptors. * Allen Delaney: tli.c: Don't declare t_alloc if we have , since it can cause conflicts. * configure.in: Call AC_CONST. * config.h.in: Added #undef const for configure to comment out. * uucp.h: Don't undefine const here. * Spider Boardman: uucico.c (main): Correct error message. uux.c (main), uucp.c (main): Call uucico with -C option. * tstuu.c (uprepare_test): Don't put the obsolete pty command into the port file. * spawn.c (ixsspawn): Set close-on-exec flag for both ends of new pipe. * Andrew A. Chernov: unix/serial.c (ICLEAR_IFLAG): Clear IMAXBEL if it is defined. (ICLEAR_LFLAG): Clear PENDIN if it is defined. * send.c (flocal_send_file_init): If stat fails, discard the command and save the temporary file. (flocal_send_fail): Cleaned up zsysdep_save_temp_file call. Thu Dec 23 00:55:22 1993 Ian Lance Taylor (ian@airs.com) * Martin Tomes: spawn.c (ixsspawn): On ISC, call __setostype before execve. Wed Dec 22 00:06:25 1993 Ian Lance Taylor (ian@airs.com) * uuconf/tport.c (ipunknown): Set UUCONF_CMDTABRET_EXIT if an error is found. * uucico.c (asLongopts): Add --login as a synonym for -u. (main): Permit a privileged user to use -u to set the login name rather than always using zsysdep_login_name (). (flogin_prompt): Accept login name as an argument. If non-null, use it rather than prompting for one. (uhelp): Document new --login option. * uucico.8: Document new --login option. * unix/priv.c: New file, containing fsysdep_privileged. * unix/statsb.c (fsysdep_privileged): Moved to priv.c. * unix/MANIFEST, unix/Makefile.in: Support new priv.c file. * uuchk.c (ikshow_port): Print a note when using the port name as a device name. Tue Dec 21 00:01:40 1993 Ian Lance Taylor (ian@airs.com) * uucico.c (fcall): Ignore status file times in the future when deciding whether a retry is permitted. * detach.c (usysdep_detach): If it forks, output a debugging message with the old and new process IDs. * Scott Ballantyne: unix/spawn.c (ixsspawn): If fkeepuid is TRUE, try to set the real user and group ID to the effective user and group ID. This will not work on System V derived systems, but should do no harm. * unix/xqtsub.c (fsysdep_execute): Pass fkeepuid as TRUE to ixsspawn. * unix/epopen.c (espopen): Likewise. * uucico.c (faccept_call): Use correct default for max-remote-debug. * uuconf/tportc.c (ipdialer): Don't core dump if the port name is NULL, as it is for the default port. * unix/xqtsub.c (fsysdep_xqt_check_file): Do not permit the name ``..'', or strings starting with ``../''. * proti.c (fijstart): Send a fourth byte in the SYNC packet with the number of channels. (fiprocess_packet): If a SYNC packet has a fourth byte, use it to set the number of channels. * rec.c (flocal_rec_await_reply): Handle RN9 (no channels available on remote). * send.c (flocal_send_await_reply): Handle SN9 (no channels available on remote). * trans.h (struct sdaemon): Added cchans field. * uucico.c (fcall, fdo_call, faccept_call): Initialize cchans. * trans.c (utchanalc, fcheck_queue, floop): Use qdaemon->cchans, not qdaemon->qproto->cchans. * send.c (flocal_send_request, flocal_send_await_reply): Likewise. * rec.c (fremote_send_fail): Likewise. Sun Dec 19 19:44:31 1993 Ian Lance Taylor (ian@airs.com) * proti.c (cIack_frequency): New static variable. (asIproto_params): New protocol parameter ack-frequency. (fijstart): If cIack_frequency is not set, set it to half the window size. (fishutdown): Clear cIack_frequency. (fiprocess_data): Use cIack_frequency to determine when to send an acknowledgement, rather than always sending one at half the window size. * uuconf/cmdfil.c (uuconf_cmd_file): Free zline. * uuconf/callin.c (uuconf_callin): Treat colon as a field delimiter, for Unix /etc/passwd support. * unix/xqtsub.c (zsysdep_find_command): If file named with absolute path does not exist, give a reasonable error message. * uuconf/rdperm.c (ihadd_norw): Ignore use of empty string with NOREAD or NOWRITE, rather than denying everything. * Chip Salzenberg: uulog.c (main): Set zluser correctly under HAVE_HDB_LOGGING. * Chip Salzenberg: protz.c (izrecv_hdr): Use %lx, not %x. Sun Dec 12 19:24:35 1993 Ian Lance Taylor (ian@airs.com) * uucp.c (uccopy): Null terminate name of forwarding system. Mon Nov 22 21:12:41 1993 Ian Lance Taylor (ian@airs.com) * unix/tmpfil.c: Include "uudefs.h". * unix/Makefile.in: Changed accordingly. * log.c (zstpcpy): New function. (ulog): Output to log file with a single call to fprintf. * uucp.c (uccopy): Clarified "not permitted to send" error. * log.c (ulog): If debugging is on, output all log messages to debugging file. * uucico.c (fdo_call): Changed "Bad initialization string" error message. * unix/lock.c (fsdo_lock): Print date a stale lock was last modified. * uucico.c (uaccept_call_cleanup): Call ulog_system (NULL). Sun Nov 21 17:04:27 1993 Ian Lance Taylor (ian@airs.com) * Joe Wells: policy.h: Added new parameter LOG_DEVICE_PREFIX. * unix/serial.c (fsserial_open): Use it. * Makefile.in: Always use CFLAGS as well as LDFLAGS when linking. * Joe Wells: policy.h: Added new parameter QNX_LOG_NODE_ID. * log.c (ulog): Log the QNX node ID if QNX_LOG_NODE_ID is set. * Joe Wells: unix/serial.c: Support QNX dev_info function for serial port locking. * Joe Wells: unix/fsusg.c: Support QNX disk_space function. * unix/Makefile.in: fsusg.o now depends upon uudefs.h. * Joe Wells: policy.h: Changed PS_PROGRAM default for __QNX__. Added HAVE_QNX_LOCKFILES. Rearranged LOCKFILE defines to permit some default selections. * sysh.unx: Removed LOCKFILES sanity check. * unix/lock.c (fsdo_lock, fsqnx_stale), unix/serial.c (fsserial_lockfile), unix/statsb.c (fsysdep_lock_status): Added support for HAVE_QNX_LOCKFILES. * configure.in, config.h.in, policy.h: Moved MAIL_PROGRAM to policy.h. Added MAIL_PROGRAM_TO_BODY and MAIL_PROGRAM_SUBJECT_BODY. * unix/mail.c: Updated accordingly. * uucico.c (main): Don't make -p imply -e. (uhelp): Modified accordingly. * uucico.8: Modified accordingly. Mon Nov 1 21:34:36 1993 Ian Lance Taylor (ian@airs.com) * uucico.c (main): Call fconn_close and fconn_open rather than calling fconn_reset. * conn.h (struct sconncmds): Removed pfreset field. (fconn_reset): Removed declaration. * conn.c (fconn_reset): Removed. * tcp.c (ftcp_reset): Removed. (ftcp_open): Save pid in ssysdep_conn information. (ftcp_close): If pid has changed, return FALSE. * tli.c (ftli_reset): Removed. (ftli_open): Save pid in ssysdep_conn information. (ftli_close): If pid has changed, return FALSE. * unix/pipe.c (fspipe_close): Replaced with fspipe_reset body. (fspipe_reset): Removed. (fspipe_dial): Call fspipe_close, not fspipe_reset. * unix/serial.c (fsserial_reset, fsstdin_reset): Removed. (fsysdep_modem_begin_dial): Hangup terminal here, rather than calling fconn_reset. * send.c (fremote_rec_reply): If we want to request hangup, send an M after the mode. * rec.c (flocal_rec_await_reply): If there an M after the mode, the remote is requesting a hangup. Sun Oct 31 23:43:40 1993 Ian Lance Taylor (ian@airs.com) * uux.c (zXnames): New static variable to hold list of file names being sent. (uxadd_name): Function to add a new name. (main, uxadd_send_file): Call uxadd_name. (main): Include zXnames in log message. Mon Oct 18 00:23:27 1993 Ian Lance Taylor (ian@airs.com) * proti.c (fijstart): Ensure that packet size and window size are reasonable; restrict window size to 16. * proti.c (iIforced_remote_winsize): Removed, along with all references. (asIproto_params): Removed "remote-window". Sun Oct 17 22:15:14 1993 Ian Lance Taylor (ian@airs.com) * Mark Delany: protg.c (cGremote_duprrs): New static variable. (fgstart): Initialize it. (fgshutdown): Count rejects as cGremote_duprrs + cGremote_rejects. (fgprocess_data): If cGremote_rejects is non-zero, don't treat duplicate RR as reject. Count duplicate RR's in cGremote_duprrs, not cGremote_rejects. * Mark Delany: unix/serial.c (fsdouble_chat, fsysdep_conn_chat): After running a chat program, reread the terminal characteristics. Wed Oct 13 20:46:46 1993 Ian Lance Taylor (ian@airs.com) * uucico.c (fdo_call): Fix typo. Thu Oct 7 22:28:45 1993 Ian Lance Taylor (ian@airs.com) * unix/app3.c (zsappend3), unix/app4.c (zsappend4), unix/ftw.c (ftw), unix/sindir.c (zsysdep_in_dir): Don't duplicate '/' character for root directory. * send.c (flocal_send_await_reply): If an SN comes in while the file is being sent, seek to the end rather than setting fsendfile to FALSE. (flocal_send_cancelled): Don't send an empty packet. * trans.c (utransfree): Set e field to EFILECLOSED when debugging. (floop): Check for file send cancelled at top of loop, not middle. * uucp.h (ffileseekend): Define. Wed Oct 6 00:51:08 1993 Ian Lance Taylor (ian@airs.com) * proti.c (fisenddata, fiprocess_packet): Report channel numbers in debugging messages. Tue Oct 5 00:00:33 1993 Ian Lance Taylor (ian@airs.com) * unix/statsb.c (fsysdep_lock_status): Only report the status of a particular job once, no matter how many lock files it has. * uustat.c (fsnotify): Added itime argument. Changed all callers. Report time job was queued in mail message. * unix/cusub.c (fsysdep_terminal_raw): For TERMIO and TERMIOS, clear IXON, IXOFF and IXANY (TERMIO only) in c_iflag. * Lele Gaifax: log.c (ustats): Report device name. * log.c (ulog): Use zsysdep_base_name of zProgram. * uuxqt.c (main): Accept local system name and unknown system names for -s argument. zsysdep_get_xqt may return an alias. Wed Sep 29 00:13:39 1993 Ian Lance Taylor (ian@airs.com) * proti.c (fiprocess_packet): If sending an ACK for a NAK, don't also send a packet. * unix/serial.c (fsysdep_modem_end_dial): If TIOCWONLINE is not defined, reopen the port to wait for carrier. * policy.h: Use __ultrix__ as well as ultrix in check for HAVE_STRIP_BUG. Tue Sep 28 22:25:05 1993 Ian Lance Taylor (ian@airs.com) * Marcel Waldvogel: uuchk.c (ukshow): Don't die if the call out file can not be opened. Sun Sep 19 00:16:01 1993 Ian Lance Taylor (ian@airs.com) * Jason Molenda: policy.h (HAVE_SEQUENT_LOCKFILES): New configuration parameter. * sysh.unx, unix/serial.c: Implement it. * uulog.c (main): Ignore any errors when trying to canonicalize the system name. * Marcel Waldvogel: uucico.c (faccept_call): If the calling system is already locked, and we are using sequence numbers for it, increment the local sequence number to keep in synch. * unix/sleep.c (usysdep_sleep): If usysdep_pause is accurate, use it. Otherwise call sleep, but always for at least two seconds. * chat.c (fcsend): Call usysdep_sleep with 1, not 2. * unix/pause.c: Correct USE_SELECT_TIMER to HAVE_SELECT. * unix/serial.c (fsmodem_open): Only turn on hardware flow control for an incoming connection. (fsmodem_carrier): Turn on hardware flow control after turning on carrier. Turn off hardware flow control before turning off carrier. * uuxqt.c (uqdo_xqt_file): Use known system name, not system name from execution file, unless the former is a prefix of the latter. Sat Sep 18 16:53:41 1993 Ian Lance Taylor (ian@airs.com) * policy.h: Add HAVE_ENCRYPTED_PASSWORDS configuration parameter. * callin.c: Change interface to use a passed in comparison function. * uuconf.h: Change declaration of uuconf_callin. * uucico.c (flogin_prompt): Change call to uuconf_callin. (icallin_cmp): New function. Handle HAVE_ENCRYPTED_PASSWORDS. * Hans-Dieter Doll: chat.c (fchat): Permit \W at the end of an expect string to specify a timeout. * util.c (zremove_local_sys): New function. * uudefs.h: Declare zremove_local_sys. * uucp.c (main): Read local system information. Ignore local system name in front of arguments. * uux.c (main): Ignore local system name in front of arguments. * configure.in: Call AC_HAVE_POUNDBANG, AC_STAT_MACROS_BROKEN, AC_TIME_WITH_SYS_TIME, AC_STRUCT_TM. Call AC_SUBST(POUNDBANG). Remove HAVE_SYS_TIME_AND_TIME_H check. Rework disk space configuration to actually check for the functions. Check for function dev_info. Don't confuse HAVE_FTW_H and HAVE_FTW (from Joe Wells). * config.h.in (STAT_MACROS_BROKEN, TM_IN_SYS_TIME, STAT_DUSTAT, STAT_DISK_SPACE, HAVE_DEV_INFO): New macros set by configure. (TIME_WITH_SYS_TIME): Renamed from HAVE_SYS_TIME_AND_TIME_H. * Makefile.in (POUNDBANG): Set to @POUNDBANG@. (uusched, uuto): If POUNDBANG = no, turn #!/bin/sh into :. (config.status): Use config.status --recheck. (configure): Chdir to $(srcdir) before running autoconf. * sysh.unx: If STAT_MACROS_BROKEN, undefine S_ISDIR. * log.c, time.c, uustat.c, unix/loctim.c: If TM_IN_SYS_TIME, include , not . * tstuu.c, unix/pause.c, unix/proctm.c, unix/serial.c: Rename HAVE_SYS_TIME_AND_TIME_H to TIME_WITH_SYS_TIME. * fsusg.c: Check STAT_DUSTAT, not _AIX and _I386. * config.h.in: Renamed from conf.h.in. * MANIFEST, configure.in, Makefile.in, lib/Makefile.in, unix/Makefile.in, uuconf/Makefile.in, uucp.h: conf.h renamed to config.h. Fri Sep 17 00:36:16 1993 Ian Lance Taylor (ian@airs.com) * Joe Wells: policy.h: If __QNX__, default to HAVE_POSIX_TERMIOS. * Joe Wells: Makefile.in (FORCE): Add dummy command to work around QNX make bug. * Makefile.in, lib/Makefile.in, unix/Makefile.in, uuconf/Makefile.in: Add .PHONY declaration for appropriate commands. * Joe Wells: Makefile.in (install): Create $(man1dir) and $(man8dir) if necessary. (install-info): Create $(infodir) if necessary. * Joe Wells: sysh.unx (bsgrade): Declare as returning int rather than char, since it can return a negative number. * unix/work.c (bsgrade): Define as returning int. * Joe Wells: unix/lock.c (fsdo_lock), unix/statsb.c (fsysdep_lock_status): Use pid_t rather than int for variables that hold pid's. Cast to long when using printf. * Joe Wells: uucico.c (fcall): Fix test for 24 hour check when too many retries. * Joe Wells: uucico.c (fcall), unix/opensr.c (esysdep_open_receive), unix/recep.c (fsysdep_already received): Cast values in multiplication to determine seconds per day or per week to long, because result is larger than 16 bits. * Joe Wells: uuconv.c: Add return 0 after exit to avoid warnings. Thu Sep 16 23:53:58 1993 Ian Lance Taylor (ian@airs.com) * Joe Wells: configure.in: Set AR from environment, defaulting to ar, and substitute it in Makefiles. * Makefile.in: Set AR to @AR@. Pass it down in MDEFINES. * lib/Makefile.in, unix/Makefile.in, uuconf/Makefile.in: Set AR to @AR@. Use $(AR) instead of ar. Use rc instead of qc (POSIX.2 does not define q). Wed Sep 15 00:47:33 1993 Ian Lance Taylor (ian@airs.com) * uuconf/callin.c (uuconf_callin): Take an additional argument: a function to call to transform the login name and password. This is a hack to avoid requiring escape sequence handling in uuconf. * uucico.c (flogin_prompt): Pass cescape to uuconf_callin. This is an incompatible change. * uuconf.h (uuconf_callin): Update declaration. * tstuu.c (uprepare_test): Use \s in password in Call1 and Pass2. * chat.c (fcsend, fcprogram): Expand escape sequences in callout login names and passwords. This is an incompatible change. * Joe Wells: uustat.c (fsnotify): Add missing break statement. * Mark Eichin: tstuu.c (main): Add some sleeps in the children to make the tests more robust on Linux. * uulog.c (ulhelp): Clean up general usage message: don't show -F for HDB_LOGGING, don't show -x for non HDB_LOGGING. Remove mention of numeric debugging levels. * uustat.c (ushelp): Remove mention of numeric debugging levels. * unix/serial.c (ICLEAR_CFLAG): Removed CLOCAL. (enum tclocal_setting): New enum. (fsserial_lock): Don't call TIOCSCTTY. (fsserial_open): Changed flocal argument to tlocal. Use it to determine initial CLOCAL setting for TERMIO and TERMIOS. Don't call TIOCSCTTY until after setting the terminal state. (fsstdin_open): Call fsserial_open with IGNORE_CLOCAL. (fsmodem_open): Call fsserial_open with SET_CLOCAL if calling out, CLEAR_CLOCAL if waiting for an incoming call. (fsdirect_open): Call with SET_CLOCAL or CLEAR_CLOCAL depending upon fcarrier setting. * uuconf.h (struct uuconf_direct_port): Added fcarrier field. * uuconf/tportc.c (asPdirect_cmds): Added ``carrier'' command. (_uuconf_iport_cmd): Initialize direct fcarrier field to FALSE. * uuconf/hport.c (uuconf_hdb_find_port), uuconf/vport.c (uuconf_v2_find_port): Set direct fcarrier field to FALSE. * uuchk.c (ikshow_port): Print direct port carrier field. * uuconv.c (uvwrite_taylor_port): Likewise. * uustat.c (main, fsquery, fsquery_systems, fsquery_show): Support -o, -y, -s and -S in conjunction with -q. Tue Sep 14 00:51:50 1993 Ian Lance Taylor (ian@airs.com) * log.c (ulog): If we can't open the log file, print an error on stderr. * configure.in, conf.h.in: Adjusted for autoconf 1.5. Sun Sep 12 15:52:29 1993 Ian Lance Taylor (ian@airs.com) * unix/serial.c (fsserial_open): Add flocal argument. Changed all callers. Pass it as TRUE when dialing out on a modem. This is supposedly required on 386bsd. * conn.c (fconn_dial_sequence): New function. (fmodem_dial): Use fconn_dial_sequence. Call fsysdep_modem_begin only once, before entire sequence, and fsysdep_modem_end only once, after entire sequence. Don't call fcdo_dial. (fcdo_dial): Removed. * conn.h: Declare fconn_dial_sequence. * uucico.c (fconn_call): Don't free dialer if fconn_dial fails. * uuconf.h (struct uuconf_tcp_port): Add pzdialer field. * tcp.c (ftcp_dial): Pass new pzdialer field to fconn_dial_sequence. * tli.c (ftli_dial): Pass pzdialer to fconn_dial_sequence. * uuconf/hport.c (uuconf_hdb_find_port): Add trailing dialer sequence to pzdialer field for TCP port. * uuconf/tportc.c (asPtcp): Add ``dialer-sequence'' command. (_uuconf_iport_cmd): Initialize pzdialer for TCP port. * uuconf/vsinfo.c (_uuconf_iv2_system_internal): Initialize pzdialer for TCP port. * uuchk.c (ikshow_port): Print TCP pzdialer field. * uuconv.c (uvwrite_taylor_port, ivwrite_hdb_port): Output TCP pzdialer field. Sat Sep 11 16:30:17 1993 Ian Lance Taylor (ian@airs.com) * uulog.c, uuname.c (main): Pass INIT_NOCHDIR to usysdep_initialize. * uucp.1, uustat.1, uux.1, uuxqt.8: Remove uses of nonportable .EX and .EE macros. * uuxqt.c (asQcmds, iqout, iqfile, iqrequestor, iquser): Remove restrictions on number of arguments to commands in execution file, since is there is such a range of buggy UUCP implementations out there. * sysh.unx (CORRUPTDIR): Define. * unix/corrup.c: New file for new zsysdep_save_corrupt_file function to save a file in CORRUPTDIR. * unix/Makefile.in, unix/MANIFEST: Add corrup. * system.h: Add declaration of zsysdep_save_corrupt_file. * uuxqt.c (uqdo_xqt_file): If execution file has a syntax error, save it using zsysdep_save_corrupt_file and notify OWNER. * uuconf/hsinfo.c (_uuconf_ihdb_system_internal), vsinfo.c (_uuconf_iv2_system_internal): Treat a specified time/grade as both a timegrade and a call-timegrade. * rec.c (frec_file_end): If the received file can not be moved to the final location, and there is enough disk space, keep the file, mentioned the saved name in the error message, and send mail to OWNER about it. If the hand created execution file can not be moved, delete it. * unix/move.c (fsysdep_move_file): Don't delete the original file if the move fails. * unix/splcmd.c (zsysdep_spool_commands): Remove the temporary file if the move fails. Wed Sep 1 23:29:30 1993 Ian Lance Taylor (ian@airs.com) * uuconf/tinit.c (itdebug, _uuconf_idebug_cmd): New functions. (asCmds): Call itdebug for "debug", to accept spaces between options as well as commas. * uuconf/tsinfo.c (iidebug): New function. (asIcmds): Call iidebug for "debug". * uuconf/uucnfi.h: Added prototype for _uuconf_idebug_cmd. Tue Aug 31 00:09:33 1993 Ian Lance Taylor (ian@airs.com) * send.c (flocal_send_file_init): Don't set flocal if job was requested by a remote user. (flocal_send_fail, flocal_send_open_file): Don't save temporary file in .Preserve if job was requested by a remote user. * unix/wldcrd.c (fsysdep_wildcard_start): Don't free zcmd until after calling espopen. * lib/buffer.c (ubuffree): Added debugging code controlled by DEBUG_BUFFER macro. Sun Aug 29 13:33:21 1993 Ian Lance Taylor (ian@airs.com) * uuconf/tcalou.c: Permit empty password in call file. * unix/work.c (COMMANDS_PER_SCAN): New macro. (fsysdep_get_work_init): Get at most COMMANDS_PER_SCAN new command files, to avoid timeouts while reading a large directory. * rec.c (fremote_send_file_init): Initialize crestart. * uux.c (main): Changed special handling of single "-" argument to call getopt multiple times. * D.J. James: protg.c (fgsendcmd, fgsenddata), prott.c (ftsendcmd): Avoid passing 0 to bzero to avoid SunOS bug. * protf.c (ffprocess_data): Some systems seem to send characters with parity, so strip the parity bit from incoming bytes. * Kenji Rikitake: uucp.h: Changed order of header files to avoid gcc stddef.h vs. sys/stdtypes.h problem on SunOS 4.1. * Alexander Lehmann: configure.in: Correct misspelling of HAVE_GETWD. * John Hood: unix/filnam.c (ZCHARS): Get the alphabet right. * Gabor Kiss: tcp.c (ftcp_dial): Use all gethostbyname info before calling getservbyname. Thu Aug 26 23:15:33 1993 Ian Lance Taylor (ian@airs.com) * uux.c, uuxqt.c: Added long options. * uucp.c: Added v to getopt_long argument, print help and version info to stderr. * unix/splcmd.c (zsysdep_spool_commands): Create command file via temporary file, so that the command file is created atomically. * unix/spool.c (zscmd_file): Accept files starting with "TMP". Sun Jul 25 14:50:41 1993 Ian Lance Taylor (ian@airs.com) * uupick.c, uustat.c: Added long options. Mon Jul 19 22:06:19 1993 Ian Lance Taylor (ian@airs.com) * uucico.c, uuconv.c, uucp.c, uulog.c, uuname.c: Added long options. * uucico.c (main, fcall): Made -c option not print the ``No work'' log message. * uuname.c (main): Call ulog_uuconf rather than unuuconf_error. Sun Jul 11 14:29:39 1993 Ian Lance Taylor (ian@airs.com) * cu.c, uuchk.c: Added long options. * uudefs.h, log.c (zProgram): Renamed from abProgram. * cu.c, uucico.c, uucp.c, uulog.c, uuname.c, uupick.c, uustat.c, uux.c, uuxqt.c (main): Initialize zProgram from argv[0]. * Bob Hemedinger: unix/cohtty.c (fscoherent_disable_tty): Almost always return TRUE. * unix/serial.c (fsserial_lockfile): Skip "LCK.." in string passed to lockttyexist and fscoherent_disable_tty. * uucico.c (main): If __COHERENT__ is defined, change the meaning of -c for backward compatibility with old Coherent UUCP. * David Nugent: uucico.c (main): Added -C option to only call system named by -s or -S if there is work. * uuconf/syssub.c (_uuconf_isystem_default): Merge in default protocol parameters so that setting parameters for one protocol does not lose the default settings for other protocols. * unix/lcksys.c (zssys_lock_name): New function. (fsysdep_lock_system, fsysdep_unlock_system): Use it. * John Plate: uuchk.c (ukshow): Call ukshow_size with the right arguments in the called remote case. * uuconf/remunk.c (uuconf_remote_unknown): use the remote.unknown shell script if HDB_CONFIG and no ``unknown'' commands appeared in the config file. * Jim Brownfield: uuconf/vsinfo.c (_uuconf_iv2_system_internal): Accept continuation lines in L.sys. * Marc Evans: unix/serial.c (fsysdep_conn_write, fsysdep_conn_io): Add casts to t_snd calls to avoid warnings. * Julian Stacey: uuchk.c (main): If no information found, say so. * Ju"rgen Fluk: uulog.c (main): Better error messages for HDB. * uucico.c (zget_typed_line): If last string ended in \r, ignore leading \n. * Mark E. Mallett: uuconf/time.c (asTdays): Add "none". * uuconf/hsinfo.c (_uuconf_ihdb_system_internal): Report line numbers for syntax errors. Sat Jul 10 10:28:03 1993 Ian Lance Taylor (ian@airs.com) Initial hardware flow control support from Peter Wemm: * uuconf.h (struct uuconf_modem_port, struct uuconf_direct_port): New field uuconf_fhardflow in each structure. * unix/serial.c (fsserial_hardflow): New routine. Initially supports SunOS and SCO Unix. (fsmodem_open, fsdirect_open): Turn on hardware flow control if supported by the port. (fsserial_set): If CRTFSL is set, don't send XON/XOFF characters. * uuconf/hport.c (uuconf_hdb_find_port), uuconf/tportc.c (_uuconf_iport_cmd), uuconf/vport.c (uuconf_v2_find_port): Initialize uuconf_fhardflow field to TRUE. * uuconf/tportc.c (struct asPmodem_cmds, struct asPdirect_cmds): Added "hardflow" command. * uuchk.c (ikshow_port): Report whether hardware flow control is available. * uuconv.c (uvwrite_taylor_port): Write out hardware flow control information. * Peter Wemm: protg.c (fgstart), proti.c (fijstart): Report local packet and window size as well as remote. * rec.c (fremote_send_file_init), send.c (flocal_send_open_file, fremote_rec_reply): Report number of bytes being sent or received, and restart point if any. * Peter Wemm: trans.h (struct sdaemon): New fields csent and creceived. * uucico.c (fcall, faccept_call): Initialize csent and creceived. (fdo_call, faccept_call): Report on number of file bytes transferred and bytes per second. * rec.c (frec_file_end): Record number of bytes received. * send.c (fsend_wait_confirm): Record numbers of bytes sent. * trans.c (ufailed): Record number of bytes sent or received. * Peter Wemm: uusched.in, uuto.in: Use #!/bin/sh rather than :. Use exec when invoking program. * uulog.c (main): Don't die if we can't canonicalize the -s argument. * unix/cusub.c (uscu_child): Force the descriptor into blocking mode. Port type pipe support contributed by Marc Boucher: * unix/pipe.c: New file. Support routines for pipes. * unix/MANIFEST, unix/Makefile.in: Adjusted for new file pipe.c. * uuconf.h (enum uuconf_porttype): Added UUCONF_PORTTYPE_PIPE. (struct uuconf_pipe_port): New structure. (struct uuconf_port): Added uuconf_pipe_port to union. * sysh.unx (struct ssysdep_conn): Add fields ord, owr and ipid, rename istdout_flags to iwr_flags. (fsdouble_{read, write, chat}): New prototypes. * conn.h: Prototype for fsysdep_pipe_init. * unix/serial.c: Renamed fsstdin_{read, write, chat} to fsdouble_{read, write, chat}. Made them non-static. Changed them to use ord and owr fields rather than 0 and 1. (fsserial_init, fsstdin_open): Initialize ord and owr fields. (fsstdin_close, fsblock, fsstdin_reset, fsysdep_conn_io, fsstdin_break, fsstdin_set): Use ord and owr fields rather than 0 and 1. * uuconf/tportc.c (asPtype_names): Added "pipe". (asPpipe_cmds, CPIPE_CMDS): New array of pipe commands. (CCMDS, _uuconf_iport_cmd): Adjusted accordingly. * tcp.c (fsysdep_tcp_init), tli.c (fsysdep_tli_init): Initialize new ord and owr fields. * conn.c (fconn_init): Call fsysdep_pipe_init for UUCONF_PORTTYPE_PIPE. * unix/cusub.c (zsport_line, uscu_child, fsysdep_shell): Handle UUCONF_PORTTYPE_PIPE. * uuchk.c (ikshow_port): Report on port type pipe. * uuconv.c (uvwrite_taylor_port): Write out port type pipe. * Marc Boucher: cu.c: (main, ucuabort): Use new variable fCuconnprinted to avoid printing ZDISMSG if ZCONNMSG has not been printed. (main): Call fsysdep_port_access only after we have locked the port, to get a better error message on systems with shared lines. * Marc Boucher: policy.h (HAVE_FULLDUPLEX_PIPES): New macro. * unix/spawn.c (ixspawn): Use it. * Marc Boucher: uucico.c (uusage): Added lines for -c and -D. * uuconf/time.c (_uuconf_itime_parse): Add casts to avoid a compiler warning. * uustat.c (fsworkfile_show): Don't report non-existent send files. * lib/parse.c (fparse_cmd): Accept any base for the mode argument, rather than always using 8. Depend upon the leading zero to indicate base 8. Accomodates UFGATE 1.03. Wed Jun 30 00:27:27 1993 Ian Lance Taylor (ian@airs.com) * uudefs.h (struct scmd): Changed bdummy field to bgrade. * trans.c (fqueue_send): Sort sends by whether they are a command and then by grade. * unix/work.c (asSwork_files): Renamed from azSwork_files, made array of struct ssfilename rather than char *. (struct ssfile): Added bgrade field. (iswork_cmp, fsysdep_get_work_init, usysdep_get_work_freed): Changed accordingly. (fsysdep_get_work): Set qcmd->bgrade. * uucp.c (uccopy), uux.c (main, uxadd_send_file), uuxqt.c (uqdo_xqt_file), xcmd.c (fremote_xcmd_init), lib/parse.c (fparse_cmd): Initialize bgrade field of scmd structure. Sun Jun 27 23:21:33 1993 Ian Lance Taylor (ian@airs.com) * send.c (flocal_send_await_reply, flocal_send_cancelled): If the first D. file being sent for a faked E command fails, send the second one anyhow. Sun Jun 6 23:07:33 1993 Ian Lance Taylor (ian@airs.com) * proti.c (fiprocess_data): If we get a packet we sent a NAK for, forget that sent NAKs for all preceding packets. (fiprocess_packet): If we get a NAK for the packet we are about to send, and all our packets have been acknowledged, send an ACK. Thu Jun 3 20:54:55 1993 Ian Lance Taylor (ian@airs.com) * prot.h (struct sprotocol): Added frestart field. * uucico.c (asProtocols): Initialize frestart field. * system.h, unix/opensr.c (zsysdep_receive_temp): Added frestart argument to zsysdep_receive_temp. * rec.c (flocal_rec_send_request, fremote_send_file_init, frec_file_end): Pass frestart argument to zsysdep_receive_temp. * unix/opensr.c (esysdep_open_receive): Permit pcrestart argument to be NULL. * rec.c (flocal_rec_await_reply, fremote_send_file_init): Pass pcrestart argument to esysdep_open_receive as NULL if file tranfers can not be restarted. * lib/status.c (azStatus): Uwe Doering: If SPOOLDIR_HDB or SPOOLDIR_SVR4, use the same strings they use. * unix/status.c (aiMapstatus): Uwe Doering: Swap 4 and 20. * unix/serial.c (fsserial_open): Uwe Doering: Set VTIME to 1. * uucico.c (faccept_call, uaccept_call_cleanup): Uwe Doering: Free and unlock evertyhing after any return from faccept_call. (main): Don't need to unlock after faccept_call here any more. * proti.c (fiprocess_data): Added additional debugging information. Sat May 15 13:55:21 1993 Ian Lance Taylor (ian@airs.com) * protg.c (fgprocess_data): Don't treat a duplicate RR as an RJ if we are retransmitting packets. If we are treating a duplicate RR as an RJ, don't also treat it as an acknowledgement. * unix/serial.c (fsysdep_conn_io): Typo in debugging message. Tue May 4 00:03:32 1993 Ian Lance Taylor (ian@airs.com) * uux.c (main): Andreas Raab: Move aboptions out of local block since a pointer to it escapes the scope. * unix/mkdirs.c: W Christopher Martin: Just try to make the directories, and ignore EEXIST errors, rather than first checking whether the directory exists. * send.c (flocal_send_request): Chip Salzenberg: Double check that the file still exists before sending the S command. * uucico.c (zget_uucp_cmd, zget_typed_line), trans.c (fgot_data): Matthew Geier: Avoid doing memcpy (z, NULL, 0). Mon May 3 22:52:46 1993 Ian Lance Taylor (ian@airs.com) * system.h, unix/locfil.c, unix/cwd.c, unix/picksb.c: Johan Vromans: Added pfbadname argument to zsysdep_local_file, zsysdep_local_file_cwd, zsysdep_uupick_local_file. * Changed all callers. * send.c (fremote_rec_file_init), rec.c (fremote_send_file_init): If remote system gives bad name, return an error rather than aborting the connection. * uuxqt.c (uqdo_xqt_file): If bad file name, abort execution rather than try again later. * uupick.c (main): If bad file name, permit new command rather than exiting. * lib/debug.c: Stephan Niemz: Accept whitespace separated debugging types. * unix/detach.c: Always use setsid if it is available. Sun May 2 13:23:33 1993 Ian Lance Taylor (ian@airs.com) * unix/spool.c (zsfind_file): Fix handling of execution file names for systems to work with any possible execution file name. * send.c (flocal_send_open_file): Subtract starting position from number of bytes passed to pffile. * uuconf/rdperm.c: Syd Weinstein: Don't skip lines in Permissions with leading whitespace. * uuconf/vsinfo.c: Gero Kuhlmann: Set default retry time correctly. * unix/lock.c (fsdo_lock): Andrew Vignaux: Handle readonly lock files correctly. * send.c (flocal_send_fail, flocal_send_await_reply): James Van Artsdalen: Clarify error messages relating to execution files. * log.c (ustats): Avoid overflow in bytes/sec calculation. Sat May 1 17:40:14 1993 Ian Lance Taylor (ian@airs.com) * trans.c (ftadd_cmd): Don't treat junk at end of command as a size if the remote system doesn't support sizes. * uucico.c (faccept_call): Turn on the protocol before reading the queue, in case there are lots of command files. * unix/cusub.c: Julian Stacey: If SIGUSR2 is not defined, use SIGURG instead. * uuconf/syshdr.unx (MAKE_ABSOLUTE): New macro. * uuconf/tinit.c (itaddfile): Renamed from itadd. Use MAKE_ABSOLUTE to force absolute pathnames to configuration files. * conn.c (fconn_close): Steve M. Robbins: Ignore any SIGHUP received after closing the connection. * cu.c (main): Frank Conrad: When an alternate fails, move on to the next one. * uucico.c (faccept_call): Alexei K. Yushin: Supposedly some UUCP's send UgG rather than just Ug. * unix/serial.c (fsserial_lockfile): Bob Hemedinger: Fix error message in HAVE_COHERENT_LOCKFILES case. * unix/mkdir.c: Andy Fyfe: Pass fkeepuid as TRUE to ixsspawn. * unix/strerr.c: Undefine strerror in case there is a macro definition which configure did not pick up. * configure.in: Andy Fyfe: AT&T 3b1 has sys/mount.h but not statfs. * uudir.c: Andy Fyfe: Include uucp.h. * unix/fsusg.c: Andy Fyfe: Typos in (untested) STAT_USTAT case. * unix/filnam.c: Eric Lee Green: Avoid generating filenames that only differ in case, to make life easier for bad filesystems. * uuconf/llocnm.c: Brian J. Murrell: Don't read HDB files if ``hdb-files no'' given. Sat Mar 20 16:10:20 1993 Ian Lance Taylor (ian@airs.com) * uudefs.h (eSendfile, eRecfile): Deleted obsolete declarations. Sat Feb 13 15:57:30 1993 Ian Lance Taylor (ian@airs.com) * Released version 1.04. * unix/detach.c: Andrew A. Chernov: Don't check return of setsid. Sun Jan 31 01:45:56 1993 Ian Lance Taylor (ian@airs.com) * cu.c (main): Pass "cu" to uuconf_init. * protz.c (fzprocess): Restore ZPAD char before calling getinsync. Sat Jan 30 22:19:26 1993 Ian Lance Taylor (ian@airs.com) * Makefile.in (doc-dist): New target. Wed Jan 27 22:55:26 1993 Ian Lance Taylor (ian@airs.com) * protg.c (fgstart): Set iGremote_segsize when using remote-packet-size. Tue Jan 26 01:01:34 1993 Ian Lance Taylor (ian@airs.com) * proti.c (fiprocess_data): always send an ACK after receiving half a window, rather than sometimes resending a packet. Half a window of short packets can arrive very quickly. * tstuu.c (main, cread, fsend): rewrote communication routines to avoid deadlock. Sun Jan 24 01:02:47 1993 Ian Lance Taylor (ian@airs.com) * trans.c (ufailed): don't report statistics if no bytes transferred. * Makefile.in (install): simplified somewhat. (dist): distribute the sample directory. Sat Jan 23 19:47:12 1993 Ian Lance Taylor (ian@airs.com) * configure.in, conf.h.in, tli.c: Karl Swarz: check for and use . Fri Jan 22 00:09:37 1993 Ian Lance Taylor (ian@airs.com) * send.c (flocal_send_request): Alan Judge: don't send C in option string when faking an E command as an S command. Thu Jan 21 00:09:31 1993 Ian Lance Taylor (ian@airs.com) * uux.c (main): don't use E command if forwarding. Wed Jan 20 00:22:38 1993 Ian Lance Taylor (ian@airs.com) * send.c (fsend_exec_file_init), rec.c (frec_file_end), uux.c (main): Chip Salzenberg: always put the C line last in an execution file, to support Fredmail. Tue Jan 19 00:09:43 1993 Ian Lance Taylor (ian@airs.com) * trans.h, trans.c (ftcharge, floop, fgot_data): rewrote timing code. * trans.h, trans.c, send.c, rec.c, xcmd.c, protf.c, protz.c (fqueue_local, fqueue_remote, fqueue_send, fqueue_receive): added boolean return value and qdaemon argument. Mon Jan 18 00:01:46 1993 Ian Lance Taylor (ian@airs.com) * uucico.c (fdo_call, faccept_call): Ted Lindgreen, Chip Salzenberg: wait for remote hangup string before hanging up. * proti.c (fiprocess_data, fiprocess_packet): stop scanning input buffer after a CLOSE packet. Sat Jan 16 22:44:28 1993 Ian Lance Taylor (ian@airs.com) * system.h, uucico.c (main), uuxqt.c (main), unix/init.c: Ted Lindgreen: eliminated INIT_DAEMON. * log.c (ulog): don't log SIGINT if fLog_sighup is FALSE. * unix/move.c (fsysdep_move_file), unix/xqtsub.c (fsysdep_move_uuxqt_files): the system call rename seems to fail on some systems for arbitrary reasons, so always try to copy the file by hand, not just if we get EXDEV. * policy.h, unix/pause.c: Gregory Gulik: added HAVE_HUNDREDTHS_NAP configuration parameter. Wed Jan 6 21:06:45 1993 Ian Lance Taylor (ian@airs.com) * unix/serial.c (fsserial_lockfile): create HDB lock files when using HAVE_COHERENT_LOCKING. unix/cohtty.c (fscoherent_disable_tty): consistently return FALSE on error. * unix/cusub.c (fsysdep_terminal_raw): Andrew A. Chernov: if POSIX_TERMIOS, turn of IEXTEN flag. Sat Jan 2 23:19:27 1993 Ian Lance Taylor (ian@airs.com) * protg.c (fgprocess_data): treat a duplicate RR as an RJ. Fri Jan 1 11:17:30 1993 Ian Lance Taylor (ian@airs.com) * policy.h, unix/proctm.c: Steven S. Dick: use sysconf (_SC_CLK_TCK) for TIMES_TICK if possible. * uuconf/diacod.c: Gregory Gulik: accept an empty dialcode string. * system.h, uucico.c (main), uucp.c (main), uux.c (main), unix/run.c: Karsten Thygesen: removed ffork argument from fsysdep_run. Wed Dec 30 00:21:55 1992 Ian Lance Taylor (ian@airs.com) * unix/link.c: Andrey G Blochintsev: don't fail just because destination directories do not exist. * send.c (flocal_send_open_file): Scott Ballantyne: record file name when logging send of execution command. * protz.c: Chip Salzenberg: reformatted to 80 columns. Tue Dec 29 23:50:52 1992 Ian Lance Taylor (ian@airs.com) * uuconv.c (uvwrite_time): scott@geom.umn.edu: handle midnight more correctly. Fri Dec 18 00:49:16 1992 Ian Lance Taylor (ian@airs.com) * system.h, uucp.c (uccopy), uux.c (main), cu.c (icuput, icutake), unix/ufopen.c (esysdep_user_fopen): Doug Evans: open files used for %put and %take using esysdep_user_fopen, rather than with privileges of uucp. Added frd and fbinary arguments to esysdep_user_fopen. Thu Dec 17 00:04:53 1992 Ian Lance Taylor (ian@airs.com) * unix/picksb.c (zsysdep_uupick): Peter Wemm: allocation error. * uupick.c (main): Peter Wemm: pass INIT_GETCWD to usysdep_initialize; really quit if 'q' is typed. * uulog.c (main): Peter Wemm: always canonicalize system name, not just if using HDB_LOGGING. * uudefs.h, log.c (ustats), trans.c (ufailed), send.c (fsend_await_confirm), rec.c (frec_file_end): Peter Wemm: added fmaster argument to ustats, used only in HDB_LOGGING. Wed Dec 16 23:35:51 1992 Ian Lance Taylor (ian@airs.com) * uustat.c (main): Marc Unangst: forgot to call strtol for -y. * policy.h, sysh.unx: Brian J. Murrell: yet another configuration parameter: HAVE_BROKEN_SETREUID. Tue Dec 15 00:13:04 1992 Ian Lance Taylor (ian@airs.com) * uuconv.c (uvwrite_taylor_system): mnichols@pacesetter.com: use command-path rather than path. * trans.c (floop): Marc Unangst: don't clear frequested_hangup if we didn't manage to hang up. * uucp.h, rec.c (fremote_send_file_init): Oleg Girko: patches to make code compile if USE_STDIO is 0. * unix/proctm.c: Tim Peiffer: reverse sense of TIMES_TICK check in hopes of avoiding ISC preprocessor bug. * unix/fsusg.h, unix/fsusg.c, unix/bytfre.c, system.h, conf.h.in, configure.in, unix/Makefile.in, unix/MANIFEST: use new disk space checking routines from GNU fileutils 3.4. * unix/opensr.c (zsysdep_receive_temp): don't check free space here any more. * policy.h, trans.h, trans.c, rec.c, uucico.c, uudefs.h: Chip Salzenberg: check amount of remaining space on disk every FREE_SPACE_DELTA bytes, and abort the file transfer if disk space gets too low. Wed Dec 2 00:24:12 1992 Ian Lance Taylor (ian@airs.com) * policy.h, unix/serial.c (fsserial_set): Frank Conrad: added HAVE_PARITY_BUG parameter for the Sony NEWS. Mon Nov 30 00:06:59 1992 Ian Lance Taylor (ian@airs.com) * lib/spool.c (fspool_file): Andrew Chernov: accept any alphanumeric character in the name, because it could be a grade from another system. Sun Nov 29 22:36:47 1992 Ian Lance Taylor (ian@airs.com) * lib/buffer.c (ubuffree): scott@geom.umn.edu, Richard Gumpertz: use a temporary variable to hold the offsetof result. * configure.in: scott@geom.umn.edu: define HAVE_SYS_SELECT_H correctly. * protg.c (fgsend_control): Niels Baggesen: report all non-RR packets if DEBUG_ABNORMAL. * unix/cusub.c (uscu_child): Ed Carp: apparently the read and write calls can get EAGAIN on some systems. * unix/status.c (fsysdep_get_status, fsysdep_set_status): Chip Salzenberg: map status values when using SPOOLDIR_HDB. * rec.c (fremote_send_reply): do file restart correctly for E commands. Sun Nov 22 15:09:43 1992 Ian Lance Taylor (ian@airs.com) * protz.c: Chip Salzenberg: always do bitwise operations on unsigned values. * getopt.h: Chip Salzenberg: don't rely on __STDC__. Thu Nov 19 00:13:46 1992 Ian Lance Taylor (ian@airs.com) * uuconf/freblk.c: Niels Baggesen: loop over the right list. * uulog.c (main): Peter Wemm: added -D, -F and -S options, made -f take an argument and default to showing 10 current lines. (ulusage): added new options and missing old ones. Wed Nov 18 22:26:36 1992 Ian Lance Taylor (ian@airs.com) * rec.c (frec_file_end): Andrey G Blochintsev: call fsysdep_remember_reception as soon as the file has been moved to the final destination; write fake execution file via a temporary file to prevent uuxqt from getting at it early. * trans.c (usent_receive_ack): don't call fsysdep_remember_reception here. * unix/tmpfil.c (ZDIGS): don't use '.', since we use it to separate parts of the file name. Sun Nov 15 15:31:49 1992 Ian Lance Taylor (ian@airs.com) * uustat.c (fsquery_show, csunits_show): Marc Unangst, Chip Salzenberg: line up uustat -q output. * sysh.unx, ftw.c (ftw_dir, ftw), srmdir.c (isremove_dir), walk.c (iswalk_dir): Marc Unangst: stat argument to function argument to ftw is const. * unix/serial.c (fsserial_set): Mike Bernson: set CSIZE correctly when changing parity. * uux.c (main): Andrew A. Chernov: check for executions which name the local system, to handle dumb mailers. * uucp.h: Doug Evans: #undef strerror if HAVE_STRERROR is 0, to avoid macro definition on Xenix. * unix/serial.c (fsserial_set): Peter Wemm: only check CRTSCTS if HAVE_POSIX_TERMIOS. * cu.c (main): Peter Wemm: use alternates for systems if a call fails. * tstuu.c (uprepare_test): Gerben Wierda: set execute bits for Chat1 and Chat2. * trans.c (floop): Marc Unangst: don't hang up when requested unless the send queue is empty. * uuxqt.c (iqrequestor): Marc Boucher: new function to accept R command with two arguments, as generated by UUPC. * uucico.c (faccept_call): Christian Seyb: don't free the system info until after writing the status. * configure.in: Marc Boucher: check -lsocket and -lnsl together. * unix/portnm.c: Stephen J. Walick: it's types.tcp.h, not tcp.types.h. * configure.in: Brian Campbell: check for /usr/bin/mailx. Sat Nov 14 11:11:04 1992 Ian Lance Taylor (ian@airs.com) * uuconf/hlocnm.c (uuconf_hdb_login_localname): Christian Seyb: check for _uuconf_unset as well as NULL. * conn.c (fconn_dial): initialize *ptdialerfound. * many files: rearranged header files to include "sysdep.h" before system header files. Also eliminated various pedantic warnings, and made _uuconf_unset char * to avoid possible alignment problems. Tue Nov 10 00:16:35 1992 Ian Lance Taylor (ian@airs.com) * trans.h, uucico.c (fcall, faccept_call), trans.c (uclear_queue, floop): Stephen J. Walick: move clean up from end of floop into uclear_queue, and call it instead of just doing usysdep_get_work_free. * unix/serial.c (fsserial_lockfile): Marc Unangst: bad #endif location for HAVE_SVR4_LOCKFILES. (fsserial_init): Doug Evans: null terminate the device name. Sun Nov 8 10:58:59 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (fcall, faccept_call): Stephen J. Walick: call usysdep_get_work_free here. trans.c (floop): don't call usysdep_get_work free here. Sun Nov 1 17:05:07 1992 Ian Lance Taylor (ian@airs.com) * Released gamma version 1.04. * configure.in: check that sys/select.h and sys/time.h work together, since that's how they are currently used. * cu.c, uustat.c, uuconf/diacod.c: add casts to eliminate warnings. * configure.in: don't add strlwr to LIBOBJS. * policy.h, unix/cohtty.c: Bob Hemedinger: finish Coherent style locking. Wed Oct 28 00:20:15 1992 Ian Lance Taylor (ian@airs.com) * tstuu.c: Ralf Stephan: check HAVE_POLL_H and HAVE_STROPTS_H. * Nickolay Saukh: accept SVR4 style R request file position. uudefs.h: added ipos field to struct scmd. lib/parse.c: accept SVR4 style R request with file position to start from. send.c (fremote_rec_file_init): start transferring file from requested position. uucp.c, uux.c, uuxqt.c, xcmd.c: initialize ipos field. Sun Oct 25 10:39:23 1992 Ian Lance Taylor (ian@airs.com) * unix/serial.c (fsysdep_conn_write, fsysdep_conn_io): T. William Wells: take special care to ensure we don't write after SIGHUP. * policy.h, sysh.unx, unix/MANIFEST, unix/Makefile.in, unix/serial.c (fsserial_lockfile), unix/cohtty.c (new file): Bob Hemedinger: added HAVE_COHERENT_LOCKFILES. * unix/cusub.c (uscu_child): Igor V. Semenyuk: accept a 0 return from read until we have read some data at some point. Thu Oct 22 10:38:32 1992 Ian Lance Taylor (ian@airs.com) * proti.c: various tweaks for bad connections. * uucp.h: T. William Wells: rename strcasecmp and strncasecmp, if the system doesn't provide them, to avoid the ANSI C name space. * lib/buffer.c: Bob Hemedinger: put ab in union so that offsetof will not take the address of an array. * uuxqt.c (uqdo_xqt_file): Bob Hemedinger: don't take address of array. Wed Oct 21 00:05:31 1992 Ian Lance Taylor (ian@airs.com) * uustat.c (fsnotify): Gert Doering: if the file appears to be binary, don't include it in any mail message. * unix/mkdir.c: Michael Yu.Yaroslavtsev: check whether directory already exists before spawning /bin/mkdir. * proti.c: Michael Yu.Yaroslavtsev: iIsendpos and iIrecpos should be long. * send.c (flocal_send_await_reply): Gert Doering: improved error messages. * tli.c, unix/detach.c: include "sysdep.h" before . * configure.in, conf.h.in: added some system specific checks provided by autoconf. * tstuu.c, unix/serial.c: Merlyn LeRoy: check for ENODATA as well as EAGAIN and EWOULDBLOCK. * uucico.c (faccept_call): Zacharias J. Beckman: if calling back, clear status first. * uucico.c (fdo_call, faccept_call): Hans-Dieter Doll: avoid overflow when turning ulimit value into bytes. Tue Oct 20 23:12:26 1992 Ian Lance Taylor (ian@airs.com) * serial.c (fsmodem_carrier): Hans-Dieter Doll: use IS68K LNOMDM bit if available. * chat.c (fcsend): Hans-Dieter Doll: advance z after EOT. * cu.c: T. William Wells: beep on connected and disconnected messages (only if ANSI_C, to use \a). * unix/run.c: Peter Wemm: pass fsetuid as TRUE to ixsspawn. Sun Oct 18 13:58:17 1992 Ian Lance Taylor (ian@airs.com) * policy.h, unix/serial.c (fsmodem_close): Stephen J. Walick: added HAVE_RESET_BUG for SCO Xenix. * configure.in: Igor V. Semenyuk: avoid looking in -linet for getline, since ISC has a different function there by that name. * unix/ufopen.c: Igor V. Semenyuk: handle unsigned uid_t. Sat Oct 17 11:00:30 1992 Ian Lance Taylor (ian@airs.com) * conf.h.in, configure.in, uucp.h, unix/serial.c (fsserial_lockfile), lib/MANIFEST: eliminated strlwr. Fri Oct 16 01:10:56 1992 Ian Lance Taylor (ian@airs.com) * Igor V. Semenyuk: uuchk.c (ukshow): print max-remote-debug correctly. lib/debug.c (idebug_parse): accept DEBUG_NONE. Thu Oct 15 00:49:58 1992 Ian Lance Taylor (ian@airs.com) * unix/cusub.c (fsysdep_terminal_puts): don't modify zalc before freeing it up. * protg.c (fgcheck_errors, fggot_ack, fgprocess_data): Mark E. Mallett: better handling of error decay. Wed Oct 14 22:09:20 1992 Ian Lance Taylor (ian@airs.com) * unix/lock.c: Tomi Vainio: make sure SEEK_SET is defined. * tcp.c (ftcp_dial): print a better error message if gethostbyname doesn't set errno. * Stephen J. Walick: configure.in, conf.h.in: check for . tcp.c, unix/opensr.c: include if available. lib/debug.c, unix/portnm.c, uuconf/int.c, uuconf/llocnm.c, uuconf/time.c: cast more arguments to eliminate more warnings. Tue Oct 13 00:25:03 1992 Ian Lance Taylor (ian@airs.com) * prot.h, proti.c (fistart, fijstart), protj.c, uucico.c, tstuu.c (uprepare_test), Makefile.in, MANIFEST: added 'j' protocol. Sun Oct 11 23:45:20 1992 Ian Lance Taylor (ian@airs.com) * policy.h, unix/serial.c (fsserial_set): added HAVE_STRIP_BUG to policy.h to get around stupid Ultrix bug. * sysh.unx, unix/cusub.c, unix/serial.c (fsserial_open): for HAVE_BSD_TTY, keep tchars and ltchars in the sterminal structure, and in fsserial_open disable all interrupt characters. Sat Oct 10 01:18:31 1992 Ian Lance Taylor (ian@airs.com) * uuconf/tinit.c (itunknown): Gert Doering: don't save "unknown" with the other arguments. Fri Oct 9 00:56:43 1992 Ian Lance Taylor (ian@airs.com) * unix/lock.c: check for running process before doing kill. Thu Oct 8 00:20:12 1992 Ian Lance Taylor (ian@airs.com) * chat.c, protf.c, send.c, rec.c, unix/locfil.c: Stephen J. Walick: cast arguments to strtol and strcspn to avoid warnings. * uustat.c (fsnotify): Marc Boucher: don't free string from uuconf_localname, and only prepend remote system name to execution requests, not to local UUCP commands. * unix/lock.c (fsdo_lock): Marc Boucher: set fret to TRUE before going around the loop again. * uucico.c: Marc Boucher: use 'a' protocol before 'g'. * unix/spool.c (zsfind_file): Matthias Zepf: fixed typos for SPOOLDIR_BSD*. Wed Oct 7 00:03:08 1992 Ian Lance Taylor (ian@airs.com) * uuname.c (main): Marc Boucher: reverse sense of -a, and do not display aliases by default. * uucico.c (fdo_call): Marc Boucher: some systems only provide 14 characters in the Shere line. * tstuu.c (main): Marc Boucher: add support for STREAMS ptys. Tue Oct 6 23:16:15 1992 Ian Lance Taylor (ian@airs.com) * policy.h: Marc Boucher: improve comments to describe SVR4. * chat.c (fcsend, fcecho_send, fcecho_send_strip, fcecho_send_nostrip): Marc Boucher: don't send CR after BREAK or EOT, and let chat-seven-bit apply to echo checking. * uuname.c (main): Andreas Vogel: usysdep_exit (TRUE) rather than usysdep_exit (EXIT_SUCCESS). Mon Oct 5 22:59:51 1992 Ian Lance Taylor (ian@airs.com) * sysh.unx, unix/serial.c (fsserial_init): Marc Boucher: avoid freeing unallocated string. * unix/serial.c (fsmodem_carrier): Peter Wemm: eliminated useless undeclared variable which only appeared if HAVE_CLOCAL_BUG. * cu.c (main): don't require carrier when opening a direct line. (fcudo_cmd, fcudo_subcmd, uculist_fns, icuunrecogfn): T. William Wells: give reasonable error messages. Sun Oct 4 00:03:10 1992 Ian Lance Taylor (ian@airs.com) * */Makefile.in: T. William Wells: use ar qc rather than ar rc. * many: T. William Wells: renamed isysdep_* functions to ixsysdep_*, and renamed isfork, isspawn, and isswait similarly, to avoid ANSI C namespace restrictions. * uucp.h: T. William Wells: default size_t to unsigned, not int. * configure.in: T. William Wells: new definition for AC_RETSIGTYPE. * configure.in: T. William Wells: test for sh builtin echo. conf.h.in: default ECHO_PROGRAM to undefined. * proti.c (fiprocess_data, fiprocess_packet): fix confusion between iIremote_winsize and iIrequest_winsize. * proti.c (fiwindow_wait, fisenddata): wait for a window opening before sending SPOS. * proti.c (fiprocess_data): don't send a NAK for a duplicate of the most recent packet. * configure.in: Stephen J. Walick: don't use AC_PREFIX, check for /usr/bin/mail. * system.h, sysh.unx, send.c (flocal_send_file_init, fsend_exec_file_init), rec.c (flocal_rec_file_init, fremote_send_file_init, frec_file_end), xcmd.c (fremote_xcmd_init), uuxqt.c (uqdo_xqt_file, uqcleanup), uux.c (main, uxadd_send_file), uucp.c (main, uccopy), uustat.c (fsworkfile_show, fsexecutions, fsnotify), unix/filnam.c (zsfile_name, zsysdep_data_file_name, zsysdep_xqt_file_name), unix/jobid.c (zsfile_to_jobid, zsjobid_to_file), unix/splcmd.c (zsysdep_spool_commands), unix/splnam.c (zsysdep_spool_file_name), spool.c (zsfind_file), statsb.c (fskill_or_rejuv, isysdep_work_time), work.c (fswork_file, fsysdep_get_work, zsysdep_jobid, bsgrade): Marc Unangst, Brian Murrell: Corrected support for SPOOLDIR_SVR4, since SVR4 doesn't use grades in file names. Changed flocal argument to pseq argument in zsysdep_spool_file_name, and changed flocal argument to bgrade argument in zfind_file. Added fxqt argument to zsysdep_data_file_name. Added bsgrade function. Added bgrade argument to zsfile_to_jobid, and pbgrade argument to zsjobid_to_file. Sat Oct 3 11:03:13 1992 Ian Lance Taylor (ian@airs.com) * MANIFEST, Makefile.in, lib/MANIFEST, lib/Makefile.in, lib/parse.c: moved parse.c from main directory to lib. * system.h, unix/size.c, unix/Makefile.in, unix/MANIFEST: moved csysdep_size into its own file, made it return -1 if the file does not exist or -2 on other errors. uustat.c (fsworkfile_show): handle errors from csysdep_size. send.c (flocal_send_file_init): handle errors from csysdep_size, removed unneeded calls to fsysdep_file_exists. * trans.c (flocal_poll_file), tcp.c (ftcp_dial): Bob Cunningham: declare functions consistently static. * Makefile.in: Marc Unangst: don't run config.status unnecessarily. * configure.in: Marc Unangst: check for socket and t_open in -lsocket, -lnsl and -lxti. * uuconf/cmdarg.c: check first character to avoid calls to strcmp or strcasecmp. Thu Oct 1 23:44:24 1992 Ian Lance Taylor (ian@airs.com) * trans.h, uucico.c (fdo_call, faccept_call), parse.c (fparse_cmd), send.c (flocal_send_request): Gert Doering: SVR4 UUCP uses a dummy string between the notify field and the size, for some reason. * tstuu.c (main, uprepare_test): added -n switch to not destroy existing configuration files. Fri Sep 25 00:16:35 1992 Ian Lance Taylor (ian@airs.com) * protg.c (fgsenddata): T. William Wells: clear bytes correctly so that resending a packet doesn't get a completely incorrect size. * send.c (usadd_exec_line): Stephen J. Walick: don't send trailing spaces on the created execute file, because it confuses Waffle. Thu Sep 24 00:25:18 1992 Ian Lance Taylor (ian@airs.com) * unix/jobid.c (zsjobid_to_file): Franc,ois Pinard: if the job ID is too short, return NULL rather than dumping core. unix/statsb.c (fskill_or_rejuv, isysdep_work_time): handle a NULL return from zsjobid_to_file. Mon Sep 21 09:01:02 1992 Ian Lance Taylor (ian@airs.com) * uuconf/init.c, uuconf/syssub.c: Lele Gaifax: moved declaration of _uuconf_unset from syssub.c to addstr.c because NeXT linker does not pull in object files solely because of variable declarations. * sysh.unx: Lele Gaifax: typo in ftw declaration. * lib/Makefile.in, unix/Makefile.in: Lele Gaifax: bug in clean target. Thu Sep 17 01:01:13 1992 Ian Lance Taylor (ian@airs.com) * Released beta version 1.04. Wed Sep 16 01:02:55 1992 Ian Lance Taylor (ian@airs.com) * uux.c (main): null terminate the options list for an 'E' command. * ustat.c (fsexecutions): allow privileged users to kill remote execution files, and handle local executions correctly. * uuconf/hinit.c: added parens to avoid warning. * unix/splcmd.c: cast to avoid warning. * unix/serial.c (fsmodem_close): fixed HAVE_SYSV_TERMIO typo. * trans.c (uqueue_receive, floop, fgot_data): improved timing code to make fewer system calls. * send.c (fsend_exec_file_init, fsend_exec_file): handle separate E file correctly, and make a good statistics file entry for it. * Makefile.in, unix/Makefile.in, uuconf/Makefile.in, lib/Makefile.in: use -I flags to permit compilation in a separate directory. Set up clean targets per GNU standards. Tue Sep 15 00:07:09 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (zget_uucp_cmd): can't set size_t variable to -1. * Makefile.in (install): don't install info files. Added new targets info and install-info. Mon Sep 14 13:19:42 1992 Ian Lance Taylor (ian@airs.com) * uuxqt.c (main): Gregory Bond: canonicalize the system name given by the -s argument. * system.h, uuconf.h, uucico.c (faccept_call), unix/unknwn.c, unix/Makefile.in, unix/MANIFEST, uuconf/syshdr.unx, uuconf/remunk.c, uuconf/hrmunk.c, uuconf/Makefile.in, uuconf/MANIFEST: support HDB remote.unknown shell script. * protg.c (igchecksum, igchecksum2): Inspired by Mark Pizzolato, put in new, improved checksum routines. * uuxqt.c (uqdo_xqt_file): make sure the execution file still exists after locking it. * unix/lock.c (fsdo_lock): don't fail if the lock file is removed between the link and the open. * unix/xqtsub.c (fsysdep_execute, fsysdep_lock_uuxqt_dir, fsysdep_unlock_uuxqt_dir, fsysdep_move_uuxqt_files): use .Xqtdir for first uuxqt execution, not .Xqtdir0000. Sun Sep 13 11:51:22 1992 Ian Lance Taylor (ian@airs.com) * trans.h, uucico.c (fdo_call, faccept_call), send.c (flocal_send_request), rec.c (flocal_rec_send_request) parse.c (fparse_cmd): send file size in hex for SVR4 compatibility. Required new FEATURE_V103 for 1.03 backward compatibility, since 1.03 requires decimal size. * various: eliminated remaining calls to alloca. * tcp.c (ftcp_open), tli.c (ftli_open): set FD_CLOEXEC for sockets and TLI descriptors. * tcp.c (ftcp_open): switch to real user ID before binding the socket when running as a server. This will permit uucico invoked by root to open privileged TCP ports. Don't switch to real ID if effective ID is already root, to permit an suid root program to be invoked by anybody. * uuxqt.c (uqdo_xqt_file): removed special case for system which does not permit any commands: unnecessary and unusual. * uucico.c (fconn_call): Ed Carp: clear the SIGHUP signal indication before opening the modem. * trans.h, trans.c (fqueue, fcheck_queue, floop, fgot_data), send.c (fsend_await_confirm), rec.c (frec_file_send_confirm), uucico.c (fcall, faccept_call): recheck the work queue every 10 minutes. Honor CYM from the remote system. Send CYM if we have something to do. Sat Sep 12 15:47:52 1992 Ian Lance Taylor (ian@airs.com) * Makefile.in: use $(MAKE) instead of make for recursive calls. * system.h, uucp.c (main), uux.c (main), unix/ufopen.c, unix/MANIFEST, unix/Makefile.in: added esysdep_user_open to open a file with user permissions. Fri Sep 11 00:27:32 1992 Ian Lance Taylor (ian@airs.com) * uudefs.h, copy.c: added fcopy_open_file. * policy.h: added HAVE_SAVED_SETUID. * configure.in, conf.h.in: check for setreuid. Tue Sep 8 00:11:10 1992 Ian Lance Taylor (ian@airs.com) * protf.c (ffsendcmd), prott.c (ftsendcmd): eliminate calls to alloca. * uucico.c (main), uuxqt.c (main), uux.c (main), uucp.c (main), uustat.c (main), uuchk.c (main), uuconv.c (main), uuname.c (main), uulog.c (main), uupick.c (main), cu.c (main), lib/getop1.c, lib/Makefile.in, lib/MANIFEST: added getopt_long, and changed all calls to getopt to call getopt_long instead. Mon Sep 7 22:26:51 1992 Ian Lance Taylor (ian@airs.com) * getopt.h, lib/getopt.c, lib/Makefile.in: bring getopt up to glibc 1.04; call malloc instead of alloca in exchange. * system.h, uucico.c (main), uuxqt.c (main), uux.c (main), uucp.c (main), uustat.c (main), cu.c (main), uuname.c (main), unix/init.c (usysdep_initialize): added INIT_SUID, for old systems which don't do setuid correctly for root. * cu.c, unix/cusub.c: various minor improvements. Sun Sep 6 20:25:20 1992 Ian Lance Taylor (ian@airs.com) * uux.c (uxcopy_stdin): use getchar rather than fread to avoid SVR4 bug. * uucico.c (fsend_uucp_cmd): Niels Baggesen: report message when DEBUG_HANDSHAKE. * protg.c (fgsend_control): Niels Baggesen: report sending an RJ when DEBUG_ABNORMAL. Tue Aug 25 00:07:20 1992 Ian Lance Taylor (ian@airs.com) * uuconf/time.c: Zacharias Beckman: let user defined time tables override the defaults. Mon Aug 24 00:25:23 1992 Ian Lance Taylor (ian@airs.com) * system.h, uuxqt.c (uqdo_xqt_file), unix/xqtsub.c (zsysdep_xqt_local_file): Jarmo Raiha: expand ~name in uuxqt.c. * send.c (fremote_rec_reply): SVR4 sends the size of the file with the RY string, so we do too. We don't look for it, though. * uustat.c, uustat.1: Don Phillips: removed all printing of years and seconds. Hope nobody complains. * uucico.c (fdo_call): don't set the status to TALKING until we see the Shere string. * configure.in, conf.h.in, unix/wldcrd.c: if the system has glob, use it for wildcards. If it doesn't, quote special characters in the wildcard string. * uucico.c (fdo_call): Zacharias Beckman: don't report ``Login successful'' until we see the Shere string. * prot.c (fsend_data): Don Lewis: bug in crec calculation. * uustat.c (fsworkfile_show, usworkfile_header, fsnotify): Don Lewis: show poll files. * unix/init.c: check LOGNAME and USER environment variables before invoking getlogin. * unix/serial.c: Brian Campbell: check for B57600, B76800 and B115200 in baud rate table. Sun Aug 23 13:05:28 1992 Ian Lance Taylor (ian@airs.com) * chat.c (fcsend), tstuu.c (uchild): Chip Salzenberg: call sleep (2) instead of sleep (1). Hopefully this won't break any chat scripts. * system.h, parse.c, trans.c (fqueue, flocal_poll_file), uustat.c (fsworkfiles_system, fsquery_system), unix/work.c (fsysdep_get_work, fsysdep_get_work_init): don't delete poll files immediately, but instead return a 'P' command and delete them when the command is passed to fsysdep_did_work. * tstuu.c (uprepare_test): change ``call-request'' to ``request''. * uuconf/iniglb.c (_uuconf_itimetable): return CMDTABRET_KEEP so we don't lose the timetable name and definition. * uuconf.h, send.c (fremote_rec_file_init), rec.c (fremote_send_file_init), uuchk.c (ukshow), uuconv.c (uvwrite_taylor_system, uvwrite_hdb_system), uuconf/tsinfo.c (iirequest), uuconf/hsinfo.c, uuconf/hunk.c, uuconf/syssub.c: added ``send-request'' and ``receive-request'' commands, eliminated ``call-request'' and ``called-request'' commands. * uux.c (main): make sure we are permitted to transfer files before queuing requests. * uuconf.h, uucico.c (fcall), uuconf/tsinfo.c, uuconf/syssub.c: David Nugent: added ``success-wait'' command for systems, to set a minimum time between successful calls. * send.c (fremote_rec_file_init): Don Phillips: let a request only specify the file base name in the TO argument. * uucico.c (main): Don Lewis: don't exit with success just because we were able to start uuxqt. * unix/serial.c (fsmodem_close, fsserial_read): always drop DTR when closing a modem connection. Also, retry if we time out when setting MIN. Sat Aug 22 22:31:34 1992 Ian Lance Taylor (ian@airs.com) * uuconf/time.c: Stephen Walick: don't require a comma between time strings, since HDB doesn't seem to. * protg.c (fgcheck_errors): added "error-decay" protocol parameter to decay errors as packets are successfully received. * uustat.c (fsmachines), uustat.1: Chris Lewis: don't display the year or seconds for uustat -m. Probably uustat -q should be changed as well. * tstuu.c: Larry Fahnoe: don't report EWOULDBLOCK errors when writing to a pty. Also removed functions which are now in lib. * MANIFEST, Makefile.in, uusched.in: added a simple uusched shell script. * parse.c: Heiko Rupp: don't die if there is trailing garbage in an 'R' command. * policy.h, system.h, sysh.unx, send.c, rec.c, uuxqt.c, uux.c, unix/filnam.c, unix/init.c, unix/jobid.c, unix/splnam.c, unix/spool.c, unix/statsb.c, unix/tmpfil.c, unix/work.c, unix/xqtfil.c: Brian J. Murrell and Don Phillips: added SPOOLDIR_SVR4. Thu Aug 20 00:06:32 1992 Ian Lance Taylor (ian@airs.com) * sysh.unx: Chiaki Ishikawa: some systems define some but not all of the S_ file mode bits. * uuchk.c (ikshow_port): Chiaki Ishikawa: display lockname. Wed Aug 19 22:41:39 1992 Ian Lance Taylor (ian@airs.com) * log.c (ustats): Scott Blachowicz: avoid overflow when reporting bytes per second. * unix/lock.c (fsdo_lock): Chip Salzenberg: sometimes other programs create lock files that uucp can't write. * trans.h, system.h, trans.c (floop, fgot_data, usent_receive_ack, uwindow_acked), send.c (flocal_send_await_reply, flocal_send_fail), rec.c (fremote_send_fail_send, frec_file_send_confirm), prote.c, protf.c, protg.c, proti.c, prott.c, protz.c (calls to fgot_data), unix/recep.c, unix/MANIFEST, unix/Makefile.in: keep trace of whether we have already received a file, in case the other side never sees our ack. Added new SN8 rejection, meaning that the file has already been received. Sat Aug 15 11:50:32 1992 Ian Lance Taylor (ian@airs.com) * uuconf/time.c (itadd_span): Don Lewis: fixed bug if later span overlapped two or more earlier spans. Thu Aug 13 00:19:50 1992 Ian Lance Taylor (ian@airs.com) * system.h, rec.c (fremote_send_file_init, fremote_send_reply), uucico.c (fdo_call, faccept_call), uucp.c (main), uux.c (main), unix/opensr.c (zsysdep_receive_temp, esysdep_open_receive): implemented file restart. Wed Aug 12 23:32:05 1992 Ian Lance Taylor (ian@airs.com) * proti.c (fiprocess_data): ensure that the first argument to fgot_data is always > 0 if the second argument is > 0. Mon Aug 10 22:43:40 1992 Ian Lance Taylor (ian@airs.com) * trans.c (floop, ustats_failed): handle half-duplex connections and failed calls correctly. Sun Aug 9 17:56:32 1992 Ian Lance Taylor (ian@airs.com) * proti.c (firesend, fisenddata, ficheck_errors): made several changes to improve performance on a lossy line: can now shrink packet size using SYNC packets, avoids multiple bad header errors in a sequence of INTRO characters, avoids letting one side lock up if a NAK is lost. * configure.in: set HAVE_LONG_FILE_NAMES to 0 if cross-configuring. * tstuu.c: changed -p option to be mod 1000, not mod 100. * MANIFEST, Makefile.in, prot.h, uucico.c, protz.c, trans.c: Doug Evans: added Doug Evans's zmodem implementation as protocol 'a'. Wed Aug 5 22:28:14 1992 Ian Lance Taylor (ian@airs.com) * policy.h, uuconf.h, uucico.c (fcall), uuconf/tsinfo.c, uuconf/hsinfo.c, uuconf/syssub.c: added "max-retries" command for systems, eliminated CMAXRETRIES configuration parameter, set max_retries to 0 for HDB if retry time given, (from Chris Lewis) call once a day even if max_retries has been exceeded. * prot.h, uucico.c (fdo_call, faccept_call), prott.c, prote.c, proti.c, protg.c, protf.c: added pzlog argument to pfstart protocol entry point, changed handshake successful message to display it. Tue Aug 4 00:04:31 1992 Ian Lance Taylor (ian@airs.com) * prot.h, uucico.c, protg.c (fbiggstart, cGshort_packets): Chip Salzenberg: added support for 'G' protocol. Added "short-packets" protocol parameter for 'g' and 'G' protocols. * uuconf.h, rec.c (flocal_rec_file_init), uucp.c, uux.c, uuxqt.c, uuchk.c, uuconv.c, uuconf/local.c, uuconf/tsinfo.c, uuconf/syssub.c: support UUCP forwarding. Added "forward-from", "forward-to", and "forward" commands for systems. * unix/spawn.c: don't close the file descriptor after dupping it. Sun Aug 2 23:04:18 1992 Ian Lance Taylor (ian@airs.com) * trans.c (fremote_hangup_reply): don't hangup if a file transfer is in progress. * send.c (flocal_send_cancelled): don't pass a NULL buffer to pfsenddata. Sun Jul 26 13:28:27 1992 Ian Lance Taylor (ian@airs.com) * unix/work.c (fsysdep_get_work_init): return TRUE if there is no work directory. * configure.in, sysh.unx: don't run any programs in configure if we are cross-configuring; this applies to HAVE_FTIME and HAVE_RESTARTABLE_SYSCALLS. The code can cope with the buggy ftime. If we are cross-configuring, HAVE_RESTARTABLE_SYSCALLS is set to -1, and sysh.unx guesses that if the system has sigvec but not sigaction or SV_INTERRUPT it is on 4.2BSD and system calls are automatically restarted. * configure.in, conf.h.in, tstuu.c, unix/serial.c: removed COMBINED_UNBLOCK configuration parameter, and changed the code which sets O_NONBLOCK and O_NDELAY to drop back to using just O_NONBLOCK if it gets an EINVAL error. * configure.in, conf.h.in, uucp.h, protg.c (fgsenddata), cu.c (icutake), chat.c (icexpect), lib/MANIFEST: removed all calls to memmove, avoiding the SCO bug and making the 'g' protocol slightly more efficient. Sat Jul 25 14:20:30 1992 Ian Lance Taylor (ian@airs.com) * uucp.h, uudefs.h, many other files: broke part of uucp.h out into uudefs.h, stopped including uuconf.h in uucp.h, fixed up .c files to include uudefs.h and uuconf.h as necessary. * uuconf/syshdr.unx, uuconf/callin.c, uuconf/diacod.c uuconf/hdial.c, uuconf/hdnams.c, uuconf/hport.c, uuconf/hsinfo.c, uuconf/hsnams.c uuconf/rdlocs.c, uuconf/tcalou.c, uuconf/tdial.c, uuconf/tdnams.c, uuconf/tport.c, uuconf/vport.c, uuconf/vsinfo.c, uuconf/vsnams.c: changed uuconf library to not return an error if a configuration file does not exist; it now acts as though whatever it is is not found. * tstuu.c (main): use perror if execl fails. * configure.in, conf.h.in, uucp.h, uuconf.h, sysh.unx, conn.h, MANIFEST, Makefile.in, tli.c, chat.c (ccescape), conn.c (fconn_init), tcp.c, uucico.c (faccept_call), uuconv.c, uuchk.c, lib/MANIFEST, lib/Makefile.in, lib/escape.c, unix/cusub.c, unix/serial.c, uuconf/hport.c, uuconf/tportc.c: added support for TLI connections. Moved ccescape from chat.c to cescape in lib/escape.c. Made all connections on Unix use the same system dependent structure. Tue Jul 21 22:08:10 1992 Ian Lance Taylor (ian@airs.com) * uucp.h, trans.h, uucico.c (fdo_call, faccept_call), uuxqt.c (uqdo_xqt_file), uucp.c (main), uux.c (main), uustat.c (fsworkfile_show), parse.c (fparse_cmd), trans.c (fqueue, fgot_data, ftadd_cmd), send.c, rec.c, xcmd.c, protf.c (ffprocess_data), proti.c (fiprocess_data), tstuu.c (uprepare_tests), unix/splcmd.c (zsysdep_spool_commands), unix/statsb.c (fskill_or_rejuv), unix/work.c (fsysdep_get_work): added E request to send file executions which only require reading from standard input. Sat Jul 18 20:22:50 1992 Ian Lance Taylor (ian@airs.com) * proti.c, Makefile.in, MANIFEST, prot.h, system.h, trans.h, uucico.c, prote.c, protf.c, protg.c, prott.c, trans.c, send.c, rec.c, xcmd.c, unix/opensr.c: added 'i' protocol. Added local and remote channel arguments to protocol sendcmd and senddata entry points. Cleaned up send and receive state machines. Removed pfgone argument from esysdep_open_send. Fri Jul 17 09:41:05 1992 Ian Lance Taylor (ian@airs.com) * uuxqt.c (uqdo_xqt_file): only report base name of execution file, not full name. Thu Jul 16 00:45:06 1992 Ian Lance Taylor (ian@airs.com) * lib/crc.c: unroll the loop a bit. * configure.in, conf.h.in, unix/init.c: updated to autoconf 0.120. Wed Jul 15 14:45:32 1992 Ian Lance Taylor (ian@airs.com) * uuconf.h, uuconv.c, uuconf/uucnfi.h, uuconf/reliab.c, uuconf/tportc.c, uuconf/tdialc.c, uuconf/diasub.c, uuconf/hport.c, uuconf/prtsub.c, uuconf/vsinfo.c: added UUCONF_RELIABLE_FULLDUPLEX and "half-duplex" command for ports and dialers. Mon Jul 13 16:53:04 1992 Ian Lance Taylor (ian@airs.com) * prot.h, lib/crc.c, lib/Makefile.in, lib/MANIFEST: added icrc function to compute 32 bit CRC (from Gary S. Brown, via Doug Evans). Sun Jul 12 21:40:15 1992 Ian Lance Taylor (ian@airs.com) * uuconv.c (uvwrite_time): Chris Lewis: don't output two commas in a row. * uuconv.c (uvwrite_taylor_system, uvwrite_taylor_port): Chris Lewis: generate command "protocol", not "protocols". Sat Jul 11 17:09:09 1992 Ian Lance Taylor (ian@airs.com) * xcmd.c (fremote_xcmd_init): Chris Lewis: use qdaemon->puuconf, since puuconf is not defined. * uuconf/syshdr.unx, uuconf/hinit.c (uuconf_hdb_init): Chris Lewis: added HDB_SEPARATOR to insert between oldconfiglib and strings in HDB Sysfiles. * uuconf/syshdr.unx: Chris Lewis: define strerror as a macro. * uuconf/freblk.c, uuconf/free.c: Chris Lewis: don't define as void when ! UUCONF_ANSI_C. Thu Jul 9 09:17:55 1992 Ian Lance Taylor (ian@airs.com) * prot.h, uucico.c (fdo_call, faccept_call), prote.c (festart), protf.c (ffstart), protg.c (fgstart), prott.c (ftstart): no need to pass fmaster as a separate argument to protocol start routine. * protf.c (ffawait_ack, ffawait_cksum): don't try to resend if we don't have a file. Wed Jul 8 14:28:23 1992 Ian Lance Taylor (ian@airs.com) * unix/srmdir.c (fsysdep_rmdir), unix/walk.c (usysdep_walk_tree): cast to char * to avoid warning. * cu.c (main): don't compare boolean to NULL. * unix/serial.c (isblocksigs), unix/signal.c (usset_signal): use extra parens to avoid bug in SCO 3.2.2 sys/signal.h header file. * sysh.unx: always define struct ssysdep_tcp, for the benefit of systems for which HAVE_TCP is 0. * MANIFEST, Makefile.in, unix/Makefile.in, uuconf/Makefile.in, lib/Makefile.in: updated automatic distribution code for multiple directories. * unix/cusub.c, unix/serial.c: don't clobber CR when using TERMIO or TERMIOS, and default MIN to 1 to the convenience of cu. * Makefile.in, uucp.h, system.h, prot.h, trans.h, uucico.c, trans.c, send.c, rec.c, xcmd.c, prot.c, protg.c, protf.c, prote.c, prott.c, log.c, file.c, unix/opensr.c, unix/work.c: rewrote file transfer internals to support bidirectional transfers. Keep queue of jobs to do, and support connections. Added new files trans.h, trans.c, send.c, rec.c, xcmd.c, and removed old file file.c. Mon Jun 29 15:14:15 1992 Ian Lance Taylor (ian@airs.com) * Makefile.in: Stephen J. Walick: copy uustat.1 to uustat.$(manext), not uucp.($manext). Also try to create $(infodir). * chat.c (fcsend, fcprogram): check for NULL return from uuconf_callout. Thu Jun 18 22:37:28 1992 Ian Lance Taylor (ian@airs.com) * configure.in, Makefile.in: updated to autoconf 0.118. Wed Jun 17 14:22:11 1992 Ian Lance Taylor (ian@airs.com) * unix/serial.c (fsserial_init): add /dev if necessary to device as well as to port name. * cu.c (main): set zdevice to NULL when faking line. * cu.c (main), uucp.c (main), uux.c (main), uuxqt.c (main): don't call zsysdep_localname until we've called usysdep_initialize. Tue Jun 16 17:42:50 1992 Ian Lance Taylor (ian@airs.com) * unix/signal.c (usset_signal): set SA_INTERRUPT to force system calls to be interrupted on SunOS. Mon Jun 15 15:10:24 1992 Ian Lance Taylor (ian@airs.com) * everything: integrated uuconf library. Split out lib and unix libraries. Made many changes, including defaults for port and dialer files, better handling of changed local name, better handling of HDB Permissions, new zbufalc routines to manage strings on the heap. Incorporated uuconv. Wed Jun 10 23:51:03 1992 Ian Lance Taylor (ian@airs.com) * uuconf.h, uuconf/Makefile.in, uuconf/locnm.c, uuconf/llocnm.c, uuconf/hlocnm.c, uuconf/tlocnm.c: renamed uuconf_localname to uuconf_login_localname and added new uuconf_localname which doesn't need to read system information. Tue Jun 9 14:19:20 1992 Ian Lance Taylor (ian@airs.com) * uuconf.h, uuconf/Makefile.in, uuconf/local.c: wrote uuconf_system_local. Mon Jun 8 14:14:30 1992 Ian Lance Taylor (ian@airs.com) * policy.h: changed description of LOCKDIR, which now need not always be defined. * uuconf.h, uuconf/uucnfi.h, uuconf/lckdir.c, uuconf/iniglb.c, uuconf/tinit.c, uuconf/Makefile.in: added uuconf_lockdir, and ``lockdir'' command to config. Sat Jun 6 22:07:58 1992 Ian Lance Taylor (ian@airs.com) * configure.in: updated to autoconf 0.115, added code to set LIBOBJS. * uuconf/Makefile.in, uuconf/uucnfi.h: removed references to routines now in lib/, changed to include regular UUCP header files. Fri Jun 5 15:31:29 1992 Ian Lance Taylor (ian@airs.com) * uuconf.h, uuconf/uucnfi.h, uuconf/syssub.c, uuconf/uuconv.c: always set zpubdir for every system, changed uuconf_zpubdir to const char *. Wed Jun 3 15:15:32 1992 Ian Lance Taylor (ian@airs.com) * uuconf.h, uuconf/Makefile.in, uuconf/deblev.c, uuconf/maxuxq.c, uuconf/pubdir.c, uuconf/spool.c: wrote uuconf_debuglevel, uuconf_maxuuxqts, uuconf_pubdir, uuconf_spooldir. * configure.in: updated to autoconf 0.114. * uuconf/tportc.c: default TCP ports to being fully reliable. Mon Jun 1 17:03:22 1992 Ian Lance Taylor (ian@airs.com) * uuconf.h, uuconf/prtsub.c: removed uuconf_psysdep from uuconf_port. Sun May 31 00:07:40 1992 Ian Lance Taylor (ian@airs.com) * uuconf.h, uuconf/Makefile.in, uuconf/diacod.c: wrote uuconf_dialcode. * uuconf.h, uuconf/Makefile.in, uuconf/logfil.c, uuconf/debfil.c, uuconf/stafil.c: wrote uuconf_logfile, uuconf_debugfile, uuconf_statsfile. * uuconf.h, uuconf/Makefile.in, uuconf/callin.c: wrote uuconf_callin. * uuconf/chatc.c, uuconf/time.c: Jean Mehat: only call tolower if isupper is true. * uuconf.h, uuconf/Makefile.in, uuconf/val.c, uuconf/tval.c: wrote uuconf_validate, uuconf_taylor_validate. Sat May 30 12:37:02 1992 Ian Lance Taylor (ian@airs.com) * system.h, sys1.unx: changed zsysdep_local_name to zsysdep_localname, and made it fatal out rather than return NULL. * uuconf.h, uuconf/Makefile.in, uuconf/uucnfi.h, uuconf/iniglb.c, uuconf/rdlocs.c, uuconf/locnm.c, uuconf/tlocnm.c, uuconf/hlocnm.c: wrote uuconf_localname, uuconf_taylor_localname, uuconf_hdb_localname. * uuconf.h, uuconf/Makefile.in, uuconf/uucnfi.h, uuconf/iniglb.c, uuconf/tinit.c, uuconf/tsinfo.c, uuconf/hunk.c, uuconf/unk.c: wrote uuconf_system_unknown, uuconf_hdb_system_unknown, uuconf_taylor_system_unknown. * log.c, time.c: always include in uucp.h. * configure.in, conf.h.in: check for size_t, renamed checks for time_t. * configure.in, conf.h.in: check for . Fri May 29 00:03:05 1992 Ian Lance Taylor (ian@airs.com) * sysinf.c (ztranslate_system): Jac Kersing: must xstrdup the argument, since it points to a buffer that will be reused. Thu May 28 12:42:20 1992 Ian Lance Taylor (ian@airs.com) * sys3.unx (zsysdep_real_file_name): Ted Lindgreen: check return value of zstilde_expand. * copy.c, sys1.unx (usysdep_detach), sys2.unx (fsserial_close), sys3.unx, sys5.unx, sys7.unx: opening /dev/tty in usysdep_detach confuses the NeXT, so instead we just call TIOCNOTTY on 0. In fsserial_close we call TIOCNOTTY on the port before closing it, to make sure that we have ditched it under BSD. Also added O_NOCTTY to every open call other than opening a port, although there are still several fopen calls which should probably have it somehow. * system.h, uucico.c (fcall), uustat.c (fsquery_system, fsquery_show), sys3.unx (fsysdep_get_status), sys7.unx (zsysdep_all_status): Bob Izenberg: changed output of uustat -q to count number of commands rather than number of files being transferred, and to not report a non-existent status. Added pfnone argument to fsysdep_get_status, and changed all calls. * uucico.c, sys1.unx, sys2.unx, sys3.unx, sys5.unx, sys6.unx, sys7.unx: Rolf Nerstheimer: cast a bunch of arguments to open, creat, stat and chmod to avoid compiler warnings. * uucp.h, log.c (ulog), port.c (fport_close), prot.c (fgetcmd): Chip Salzenberg: don't log a SIGHUP signal while we're closing down the connection, since the other side might hang up faster than we do (we still react to it correctly, we just don't put it in the log file). * sys1.unx (usysdep_detach), tcp.c (ftcp_open): Petri Helenius: update the process ID we log after a fork. * Makefile.in, sys1.unx: Chip Salzenberg: changed LIBDIR to SBINDIR. * uucp.c (main, uccopy): Andreas Vogel: check local-receive of the correct system, rather than always using sLocalsys. * configure.in, conf.h.in, sys2.unx, tstuu.c: Rob Janssen: look for , and include it if it exists and we are using select. * protg.c: Rob Janssen: rearrange macros to avoid bug in XENIX compiler. * configure.in: Scott Blachowicz: check WIFEXITED before assuming HAVE_UNION_WAIT, to avoid problems on HP/UX. * configure.in, conf.h.in, sysh.unx, sys1.unx: John Theus: use sv_onstack instead of sv_flags in the sigvec structure on 4.2BSD. Wed May 27 23:23:39 1992 Ian Lance Taylor (ian@airs.com) * policy.h, sys2.unx (fsysdep_modem_no_carrier): Scott Reynolds: added HAVE_CLOCAL_BUG compilation parameter to work around problems on some serial ports. Tue May 26 15:50:17 1992 Ian Lance Taylor (ian@airs.com) * uustat.c, uustat.1: added a bunch of options to support uuclean: -e, -i, -K, -M, -N, -W, -Q. * system.h, sys7.unx (fsysdep_privileged, fskill_or_rejuv): added fsysdep_privileged function. Thu May 21 13:30:21 1992 Ian Lance Taylor (ian@airs.com) * uuxqt.c (uqdo_xqt_file): processing of execution file has to be case significant; this will change handling of "n" flag, which was not correctly handled before. Wed May 20 14:22:12 1992 Ian Lance Taylor (ian@airs.com) * sys1.unx (usysdep_detach): close the statistics file when detaching. * policy.h, sys3.unx (fsdo_lock, fsdo_unlock), sys7.unx (fsysdep_lock_status): force LOCKDIR to always be defined. * uucp.h: put in an extern for alloca. * sysh.unx, sys1.unx, sys5.unx, sys6.unx: defined all the ?_OK macros in sysh.unx, which means that must be included before "sysdep.h" when they are both included. * sys2.unx (fsserial_set): corrected case in termio switch expression. * chat.c (fcsend): simplified expression for old compilers. * sys1.unx (rmdir): wrote rmdir replacement which invokes /bin/rmdir for old systems. * configure.in, conf.h.in, Makefile.in: updated for autoconf 0.112, added checks for ftw, ftw.h, and rmdir. * sys1.unx: added extern for ctime, removed externs for functions returning int, protected externs with ifndefs. * uucp.h, prot.h, system.h, uucico.c (fuucp), uuxqt.c (uqdo_xqt_file), prot.c (freceive_file), file.c (freceived_file), sys3.unx (fsysdep_move_file, fsysdep_change_mode), sys4.unx (zsysdep_save_temp_file): changed fsysdep_move_file to not set the file mode, and added fsysdep_change_mode to do it instead. * system.h, uucp.c (main, ucdirfile, uccopy), sys6.unx (usysdep_walk_tree, isdir, ftw, do_ftw): added -R option to uucp to recursively copy directories. Tue May 19 18:29:32 1992 Ian Lance Taylor (ian@airs.com) * sys3.unx: changed zsysdep_in_dir to always append the filename to the directory, even if the directory did not already exist. * sysh.unx, sys1.unx, sys3.unx, sys4.unx, sys5.unx: renamed fsdirectory_exists to fsysdep_directory. Mon May 18 14:49:35 1992 Ian Lance Taylor (ian@airs.com) * system.h, uucp.c (main), sys6.unx (zsysdep_uuto): added -t option to uucp to emulate uuto, wrote zsysdep_uuto to do Unix dependent destination translation for uuto, added -p option to uucp as synonym for -C for uuto compatibility. Sun May 17 22:04:09 1992 Ian Lance Taylor (ian@airs.com) * protg.c (fgexchange_init): permit a second INITB to override the segment size given in the first INITB. Tue May 5 16:03:22 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (main, fdo_call), uucico.8: Chip Salzenberg: added -c option to uucico to not warn if invoked when the system may not be called. Tue Apr 28 15:05:01 1992 Ian Lance Taylor (ian@airs.com) * sysh.unx, sys2.unx (fsserial_open, fsblock): preserve file status flags. * protg.c (fgwait_for_packet): Heiko Rupp: only send RJ packet if there are no unacknowledged packets. Mon Apr 27 18:56:42 1992 Ian Lance Taylor (ian@airs.com) * system.h: added several routines for cu. * cu.c, cu.h, sys8.unx: checked into RCS. * uux.c (main): Jose Manas: dumb bug when checking against calloc_args. Fri Apr 24 20:32:06 1992 Ian Lance Taylor (ian@airs.com) * sys1.unx: changed HAVE_LONG_NAMES to HAVE_LONG_FILENAMES for new version of autoconf. * sys7.unx: check UTIME_NULL_MISSING with #if rather than #ifdef. * sys3.unx: check FS_* macros with #if rather than #ifdef. * uucp.h, sysh.unx: changed standard type definitions for new version of autoconf. * sysh.unx, sys1.unx, sys2.unx, tstuu.c: changed SIGtype to RETSIGTYPE for new version of autoconf. * sys1.unx, tstuu.c: make include of optional. * sys2.unx: get the right versions of major and minor. Wed Apr 22 11:19:11 1992 Ian Lance Taylor (ian@airs.com) * protg.c (fgsenddata, fggot_ack): Michael Haberler: the slow start after error code was essentially shrinking the window size. * sysh.unx, system.h, sys1.unx (usysdep_initialize), uuchk.c, uucico.c, uucp.c, uulog.c, uuname.c, uustat.c, uux.c, uuxqt.c: changed usysdep_initialize to take a single argument with bit flags, added INIT_NOCHDIR as one of the flags. * uucp.h, log.c (ulog): added pfLstart and pfLend functions for ulog, so that cu can use them to restore the terminal settings. * bnu.c (ubnu_read_systems, fbnu_read_dialer_info), v2.c (uv2_read_systems): Michael Richardson: don't core dump if no chat script. Tue Apr 21 00:19:47 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (faccept_call): Chris Lewis: a successful call in should clear the number of retries. * sys2.unx (fsserial_set): set LLITOUT if going to CBREAK mode. * port.h, prote.c (festart), protf.c (ffstart), protg.c (fgstart), prott.c (ftstart), port.c (fport_set), sys2.unx (fsysdep_stdin_set, fsysdep_modem_set, fsysdep_direct_set, fsserial_set): gave fport_set independent control over output parity generation, input parity checking, and XON/XOFF handshaking, all to support cu. Mon Apr 20 11:47:23 1992 Ian Lance Taylor (ian@airs.com) * port.h, uucico.c (fdo_call), port.c (fport_dial, fmodem_dial), tcp.c (ftcp_dial): added separate zphone argument to fport_dial to support cu. Thu Apr 16 01:15:42 1992 Ian Lance Taylor (ian@airs.com) * bnu.c (ubadd_perm, ubadd_perm_alternate): Chris Lewis: handle a combination of Permissions entries which specify just LOGNAME with entries that specify both MACHINE and LOGNAME. Wed Apr 15 16:11:48 1992 Ian Lance Taylor (ian@airs.com) * sys1.unx (usysdep_initialize, zsysdep_login_name): John Theus: don't die if can't get login name, unless it's really needed. Tue Apr 14 12:39:18 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (main, fcall): Petri Helenius: must relock system after detaching from terminal when trying different alternates. * system.h, uucico.c (fuucp), uustat.c (fsworkfiles_system, fsquery_system), sys4.unx (fsysdep_get_work_init, fsysdep_get_work): Marty Shannon: uustat would remove empty command files. * bnu.c (ubadd_perm_alternate): John Harkin: permit ALIAS in Permissions. * Makefile.in: John Harkin: add sys?.c dependencies to sys?.o to work around old makes which don't handle transitive .SUFFIXES. * sys2.unx: cast some function calls to void. * time.c (qttime_parse): cast to void warning. * sys1.unx (iswait): cast waitpid argument to avoid warning. * configure.in, policy.h, uucp.h, sys7.unx, tstuu.c: Zacharias Beckman: minor touchups for NeXT. * sys1.unx (usysdep_initialize), sys6.unx (zsysdep_add_cwd), uux.c (main): Jarmo Raiha: heuristic for whether to get the current directory can fail. * sys1.unx: pass argument to uudir, cast sigemptyset calls to void. * uucp.texi: Harlan Stenn: correct case of references. Tue Apr 7 01:02:17 1992 Ian Lance Taylor (ian@airs.com) * Released version 1.03. Mon Apr 6 15:49:08 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (faccept_call): Marc Boucher: set *pqsys to NULL. * bnu.c (ubnu_read_systems, fbnu_find_port): Erik Forsberg: support multiple character modem classes. Fri Apr 3 00:37:25 1992 Ian Lance Taylor (ian@airs.com) * sys2.unx: Petri Helenius: only clear known bits in termio or termios structure; didn't change HAVE_BSD_TTY handling--maybe next version. * configure.in: test TIMES_DECLARATION_OK correctly. * Makefile.in: update version to 1.03, remove distclean, add mostlyclean per GNU standards. * sys1.unx, chat.c: minor cleanups for gcc 2.1. Thu Apr 2 17:51:36 1992 Ian Lance Taylor (ian@airs.com) * tstuu.c: conditionally declare times. * uucp.h, prot.c, sysinf.c, prtinf.c: added gcc 2.0 format checking to ulog, and fixed a few problems it discovered. Wed Apr 1 16:21:08 1992 Ian Lance Taylor (ian@airs.com) * sys3.unx (esysdep_open_receive): David J. MacKenzie: some USG_STATFS systems use 512 as the block size of f_bfree, despite the existence of f_bsize. * port.c (fport_open): initialize stdin port. * policy.h, log.c: added CLOSE_LOGFILES configuration parameter. * sys2.unx: T. William Wells: handle a system without or . * configure.in: Franc,ois Pinard: warn if none of napms, nap, usleep, poll or select are available, since \p will sleep for a full second. * Makefile.in: Gerben Wierda: fixed uninstall to set file owner and mode correctly. Also changed install to handle uucp.info-4 and uustat.1. * MANIFEST: added uucp.info-4 and uustat.1. * uustat.1: Wrote. * uucico.8, uuxqt.8, uucp.1, uux.1: updated -x switch, cleaned up a bit. Tue Mar 31 14:40:06 1992 Ian Lance Taylor (ian@airs.com) * sys1.unx (usysdep_initialize): use $PWD to get the current working directory if it's defined and correct. * sys1.unx (usysdep_initialize): Brian Antoine: use name from getpwname rather than getlogin. * uucp.texi: David J. MacKenzie: put in a number of corrections. Also split sys file and config file nodes, and rearranged several nodes. * protg.c (fgsenddata): Niels Baggesen: packet to retransmit did not get reset correctly. Mon Mar 30 10:03:28 1992 Ian Lance Taylor (ian@airs.com) * tcp.c (ftcp_reset): Petri Helenius: TCP server never started uuxqt, because it exited in ftcp_reset. * policy.h, sysh.unx, sys2.unx (fsserial_lockfile): added HAVE_SVR4_LOCKFILES configuration parameter. * sys3.unx (esysdep_open_receive): Niels Baggesen: USG statfs has an f_bsize field. Sun Mar 29 23:04:20 1992 Ian Lance Taylor (ian@airs.com) * uucp.h, sysinf.c, prot.c, prote.c, protf.c, protg.c, prott.c: Niels Baggesen: added new debugging types abnormal and uucp-proto. * uucico.c (fuucp), prot.c (freceive_file), file.c (fstore_recfile): Dirk Musstopf: if a file receive fails before it starts, perhaps because the file was too large, remember to remove the temporary file. * sys2.unx (fsserial_lock, fsserial_open, fsserial_write, fsserial_io): always block and unblock the read and write descriptors together. Sat Mar 28 14:40:50 1992 Ian Lance Taylor (ian@airs.com) * uustat.c: allow multiple systems and users to be specified at once; likewise for kills and rejuvenates. Allow old and young to be combined with systems and users. As suggested by Niels Baggesen, make machine status output more columnar. * uucp.h, uucico.c, config.c, sys3.unx: Michael I Bushnell: renamed enum tstatus to tstatus_type to avoid conflict with on some systems. * config.c, sysinf.c, prtinf.c, chat.c: David J. MacKenzie: allow backslash newline quoting in all TAYLOR_CONFIG configuration files. * chat.c (fchat): David J. MacKenzie: handle empty subexpect strings correctly. * uucico.c (main, fcall): Petri Helenius: must dump controlling terminal before going to next alternate. Also fixed David J. MacKenzie bug in which a signal did not prevent the next alternates from being tried. Also made sure qtime was always freed up. * uucp.h, uucico.c (fdo_call), sysinf.c (tialternate), uuchk.c (ukshow): Franc,ois Pinard: allow a name to be given to an alternate, and display the name when placing a call. * chat.c (fcprogram), port.c (fport_open, fport_close): David J. MacKenzie: send port device rather than port name to a chat program using \Y; make sure port device is reset if port open fails and when port is closed. * uucico.c (fuucp), log.c (ulog, ustats, ustats_close): close log and statistics file every time master and slave switch roles. Fri Mar 27 00:31:23 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (fdo_call): Mark Mallett: minor cleanup. * uuname.c (main): Franc,ois Pinard: output aliases, added -a switch. * uucico.8, uuxqt.8, uux.1, uucp.1: David J. MacKenzie: changed .TP5 to .TP 5; also updated to 1.03. * tstuu.c: Roberto Biancardi: if SIGCHLD is not defined, define it as SIGCLD. * config.c: David J. MacKenzie: cMaxuuxqts is independent of HAVE_TAYLOR_CONFIG. * uucp.h: Gerben Wierda: don't always declare bzero. * sys7.unx (ussettime, fsysdep_lock_status): Niels Baggesen, Gerben Wierda: minor patches. * sys2.unx: Gerben Wierda: minor cleanups. * uucp.h: Niels Baggesen: simplified debugging message macros to avoid broken compilers. * sys2.unx: don't use TIOCEXCL locking. * sys2.unx: rework HAVE_UNBLOCKED_WRITES == 0 to work even if writes are unblocked. Correct initialization of fwrite_blocking. * Makefile.in, configure.in: David J. MacKenzie: various cleanups. Changed default newconfigdir definition. Supported compilation in a different directory. Used symbolic links if available. Changed default infodir definition per Franc,ois Pinard. * policy.h: David J. MacKenzie: various cleanups. Thu Mar 26 12:17:41 1992 Ian Lance Taylor (ian@airs.com) * sys3.unx: reduced race condition in fsdo_lock. * sys1.unx: Gerben Wierda: various cleanups. Also don't set sa_flags to SV_INTERRUPT per Chip Salzenberg. Wed Mar 25 22:20:24 1992 Ian Lance Taylor (ian@airs.com) * configure.in: Overhauled for readability and functionality as suggested by T. William Wells and others. Added bug checks, including for SCO memmove and ftime. Tue Mar 24 12:18:56 1992 Ian Lance Taylor (ian@airs.com) * sysinf.c (uiread_systems): fixed handling of alternates in file-wide defaults. Wed Mar 18 01:01:25 1992 Ian Lance Taylor (ian@airs.com) * config.c (tprocess_one_cmd): handle CMDTABTYPE_FULLSTRING correctly if there are no arguments. * Released beta version 1.03 * sys1.unx (usysdep_detach): open the controlling terminal in non delay mode since it might be a modem. Tue Mar 17 00:01:53 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (fdo_call, faccept_call): T. William Wells: set current time in status file when call completes. * sys1.unx (iswait), sys2.unx (fsserial_read, fsserial_write, fsserial_io): log signals when they occur, even if we continue some sort of loop, rather than waiting for the next ulog call. * sys2.unx (fsserial_lock, fsserial_open): don't block when opening the write descriptor. Mon Mar 16 00:14:43 1992 Ian Lance Taylor (ian@airs.com) * system.h, uuxqt.c (uqdo_xqt_file), sys5.unx (fsysdep_execute): pass command to fsysdep_execute as first element of argument array. * tcp.c: declare _exit. * uucp.h: move definition of const before use for non ANSI C. * uucp.h, sys1.unx: undefine remove in uucp.h if the system does not have it to avoid conflict with macro definitions. * uucico.c, uuxqt.c, protf.c, prott.c, prote.c, config.c, chat.c, port.c, sys2.unx: miscellaneous cleanups. * tcp.c (ftcp_open): cast argument to bzero. * time.c (qtimegrade_parse): cast argument to qttime_parse to long. * file.c: changed iRecmode to unsigned int. * configure.in, uucp.h: on SCO 3.2.2 sig_atomic_t is defined in but not . * sys1.unx: undefined remove before the function definition to avoid trouble on systems for which it is a macro. * Makefile.in: removed dependencies of getopt.o. * sys1.unx, sys7.unx, tstuu.c: adjusted external declarations. * getopt.h, getopt.c: get new versions from glibc 1.01. * sys1.unx: don't declare sigemptyset. * version.c: updated to beta 1.03. * chat.c (fcsend): Scott Ballantyne: go ahead and send a character for an illegal escape sequence rather than failing out. * uuxqt.c (uqdo_xqt_file), sys5.unx (zsysdep_find_command): cast result of alloca. * protg.c (fgprocess_data): Niels Baggesen: improved debugging information. Also tweaked fgprocess_data code to use memchr to find the next DLE rather than searching for it by hand. * uucico.c (faccept_call, fuucp): accept SVR4 -U flag giving maximum file transfer size; accept and ignore SVR4 -R flag meaning that the system supports file restart. Sun Mar 15 00:21:56 1992 Ian Lance Taylor (ian@airs.com) * sysinf.c (titime, titimegrade): permit a retry time to be specified as an optional additional argument. * uucico.c (zget_uucp_cmd, zget_typed_line): turn off DEBUG_PORT when doing DEBUG_HANDSHAKE. * policy.h, sysh.unx, sys1.unx, sys2.unx (fsblock_writes, fsserial_write, fsserial_io): added configuration parameters HAVE_UNBLOCKED_WRITES and SINGLE_WRITE. Also blocked signals while clearing afSignal in fsysdep_modem_close. * chat.c (icexpect, fcsend): turn off DEBUG_PORT while doing chat script debugging. * sysh.unx, sys2.unx (fsserial_lock, fsserial_open, fsserial_write, fsserial_io, fsysdep_tcp_read, fsysdep_tcp_write, fsysdep_tcp_io): T. William Wells: some systems don't support unblocked writes, so don't use them. * port.c (fport_read, fport_write): show calls to fport_read and fport_write under DEBUG_PORT. * bnu.c (fbnu_find_port): Scott Ballantyne: accept "Any" as a Device speed. Sat Mar 14 20:52:11 1992 Ian Lance Taylor (ian@airs.com) * uucp.h, system.h, sysh.unx, uucico.c (main, zget_typed_line), uuxqt.c (main), uucp.c (main), uux.c (main, uxcopy_stdin), tcp.c (ftcp_open), log.c (ulog, ulog_close), sys1.unx (ussignal), sys2.unx (fsserial_close, fsysdep_modem_end_dial, fsserial_read, fsserial_write, fsserial_io, fsysdep_tcp_read, fsysdep_tcp_write, fsysdep_tcp_io): T. William Wells and Chip Salzenberg: keep an array of signals so that a new signal doesn't obliterate our knowledge of an old signal. Johan Vromans: if we get SIGINT continue the current session but don't start any new ones. * sysh.unx, sys1.unx (isspawn, espopen, iswait, fsysdep_mail, fsysdep_run, getcwd, mkdir), sys2.unx (fsrun_chat), sys3.unx (fsysdep_wildcard_start), sys5.unx (fsysdep_execute), sys7.unx (fsysdep_lock_status), uuxqt.c (uqdo_xqt_file), tcp.c (ftcp_open), tstuu.c (uchild): added function isspawn, espopen and iswait and channeled all execs of new processes and waits through them. Fri Mar 13 18:00:04 1992 Ian Lance Taylor (ian@airs.com) * sysinf.c (uset_system_defaults): Chip Salzenberg: changed default login script timeout to 10 seconds. * prot.h, prot.c (freceive_data, breceive_char), protg.c, protf.c, prott.c, prote.c: changed breceive_char to go through freceive_data rather than calling fport_read directly. Added an freport argument to freceive_data, and change all old calls to pass it in as FALSE. Thu Mar 12 14:49:59 1992 Ian Lance Taylor (ian@airs.com) * uucp.h: added a padding byte to scmd structure, since at least one compiler needs it. * uucp.c (main): use fake local name (from ``myname'' command) when generating an execution request intended for the local system. * sysh.unx: corrected readdir prototype. * sys2.unx: moved local header files ahead of sleep routine determination. * General overhaul to change debugging system. Debugging is now done by type rather than by number. iDebug is now interpreted as a bit sequence. DEBUG may only be 0 (no checks or debugging), 1 (checks, no debugging) or 2 (checks and debugging). The debugging names are parsed by idebug_parse and tidebug_parse in config.c. The debugging types are additive. Many source files changed. Inspired by Michael Richardson, Johan Vromans and Peter da Silva. Wed Mar 11 12:01:03 1992 Ian Lance Taylor (ian@airs.com) * policy.h, uuxqt.c (uqdo_xqt_file): Chip Salzenberg: support Internet mail addresses in uuxqt replies (added configuration parameter HAVE_INTERNET_MAIL to control this). * sys7.unx (fskill_or_rejuv): permit uucp user to delete any job. * uucp.h, system.h, sysh.unx, config.c, uuxqt.c (main, uqabort), sys5.unx (isysdep_lock_uuxqt, fsysdep_unlock_uuxqt), bnu.c (ubnu_read_sysfiles): Marty Shannon: added max-uuxqts command, along with support for BNU Maxuuxqts, to limit number of concurrent uuxqt processes. * chat.c (icexpect, fcsend), uucico.c (zget_uucp_cmd, zget_typed_line): improved debugging by avoiding incredibly long lines. * system.h, sys5.unx (fsysdep_execute), uuxqt.c (uqdo_xqt_file): Jon Zeef: if a temporary failure occurs, retry the execution later. Tue Mar 10 12:40:30 1992 Ian Lance Taylor (ian@airs.com) * sysh.unx, sys1.unx (isfork), sys2.unx, sys5.unx, tcp.c: Franc,ois Pinard: retry fork several times before giving up. * uucp.h, prot.c (fploop, fgot_data), file.c (usendfile_error, urecfile_error, frecfile_rewind): Niels Baggesen: if we can't read or write a file, treat it as a temporary error rather than a permanent error; if we get an error on write, drop the connection rather they try to continue. * uucp.h, system.h, sysh.unx, uucico.c (fuucp), prot.c (fsend_file, freceive_file), file.c (fsent_file, usendfile_error, freceived_file, urecfile_error, fmail_transfer), sys1.unx (usmake_spool_dir), sys4.unx (zsysdep_save_temp_file): if a file send fails, save the file away rather than lose it forever. * uucico.c (main): don't run uuxqt if we got a SIGTERM. * tcp.c (ftcp_open): Petri Helenius: have server fork twice to avoid zombies. * port.h, prtinf.c, v2.c, bnu.c (fbnu_find_port), uucico.c (fdo_call, faccept_call), uuchk.c (fkshow_port): added protocol command for ports, mostly to support BNU. Also modified uuchk to make the absence of any matching port or dialer more obvious. * sys3.unx (esysdep_open_receive): check size of destination file system as well as temporary file system; handle f_bsize field under FS_MNTENT. * configure.in, sysh.unx: test for including and in the same file, setting new configuration parameter HAVE_TERMIOS_AND_SYS_IOCTL_H accordingly; handle it in sysh.unx. Mon Mar 9 00:06:12 1992 Ian Lance Taylor (ian@airs.com) * sys2.unx (fsserial_close): Franc,ois Pinard: sleep for a second after closing the serial port to give it a chance to settle. * sysh.unx (fsetterminfodrain), sys2.unx (fsserial_close, fsserial_reset, fsserial_set): wait for terminal output to drain before closing it, resetting it, or changing its parameters. * uucico.c (zget_uucp_cmd): Ted Lindgreen: strip parity bit from initial handshake strings. * system.h, sys3.unx (esysdep_open_send), uucico.c (fuucp): Ted Lindgreen: don't send a mail message if a file to send has been removed, since it might have been sent in a previous session. * uuchk.c (ukshow): Zacharias Beckman: put list of permitted programs and execution path on separate lines. * uucico.c (fdo_call, faccept_call): only look for hangup string in debugging mode, since there's nothing to be done with it anyhow. * uucico.c (faccept_call): Ted Lindgreen: report the minimum transfer grade requested during an incoming call in the log file. * uucp.h, uutime.h, config.c, uucico.c (fcall), time.c (ftimespan_match, btimegrade, cmax_size_now): added a new status type for ``wrong time to call''. If a system can never be called, this status type is not used (if an attempt is made to call the system, the status is left unchanged). Sun Mar 8 11:41:45 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (main, flogin_prompt, faccept_call): Ted Lindgreen: if we were asked to call a single system, or if a single system called in, then start uuxqt with -s for just that system. * uucico.c (main): Ted Lindgreen: ignore the -u option. * tstuu.c: Ted Lindgreen: don't include if it's not there. Also removed the ``ignore this error'' message from the chat scripts since it's no longer marked as an error. * sys2.unx (fsserial_set): Ted Lindgreen: if CRTSCTS is defined and turned on, then don't turn on IXOFF. * uucp.h, log.c, uucico.c (fdo_call, faccept_call): Ted Lindgreen: report the port name and (for incoming calls) the login name in the log file. Sat Mar 7 10:00:47 1992 Ian Lance Taylor (ian@airs.com) * port.h, prtinf.c, sys2.unx (fsserial_lockfile, fsserial_lock): Peter da Silva: added ``lockname'' command to ports to permit specification of the file name to use when locking. * sys1.unx (usysdep_detach): let setpgrp fail silently. * sys2.unx: always include if it's present on the system. * time.c (btimegrade, cmax_size_now): removed extraneous semicolons. * sys2.unx (fsserial_lock, fsserial_open, fsserial_close): support TIOCEXCL locking. * sys2.unx (fsserial_open): preserve unknown bits in c_cflag when using HAVE_SYSV_TERMIO or HAVE_POSIX_TERMIOS. * prot.h: never included more than once. Fri Mar 6 21:53:28 1992 Ian Lance Taylor (ian@airs.com) * uucp.h: Eric Ziegast: some systems don't define EXIT_SUCCESS or EXIT_FAILURE in stdlib.h. * uucp.h, uutime.h, uucico.c (fuucp), sysinf.c (uinittimetables, uaddtimetable), uuchk.c (main, ukshow_size, ukshow_time, qcompress_span), time.c (all new): rewrote time routines completely for consistency and simplicity. Fixed bug causing incorrect maximum possible transfer size. Added new file uutime.h. Wed Mar 4 10:06:13 1992 Ian Lance Taylor (ian@airs.com) * sys2.unx (fsserial_lockfile, fsserial_lock, fsysdep_modem_open, fsysdep_direct_open, fsysdep_modem_close, fsysdep_direct_close): Petri Helenius: if the open failed on a serial port, the lock files were not removed. * config.c (igradecmp): the local variables in igradecmp have to be integers; signed characters might not work correctly (although they would in all normal cases). * sys4.unx (fsysdep_has_work): Johan Vromans: set *pbgrade correctly if we still have work left over that we haven't looked at yet. * tstuu.c (main, uchoose, fwriteable): Roberto Biancardi: use poll if we haven't got select. * uucico.c (zget_uucp_cmd): Michael Haberler: some systems send \n after Shere, rather than a null byte. Tue Mar 3 01:03:22 1992 Ian Lance Taylor (ian@airs.com) * uuxqt.c (main, uqdo_xqt_file): permit local executions, don't get grade out of system dependent file name. * sys4.unx (fsysdep_get_work): Bob Denny: warn if we can't open a command file. * v2.c (uv2_read_systems): Jeff Putsch: infinite loop when reading time string. * uucp.h, sys1.unx, sys2.unx, sys3.unx, tstuu.c, configure.in: Thomas Fischer: some NeXT compatibility stuff: removed externs of sleep and alarm, included in uucp.h. * uucp.h, port.h, uucico.c (zget_uucp_cmd, zget_typed_line), port.c (cdebug_char, udebug_buffer), chat.c (icexpect, fcsend, fcphone), log.c (ulog): Michael Richardson: added LOG_DEBUG_START, LOG_DEBUG_CONTINUE and LOG_DEBUG_END to allow a debugging line in the log file to be built character by character. Used this new feature in chat script debugging, rather than having each character appear on a separate line. Added fPort_debug variable to control port debugging. * uustat.c (fsquery, fsquery_system, fsquery_show): handle execution files queued up for the local system correctly when using -q option (they still don't show up on any other option). * uucp.texi, protg.c (fgstart, fgshutdown): Aleksey P. Rudnev: added remote-window and remote-packet-size 'g' protocol parameters. Reset the parameters to their default values in fgshutdown. * sysh.unx, sys2.unx (fsserial_read, usalarm), sys1.unx (usset_signal, usysdep_detach), uucico.c (main): overhauled fsserial_read yet again. The timeout passed in is now an absolute bound. A special SIGALRM handler does some wierd stuff to avoid any possible race. * config.c (uread_config), sysinf.c (uiread_systems, fcallout_login, fcheck_login), prtinf.c (ffind_port, fread_dialer_info), chat.c (fctranslate), uucico.c (faccept_call): T. William Wells: when using HAVE_TAYLOR_CONFIG in combination with V2 or BNU configuration files, don't complain if the HAVE_TAYLOR_CONFIG files are missing. Mon Mar 2 10:21:36 1992 Ian Lance Taylor (ian@airs.com) * sys2.unx (fsserial_read): T. William Wells: don't arbitrarily extend read timeout. * uux.c (main): check iSignal before entering fread, since the user may have hit ^C earlier in the program. Sun Mar 1 23:39:33 1992 Ian Lance Taylor (ian@airs.com) * policy.h, uucp.h, sysh.unx, sys2.unx (fsserial_lock, fsysdep_modem_close, fsysdep_direct_close), util.c (strlwr), configure.in: Marc Unangst: added HAVE_SCO_LOCKFILES configuration parameter to force lock file names to lower case. Fri Feb 28 00:07:12 1992 Ian Lance Taylor (ian@airs.com) * system.h, uucico.c (faccept_call, fdo_xcmd), uuxqt.c (uqdo_xqt_file), uux.c (main), uucp.c (main, ucspool_cmds), sys3.unx (zsysdep_spool_cmds), uux.1, uucp.1: added -j switch to uucp and uux to display the jobid of the spooled job. * uucp.h, system.h, sysh.unx, uucico.c (fuucp, fdo_xcmd, fok_to_send, fok_to_receive), uuxqt.c (uqdo_xqt_files), uux.c (main, uxcopy_stdin), uucp.c (main), file.c (freceived_file), config.c (fin_directory_list), sys1.unx (fsysdep_file_exists, fsuser_access, fsysdep_in_directory), sys3.unx (esysdep_open_send, fsysdep_move_file), sys5.unx (fsysdep_xqt_check_file): Chip Salzenberg: recheck file access permissions before sending, to try to avoid symbolic link games. Check that the user has search access on all directories down to the file, and read or write access to the file or directory itself. Check in uucp and uux as well as uucico, to provide early messages. Check the standard input file in uuxqt. Be more careful about creating files in the spool directory. This eliminates all security problems I know of, except for a very short race in fsysdep_move_file. * sys3.unx (esysdep_open_send): give an error message if we try to send a directory (used to just fail silently). * system.h, sysh.unx, sys1.unx (usysdep_detach, ussignal, fsysdep_catch, usysdep_start_catch), sys2.unx (fsysdep_modem_end_dial, fsserial_read), uux.c (main): T. William Wells: fsysdep_catch obviously must be a macro, since it calls setjmp. Also TIOCNOTTY sets the process group to 0, so we don't have to fork before calling it. Thu Feb 27 00:08:09 1992 Ian Lance Taylor (ian@airs.com) * sys1.unx, sys6.unx, sys7.unx: added some extern definitions. * configure.in, uucp.h, system.h, sysh.unx, uucico.c (main, ucatch, uabort, zget_typed_line, zget_uucp_cmd), uuxqt.c (main, uqcatch, uqabort), uux.c (main, uxcatch, uxrecord_file, uxabort), uucp.c (main, uccatch, ucrecord_file, ucabort), uustat.c (main, uscatch), uulog.c (main, ulcatch), uuname.c (main, uncatch), uucheck.c (main, ukcatch), log.c (ulog_fatal_fn, ulog, ulog_close), tstuu.c (main, uchild, uprepare_test), sys1.unx (usysdep_initialize, usysdep_detach, usysdep_signal, fsysdep_catch, usysdep_end_catch, ussignal, fsset_signal, fsysdep_run, raise), sys2.unx (usalarm, usysdep_pause, fsserial_lock, fsserial_open, fsysdep_stdin_close, fsysdep_modem_close, fsysdep_modem_end_dial, fsserial_read, fsserial_write, fsserial_io), sys5.unx (fsysdep_execute): T. William Wells: overhaul to support detaching from the terminal and completely reliable signals. uucico now calls usysdep_detach at various points; new option -D prevents detaching. The signal handling code all goes through usysdep_signal, fsysdep_catch and usysdep_end_catch. A signal now just sets iSignal, which is checked at various points, notably in the port routines and in the main loops in uucico and uuxqt. Tue Feb 25 10:59:23 1992 Ian Lance Taylor (ian@airs.com) * protg.c (fgwait_for_packet): Bob Denny: reset the count of timeouts only when data is recognized, so that we aren't fooled by a sequence of imperfect echoes. * sys5.unx (zsysdep_get_xqt): Bob Denny: don't warn if opendir gets ENOENT. I think POSIX requires ENOTDIR, but what can you do? Mon Feb 24 14:37:10 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (main, uusage): don't treat an extra argument as a port name. * sys3.unx (esysdep_truncate): Roberto Biancardi: support F_CHSIZE and F_FREESP in esysdep_truncate. * configure.in, sys2.unx (fsserial_read, usysdep_pause): Roberto Biancardi: use poll to sleep less than a second if we haven't got anything else. * v2.c (uv2_read_systems, fv2_find_port), bnu.c (ubnu_read_systems, fbnu_find_port, fbnu_read_dialer_info), uustat.c (fsworkfile_show): Roberto Biancardi: skip spaces and tabs after doing a strtok ((char *) NULL, ""). * copy.c (fcopy_file), sys1.unx (esysdep_fopen), sys2.unx, sys3.unx (esysdep_open_receive, esysdep_truncate, fsdo_lock, fscmd_seq), sys5.unx (fsysdep_execute), sys7.unx, tstuu.c: John Theus: some systems use instead of . Also changed the code to call creat instead of open when appropriate. Should now work on V7, with the exception of O_NONBLOCK and O_NDELAY in sys2.unx and tstuu.c. * uucp.h: John Theus: if we don't have vprintf, ulog is defined without an ellipsis, so don't declare it with one. Sun Feb 23 14:45:53 1992 Ian Lance Taylor (ian@airs.com) * uucp.h, system.h, bnu.c (ubnu_read_systems), config.c (fin_directory_list), sys1.unx (fsysdep_in_directory), sys5.unx (fsysdep_xqt_check_file), uucp.c (main), uuxqt.c (uqdo_xqt_file), uucico.c (fdo_xcmd, fok_to_send, fok_to_receive), tstuu.c (uprepare_test): only permit files to be received into directories that are world writeable. Check for this in fsysdep_in_directory, with a new argument. Changed calls appropriately. In tstuu create /usr/tmp/tstuu as world writeable. * bnu.c (ubadd_perm_alternate): Doug Evans: after all that, I got it wrong: WRITE only applies to remote requests. * uucp.h, uucico.c (fuucp, fdo_xcmd, fok_to_send, fok_to_receive), uuxqt.c (uqdo_xqt_file), uuchk.c (ukshow), sysinf.c (uset_system_defaults, tialternate), sys5.unx (fsysdep_xqt_check_file), bnu.c (ubadd_perm_alternate): fixed READ and WRITE handling to match BNU semantics. Added zcalled_local_send, zcalled_local_receive, zcalled_remote_send and zcalled_remote_receive fields to ssysteminfo structure for this, and handled them in all the appropriate places. Sat Feb 22 22:30:59 1992 Ian Lance Taylor (ian@airs.com) * Complete overhaul of configuration to use automatic shell script. Eliminated conf.h, now generated by configure. Renamed Makefile to Makefile.in. Added policy.h for administrative decisions and other choices which can not be made automatically. Many changes to many source files, none having to do with code. Thu Feb 20 17:57:55 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (fdo_call): Chip Salzenberg: some systems truncate the Shere= machine name to 7 characters. Wed Feb 19 14:36:31 1992 Ian Lance Taylor (ian@airs.com) * sys7.unx (fskill_or_rejuv): make sure that only the submitter or the superuser is permitted to cancel (or rejuvenate) a request. * system.h, sysh.unx, sys3.unx (zsfile_to_jobid, zsjobid_to_file), sys4.unx (zsysdep_jobid), sys7.unx, uustat.c, Makefile, MANIFEST: wrote uustat. Changed CSEQLEN in sys3.unx from 5 to 4. Added several new system dependent functions, mostly in sys7.unx. * system.h, sys1.unx, log.c, file.c, chat.c, protg.c, uucico.c: rearranged the time functions for the convenience of uustat. Made isysdep_time take an optional pimicros arguments. Renamed usysdep_full_time to isysdep_process_time, and made clear that it need only work within a single process. Changed usysdep_localtime to take a time returned by isysdep_time rather than always use the current time. Changed the calls as appropriate. Tue Feb 18 14:03:19 1992 Ian Lance Taylor (ian@airs.com) * uuxqt.c (main): pass fdaemon argument correctly to usysdep_initialize. Mon Feb 17 17:09:16 1992 Ian Lance Taylor (ian@airs.com) * uuxqt.c (uqdo_xqt_file): T. William Wells: make sure sh uses absolute path of command, rather than relying on PATH. * sys5.unx (zsysdep_find_command): Michael Nolan: allow full command name to be specified by remote system, not just basename, if command is not in path. * log.c (ulog): don't use headers when outputting to terminal. * sys2.unx (fsrun_chat): Bob Denny: log chat program messages as LOG_NORMAL, not LOG_ERROR. Fri Feb 14 00:17:57 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (ucatch), uuxqt.c (uqcatch): Neils Baggesen: under HAVE_BNU_LOGGING, don't lose the system name when dieing. * uulog.c, Makefile, MANIFEST: wrote uulog. * uuname.c, Makefile, MANIFEST: wrote uuname. * bnu.c (ubadd_perm_alternate): T. William Wells: must xstrdup the system name before calling uadd_validate. * log.c (ulog_close): Micheal Nolan: don't refer to eLdebug if the DEBUG configuration parameter is 0. * uucp.c (main): Niels Baggesen: abtname must be copied into memory, or it will be overwritten by the next file to be copied. Sun Feb 9 00:12:58 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (fuucp), prot.c (fsend_file, freceive_file): Bob Denny: call fmail_transfer before calling fsysdep_did_work, because the latter frees up strings used by the former. * sysh.unx, sys1.unx (mkdir), uudir.c (new file), Makefile: added HAVE_MKDIR configuration parameter for systems without the mkdir system call. The emulation function in sys1.unx invokes the new suid program uudir which sets its uid to uucp and invokes /bin/mkdir. Added rules to create uudir to Makefile. Sat Feb 8 14:25:50 1992 Ian Lance Taylor (ian@airs.com) * sysh.unx, sys1.unx (opendir, readdir, closedir), sys4.unx, sys5.unx: added HAVE_OLD_DIRECTORIES configuration parameter to support systems without opendir/readdir/closedir which use original Unix directory format. * sysh.unx, sys1.unx (dup2): added HAVE_DUP2 configuration parameter and dup2 emulation function. * sys1.unx (zsysdep_local_name): put utsname structure on stack rather than making it static. * sysh.unx, sys1.unx (usysdep_initialize, getcwd): if we have neither getcwd nor getwd, fork /bin/pwd to get the current working directory. * system.h, uucico.c (main), uuxqt.c (main), uux.c (main), uucp.c (main), uuchk.c (main), sys1.unx (usysdep_initialize), sys6.unx (fsysdep_needs_cwd, zsysdep_add_cwd): because getting the current working directory can be expensive on Unix, since some implementation of getcwd fork a shell to execute pwd, only try to get the cwd if it is going to be needed by uux or uucp. * uucico.c (main), uuxqt.c (main), uux.c (main), uucp.c (main), uuchk.c (main), log.c (ulog): handle all possible signals raised by abort, namely SIGABRT, SIGILL and SIGIOT. In ulog always call abort rather than raise (SIGABRT). * sys4.unx (usysdep_get_work_free): the qSwork_file information was freed up incorrectly if a file transfer failed. * sysh.unx, sys2.unx: Archaic Zilog System III computers use setret and longret rather than setjmp and longjmp, so I added a HAVE_SETRET configuration option. * prott.c (ftstart, ftsenddata): shifts of integers by more than 15 are not portable. * prot.c (fsend_file, freceive_file, fploop, fgot_data): I ran into an old compiler which couldn't handle the calls to pffile, so I simplified them to use a variable. * port.c (fmodem_dial): cast result of alloca. * getopt.h, getopt.c: Jay Vassos-Libove: renamed getopt and related variables by macro defining them to gnu_*. This avoids conflicts with system header files and system libraries. Fri Feb 7 12:08:42 1992 Ian Lance Taylor (ian@airs.com) * everything: added HAVE_STRING_H and HAVE_STRINGS_H. Removed include of in every source file and put it in uucp.h. Since I had to change everything anyhow, added 1992 to the copyright date. * uucico.c (fcall): Bob Denny: retry time not reached is not really an error, so just make a normal log entry for it. Sun Feb 2 01:38:47 1992 Ian Lance Taylor (ian@airs.com) * uucp.c (main): Get the file name for the destination of a local copy using zsysdep_real_file_name rather than zsysdep_in_dir, since the latter doesn't get the basename of the argument. * sys3.unx (fsysdep_get_status): Niels Baggesen: cast enum to int before comparison. * system.h, uucp.c (main), uux.c (main), sys6.unx (fsysdep_access, fsysdep_daemon_access): Niels Baggesen: check user access to file since programs are running setuid. Previously uucp and uux permitted a file readable only by uucp to be transferred to another system by user request! * chat.c (fchat): Michael Nolan: portions of a chat string might be separated by more than just a single space if they are read from a V2 or BNU configuration file. Fri Jan 31 19:51:57 1992 Ian Lance Taylor (ian@airs.com) * protg.c: Chip Salzenberg: change default window size to 7. * sys3.unx (fsdo_unlock): Michael Nolan: cast result of alloca to (char *), not that it really matters. * log.c (ulog): Michael Nolan: if SIGABRT is not defined, just call abort. Thu Jan 30 18:19:33 1992 Ian Lance Taylor (ian@airs.com) * bnu.c (ubadd_perm): Michael Nolan: debugging check was done wrong for entry with LOGNAME but no MACHINE. Wed Jan 29 13:28:59 1992 Ian Lance Taylor (ian@airs.com) * uucico.c (zget_uucp_cmd): Patrick Smith: only wait a short time for the hangup string. * sys4.unx (iswork_cmp): Patrick Smith: fixed casts to not cast away const. Tue Jan 28 11:06:34 1992 Ian Lance Taylor (ian@airs.com) * sys1.unx, sys3.unx, tstuu.c: Jay Vassos-Libove: removed some declarations of system functions that conflict with system header files on BSD/386 alpha. * Makefile: Jeff Ross: make sure the uninstall target restores the original file owner and mode. * protg.c (fgsendcmd): the previous patch wasn't really correct. Mon Jan 27 22:30:47 1992 Ian Lance Taylor (ian@airs.com) * log.c (ustats): Marty Shannon: don't report a failed transfer under USE_BNU_LOGGING. * sys3.unx (zsysdep_real_file_name): Marty Shannon: a trailing '/' on the name means that it is a directory, even if the directory does not already exist. * uucico.c (fuucp): Marty Shannon: the -f flag indicating that directories should not be created was not being handled correctly. * uucico.c (fcall): Chip Salzenberg: set .Status correctly if wrong time to call. * protg.c (fgsendcmd): John Antypas: didn't handle null byte at end of command which was exactly a power of two in length correctly. Tue Jan 21 14:37:10 1992 Ian Lance Taylor (ian@airs.com) * Released version 1.02. * system.h, uucico.c (main), uux.c (main), uucp.c (main, zcone_system), sys1.unx (fsysdep_run): Chip Salzenberg: have uucp and uux start up uucico -s system rather than uucico -r1. Mon Jan 20 11:45:38 1992 Ian Lance Taylor (ian@airs.com) * sys1.unx (fsysdep_make_dirs): don't try to create a directory with no name. * version.c: change version number to 1.02. * uucico.8, uuxqt.8, uucp.1, uux.1: change version number to 1.02. * MANIFEST: removed texinfo.tex; it's twice the size of any other file, so I think it's just to large to distribute. * uucico.c (fcall, fdo_call): Marty Shannon: update the time in the .Status file if it's the wrong time to call, and upon receiving a call. Sun Jan 19 13:29:23 1992 Ian Lance Taylor (ian@airs.com) * protg.c (fgsendcmd, fgsenddata): Dave Platt: if the remote UUCP accepts packets larger than 64 bytes, assume it can handle differing packet sizes, so if we have a small amount of data to send, use a small packet. Besides the two routines mentioned, also made minor changes to other routines to get the packet length out of the packet data rather than always assuming the same packet size. * conf.h, uucp.h: Matthew Lyle: some systems don't declare errno in , so I added HAVE_ERRNO_DECLARATION. * conf.h, uucp.h, util.c (bsearch): added HAVE_BSEARCH configuration parameter. Sat Jan 18 17:45:28 1992 Ian Lance Taylor (ian@airs.com) * tstuu.c (utransfer): Mike Park: don't sleep when the input buffer is full; it's too slow. * Makefile: when making a distribution,change the mode of separate copies of the configuration files Makefile, conf.h and sysh.unx. * uucico.c (faccept_call): Marty Shannon: update .Status file on incoming calls. * uucp.h, prot.h, uucico.c (fuucp), prot.c (fsend_file, fpsendfile_confirm, freceive_file, fprecfile_confirm, fxcmd, ustats_failed), file.c (fsent_file, usendfile_error, freceived_file, urecfile_error, frecfile_rewind, fmail_transfer): reworked calls to fsydep_did_work and sending of mail messages to be more sensible. Now sends mail to requestor if request fails permanently and does not remove file if request fails only temporarily. Thu Jan 16 11:33:08 1992 Ian Lance Taylor (ian@airs.com) * protg.c (fgsendcmd, fgsenddata): zero out unused bytes in short packets. * prot.c (zgetcmd), protf.c (ffsendcmd), prott.c (ftsendcmd), prote.c (fesendcmd): Niels Baggesen: added some debugging messages. * protg.c (fgsendcmd): corrected misspelling in debugging message. * log.c (ustats): Niels Baggesen: add FAILED to end of xferstats line if appropriate. * uuxqt.c (uqdo_xqt_file): Niels Baggesen: was checking strcmp return value incorrectly, so messages to other systems were not being sent. * sys2.unx, tstuu.c: Mike Park: ioctl is sometimes declared varadic, so we can't declare it. Wed Jan 15 02:03:43 1992 Ian Lance Taylor (ian@airs.com) * sys1.unx: put \n at end of fsysdep_run error message. * sysh.unx, sys1.unx, sys2.unx, tstuu.c: Mike Park: on some systems includes but can only be included once; added HAVE_SYS_TIME_AND_TIME_H to sysh.unx. * tstuu.c: Mike Park: removed prototype for times since some systems don't have clock_t. * conf.h, sys2.unx, util.c: Mike Park: some systems don't have . Every macro used from it already had a check to make sure it was defined anyhow. * tstuu.c (uprepare_test): Mike Park: sh on NeXT interprets a leading ~, so we have to quote the argument to system(3). Incredible. * conf.h, uucp.h: Mike Park: if HAVE_ALLOCA is 0 when compiling with gcc, don't define alloca as a macro. This will let the NeXT define it in some header file. * sysh.unx, sys2.unx, sys5.unx, tstuu.c: Mike Park: handle HAVE_UNION_WAIT completely. Assume that system(3) returns an int which should be put into the w_status field. Define macros for union wait if they are not already defined. Move all this stuff into sysh.unx rather than duplicating it in three different files. * conf.h, uucp.h, sysh.unx, config.c, bnu.c, v2.c, uucico.c, uuxqt.c, uux.c, uucp.c, uuchk.c, Makefile: set directory to look for configuration files in in Makefile rather than in sysh.unx. This forced a number of changes, as now all new style configuration files are looked up using NEWCONFIGLIB. Old style configuration files are looked up using OLDCONFIGLIB. Mon Jan 13 00:35:43 1992 Ian Lance Taylor (ian@airs.com) * sys3.unx: David Nugent: don't declare chmod, since it may be prototyped to take an argument that is smaller than an int. * uucico.c (faccept_call): Chip Salzenberg: only declare sportinfo if it will be used (if HAVE_TAYLOR_CONFIG is true). * sys3.unx (isysdep_get_sequence): Chip Salzenberg: avoid use before set warning from gcc. * protf.c (ffprocess_data): Chip Salzenberg: avoid use before set warning from gcc. * sysh.unx, sys2.unx (fsserial_read, usysdep_pause): Chip Salzenberg: added HAVE_USLEEP configuration parameter for the AIX usleep routine. * uuchk.c, prtinf.c, config.c: Chip Salzenberg: strcmp is a macro on AIX, so avoid declaring it and undef it in config.c where we want to declare it because we want to take its address. * uucp.h, sys3.unx (fsysdep_get_status, fsysdep_set_status): Chip Salzenberg: handle out of range status codes in status files. * Makefile, sysh.unx: defined LIBDIR in the Makefile, rather than forcing the user to define it in two different places. * sys.unx, tstuu.c: Chip Salzenberg: can't declare execl, since it is varadic. * sys1.unx, sys2.unx, sys3.unx, sys5.unx, tstuu.c: David Nugent: can't declare open or fcntl, since they may use ... in header file prototype; added declaration for popen; added casts of first mkdir argument to char *. * sysh.unx, tstuu.c (uchild): Mike Park: added HAVE_WAITPID and HAVE_WAIT4 configuration parameters to allow the use of wait4 as found on the NeXT. * tstuu.c (uprepare_test): Mike Park: use IPUBLIC_DIRECTORY_MODE rather than S_IRWXU | S_IRWXG | S_IRWXO. * sysinf.c (tisystem): Mike Park: ulog was being passed the wrong number of arguments. Sun Jan 12 14:32:47 1992 Ian Lance Taylor (ian@airs.com) * Eliminated CONFIG, INSTALL and THANKS. They are now included in uucp.texi. Changed README and MANIFEST accordingly. Added uucp.info* and texinfo.tex to MANIFEST. * Makefile, uucp.texi: renamed taylor.texi to uucp.texi. * uucico.c (fcall, fdo_call): John Antypas: pass in sportinfo structure for fdo_call to use for an unknown port. * log.c (ulog): allocate enough bytes to name file if HAVE_BNU_LOGGING is in use but zLogfile has no %s. Sat Jan 11 12:11:56 1992 Ian Lance Taylor (ian@airs.com) * Makefile: changed to correspond to GNU standards, according to standards.text of 24 Nov 91. * Makefile: Franc,ois Pinard: use $(INSTALL_PROGRAM) and $(INSTALL) rather than cp to install the programs. * time.c (ftime_now), sys1.unx (usysdep_localtime): John Antypas: use memcpy to get the result of localtime rather than relying on structure assignment. * sysh.unx, prtinf.c: Hannu Strang: changed definition of SYSDEP_STDIN_CMDS to pass structure instead of address of structure to avoid bug in AIX compiler which causes it to fail to recognize an address constant containing the -> operator. Tue Jan 7 10:22:43 1992 Ian Lance Taylor (ian@airs.com) * Released beta 1.02. * protg.c (fgcheck_errors): discount out of order packets in the total error count, and allow a negative error count to mean that any number of errors are accepted. * sysinf.c (tadd_proto_param): Niels Baggesen: allocate number of protocol parameters based on *pc, not sIhold.cproto_params. Sat Jan 4 16:42:21 1992 Ian Lance Taylor (ian@airs.com) * log.c (ulog): tweaked HAVE_V2_LOGGING slightly. * v2.c (uv2_read_systems): set chat script correctly. * uucp.h, sys3.unx: avoid redefining SEEK_SET if it appears in but not . * chat.c (fcsend): made fcsend into a static function. * uucp.h, uucico.c, uuxqt.c, uux.c, uucp.c, uuchk.c, version.c: changed abProgram and abVersion from const char [] to char [] because the Microsoft compiler on SCO can't handle const char [] arrays correctly. * uux.c (main): allocate enough space for log message. * sys1.unx (usysdep_localtime): to get the current time, we can't call usysdep_full_time if the latter uses times. * sys1.unx, sys3.unx: added a couple more extern definitions for SCO 3.2.2. * uucico.c (main): start uuxqt even if a call fails. * sysh.unx, system.h, uuxqt.c (uqdo_xqt_file), sys5.unx (fsysdep_xqt_check_file): Chip Salzenberg: added configuration option ALLOW_FILENAME_ARGUMENTS to permit arguments that look like filenames, to allow undoing the patch I just made. Fri Jan 3 00:44:59 1992 Ian Lance Taylor (ian@airs.com) * system.h, uuxqt.c (uqdo_xqt_file), sys5.unx (fsysdep_xqt_check_file): David J. Fiander: make sure that if an argument looks like a filename we are permitted to access it. * sys3.unx (fsdo_lock): remove temporary file if link fails in fsdo_lock. Thu Jan 2 00:01:53 1992 Ian Lance Taylor (ian@airs.com) * protg.c (fgstart, fgshutdown, fgprocess_data): count remote rejections separately from resent packets when counting errors. Tue Dec 31 14:31:38 1991 Ian Lance Taylor (ian@airs.com) * protg.c (fgstart): Franc,ois Pinard: forgot to initialize cGdelayed_packets. * prot.h, uucico.c, prote.c: added the 'e' protocol, creating the new file prote.c * prot.c (freceive_data), protf.c (fffile), prott.c (ftfile): changed pffile protocol entry point to pass number of bytes being sent; fixed bug in freceive_data which caused to ask for the wrong number of bytes when the buffer was empty. Mon Dec 30 23:16:48 1991 Ian Lance Taylor (ian@airs.com) * sys2.unx (fsserial_open): Chip Salzenberg: don't turn on IXON and IXOFF initially; after all, the initialization packets might contain an XOFF character. Sun Dec 29 00:00:42 1991 Ian Lance Taylor (ian@airs.com) * uucp.h, prot.c (fploop): John Theus: check for EOF before reading from file to work around bug in Tektronix library. * protg.c (fprocess_data): don't send RR packets when an error occurs; the other side will probably ignore them, and it may confuse some Telebit modems. * sys1.unx (usysdep_localtime): don't take the address of a cast value. * log.c (zldate_and_time): wasn't allocating enough buffer space. Sat Dec 28 01:09:58 1991 Ian Lance Taylor (ian@airs.com) * uuxqt.c (uqdo_xqt_file): forgot to initialize zmail. * uucp.h, time.c, copy.c, sys1.unx, sys2.unx, sys3.unx, sys4.unx, sys5.unx, config.c, log.c, uuxqt.c, uux.c, tstuu.c: added a bunch of externs to decrease the number of implicit function definitions. * system.h, sys1.unx (usysdep_localtime), log.c (zldate_and_time): Lele Gaifax: put the full year in the log file, using the format YYYY-MM-DD HH:MM:SS.HH (ending in hundredths of seconds). * config.c (uprocesscmds): Terry Gardner: allow a # to be quoted in a configuration file. Also made uprocesscmds reentrant. * sysh.unx, sys2.unx (fsrun_chat), sys5.unx (fsysdep_execute), tstuu.c (uchild): Monty Solomon: added HAVE_UNION_WAIT configuration option to pass a variable of type union wait to the wait and waitpid system calls. * sysh.unx, sys1.unx (usysdep_initialize): John Theus: added HAVE_GETWD to use getwd instead of getcwd. * sysh.unx, sys1.unx (usysdep_full_time): added HAVE_FTIME configuration option. * tstuu.c (uchild): use TIMES_TICK from sysh.unx rather than CLK_TCK. * conf.h, uucp.h, util.c, getopt.c, tstuu.c: added HAVE_STRCHR and HAVE_INDEX to conf.h. Fri Dec 27 01:00:41 1991 Ian Lance Taylor (ian@airs.com) * uucico.c (fuucp): set fmasterdone correctly when running as a slave. * port.c (cpshow, upshow, fport_read, fport_write, fport_io): cleaned up debugging code by isolating out upshow and by making cpshow handle backslash. * tstuu.c (uprepare_test): create spool directories by hand, to make sure test 6 can be done and to test creating needed directories. * conf.h, uucp.h, bnu.c, v2.c, chat.c, protg.c, prott.c, sysinf.c, tcp.c, getopt.c, tstuu.c, util.c: added HAVE_MEMFNS and HAVE_BFNS to conf.h. Changed memset calls to use bzero. * protg.c (fgcheck_errors, fgprocess_data): added protocol parameter ``errors'' to set maximum number of errors permitted. Also made fgprocess_data only reply once per batch of data. Thu Dec 26 17:54:54 1991 Ian Lance Taylor (ian@airs.com) * tcp.c (ftcp_dial, itcp_port_number): Monty Solomon: cast arguments to avoid prototype errors on NeXT. Mon Dec 23 00:16:19 1991 Ian Lance Taylor (ian@airs.com) * uucp.h, sysinf.c, uucico.c (main, flogin_prompt, faccept_call), uuchk.c (main): David Nugent: allow debugging level to be set for a specific system. Sun Dec 22 15:51:10 1991 Ian Lance Taylor (ian@airs.com) * conf.h, uucp.c, sysh.unx, tcp.c, sys1.unx, sys2.unx, sys3.unx, sys5.unx, sys6.unx, tstuu.c: Monty Solomon: added HAVE_UNISTD_H to conf.h for systems which don't have . Also added externs for all functions from , which required adding definitions for pid_t, uid_t and off_t to sysh.unx. * config.c, prtinf.c, time.c, uuchk.c: added externs for strcasecmp or strncasecmp, to avoid implicit function definitions now that I took the prototypes out of uucp.h. * sys3.unx (fsysdep_get_status): Franc,ois Pinard: the code added to avoid scanf had a stupid bug. * uucp.h: Monty Solomon: removed prototypes for strcasecmp and strncasecmp from uucp.h, since they may be in string.h. Sat Dec 21 16:04:58 1991 Ian Lance Taylor (ian@airs.com) * uucp.h, uucico.c (ucatch), prot.c (fpsendfile_confirm, fprecfile_confirm, ustats_failed), file.c (fsent_file, freceived_file), log.c (ustats): Terry Gardner: record failed file transfers in the statistics file. * uucico.c: change protocol ordering to 't', 'g', 'f'. * uucico.c (faccept_call), tstuu.c (uprepare_test): John Theus: don't warn if the port file doesn't exist when the slave mode uucico looks up the port. * sys1.unx, sys5.unx: moved fsysdep_file_exists from sys5.unx to sys1.unx so that uucico can call it. * uux.c: Fran,cois Pinard: remove parentheses from ZSHELLSEPS so that they may be used to quote arguments as documented. This means that may not be used to start subshell, but that is no great loss. * uux.c (main): use ulog to report illegal grade error message. * sys1.unx (fsysdep_run): use the real program name from abProgram in the error messages in fsysdep_run. Thu Dec 19 19:02:28 1991 Ian Lance Taylor (ian@airs.com) * uucico.c (fdo_call, faccept_call): Terry Gardner: put the length of the conversation in the ``Call complete'' log file message. * uux.c: added space and tab to ZSHELLSEPS, because otherwise the command was parsed to include whitespace. * protg.c, protf.c: Oleg Tabarovsky: added statistical logging messages to the 'g' and 'f' protocols. These go to the main log file right now, but I'm not sure if that's appropriate. * sys2.unx (fsserial_set): don't change terminal settings if we don't have to. * port.c (fport_io): add complete diagnostics for fport_io so we can see every byte that goes in or out. * uucico.c (fuucp): don't give user name in errors produced by getting the next command. * uuxqt.c (main): don't process execute files for unknown systems. This is not unreasonable, I hope, and it avoids errors caused by an uninitialized sUnknown structure. * sys4.unx (fsysdep_get_work_init): sort the previously found files all together so we can correctly check new files using bsearch. * protf.c (pfprocess, pfprocess_data, pfwait): Franc,ois Pinard: don't kill 'f' protocol because of an illegal byte. Also slightly optimized the protocol to wait for up to seven characters at a time rather than just one. Wed Dec 18 00:12:42 1991 Ian Lance Taylor (ian@airs.com) * sysh.unx, sys2.unx, tstuu.c: Terry Gardner: added USE_FOR_UNBLOCKED configuration parameter to support systems that don't permit O_NONBLOCK and O_NDELAY to both be set. * tstuu.c (uchild): give the uucico processes a chance to die on their own rather than killing them immediately. * uuxqt.c (main, uqdo_xqt_file): David Nugent: keep rescanning the list of execute files until nothing can be done. Also, don't remove the execute file if we get some sort of internal error. * sys4.unx (fsysdep_get_work, usysdep_get_work_free): David Nugent: after we've processed all the work files we found initially, rescan the work directory to pick up any that may have come in in the meantime. Also, reset iSwork_file to 0 in usysdep_get_work_free so that we can handle more than one system. * tstuu.c, uucico.c (main, fwait_for_calls, flogin_prompt): added -l option to uucico to prompt for the login name and password once and then exit. Tue Dec 17 00:24:41 1991 Ian Lance Taylor (ian@airs.com) * uucp.h, uucico.c, uuxqt.c, uux.c, uucp.c, config.c (uread_config), log.c (ulog): eliminated ulog_program and added abProgram and ulog_to_file. Made log messages output to stderr more Unix like. * log.c (ulog): use a fixed number of fields in a log file message by replacing a missing system or user with '-'. * port.h, chat.c (renamed fchat_send to fcsend), bnu.c (fbnu_read_dialer_info), prtinf.c (tpcomplete), sys2.unx (fsysdep_modem_close), uuchk.c (ukshow_dialer): T. William Wells: change the modem complete and abort strings into chat scripts. * sys2.unx (fsserial_open): on BSD start in RAW mode to avoid dropping characters when we switch over. I originally thought being able to use XON/XOFF was worth the risk; I no longer think so. * tstuu.c (uprepare_test): have shell script sleep before printing the login name to make sure the second system has finished flushing its input buffer. * protg.c (fginit_sendbuffers), prott.c (ftsendcmd): David Nugent: avoid sending confidential information by zeroing out memory buffers before using them. * sysinf.c (tirequest, titransfer), prtinf.c (tpseven_bit, tpreliable, tpdtr_toggle): Marc Unangst: several functions did not accept true and false as boolean strings, contradicting the documentation. * uucp.h, system.h, sysh.unx, sys1.unx (usysdep_full_time), file.c (fstore_sendfile, fsent_file, fstore_recfile, freceived_file), log.c (ustats): generate statistics in microseconds instead of seconds for more accurate time keeping. * sys2.unx (fsserial_open): David Nugent: flush pending input when a serial port is opened. This will clear out a NO\sCARRIER string left by a previous dropped connection. Mon Dec 16 11:26:17 1991 Ian Lance Taylor (ian@airs.com) * uucico.c (main), uuxqt.c (main), tstuu.c (main, uchild): David Nugent: ignore SIGHUP in uucico and uuxqt, so that they are unaffected by the parent shell closing down and by the remote terminal dropping the connection. * bnu.c (ubnu_read_sysfiles, ubnu_read_systems, fbnu_find_port, fbnu_read_dialer_info): Mike Bernson: ignore lines that begin with whitespace, fix compilation error. Sat Dec 14 20:59:10 1991 Ian Lance Taylor (ian@airs.com) * sys2.unx (fsserial_open): don't turn on ISTRIP initially. * uucp.h, sysinf.c, chat.c (icexpect), uuchk.c (ukshow_chat): added chat-seven-bit command to allow control over whether parity bits are stripped out of chat scripts. * uucp.h, port.h, chat.c (fchat, fcprogram), config.c (tprocess_one_cmd), prtinf.c, sysinf.c (tichat, tprocess_chat_cmd), bnu.c (fbnu_read_dialer_info), port.c (fpdo_dial), uucico.c (fdo_call, faccept_call) uuchk.c (ukshow_chat): changed processing of chat commands to go through tprocess_chat_cmd, avoiding repetition. All chat script information is now held in an schat_info structure. Eliminated the fchat_program function, renaming it fcprogram and making it static to chat.c (it is now called via fchat). Added CMDTABTYPE_PREFIX. Added INIT_CHAT macro to initialize chat script information. Added TTYPE_CMDTABTYPE and CARGS_CMDTABTYPE to eliminate hex constants in tprocess_one_cmd. * sys5.unx (zsysdep_get_xqt): Oleg Tabarovsky: don't stop processing files just because opendir failed on one; it could just be because we don't have read permission. Fri Dec 13 17:43:52 1991 Ian Lance Taylor (ian@airs.com) * config.c (uprocesscmds): don't continually allocate and free the array of arguments. Thu Dec 12 12:46:01 1991 Ian Lance Taylor (ian@airs.com) * prot.c (fgetcmd): Franc,ois Pinard: don't bother to give an error if the final HY doesn't come in; apparently the MtXinu UUCP doesn't send it. * chat.c (icexpect, fchat_send): Franc,ois Pinard: add some chat script debugging messages. * log.c (ulog): David Nugent: move ERROR: from the start of the line to after the date and time; this makes it easier to use awk on log files. * sys3.unx (fsdo_lock), sys1.unx (usysdep_initialize): do locking using link rather than O_CREAT | O_EXCL to avoid race conditions and to safely run as the superuser. * sys3.unx (fsysdep_move_file): fcopyfile now creates the destination file with IPRIVATE_MODE, so we don't need to chmod to it. * sys1.unx (usysdep_initialize, fsysdep_other_config): set the GID as well as the UID, in case anybody wants to run this as a setgid package. Wed Dec 11 10:03:22 1991 Ian Lance Taylor (ian@airs.com) * conf.h, uucp.h, util.c (strtol): Mark Powell: added my own version of strtol to util.c, for systems which lack it. * protg.c (fgstart, fgexchange_init): if we start resending packets during initialization, don't forget which packets we have already seen; otherwise the other side may assume we've already seen them while we're looking for them. Tue Dec 10 15:42:41 1991 Ian Lance Taylor (ian@airs.com) * conf.h, sysh.unx, log.c (ulog, ustats), tstuu.c (uprepare_test): Arne Ludwig: merged in Arne Ludwig's patches to support V2 and BNU style logging, with some minor additions and changes. * sys1.unx, sys3.unx, sys5.unx, uux.c, uucp.c, uucico.c, copy.c, uucp.h, system.h: create directories when necessary rather than assuming that they exist. Added fmkdirs argument to esysdep_fopen and fcopy_file, changing all calls. Added fpublic argument to fsysdep_make_dirs, changing all calls. Moved fsysdep_make_dirs and fsdirectory_exists from sys3.unx to sys1.unx. Added checks for ENOENT in several places in sys3.unx and sys5.unx. * log.c, port.c (fport_open), sys2.unx (fsserial_open): added ulog_device routine to record device name. This is currently only used for the BNU statistics file, but more uses might arise later. * file.c, log.c, uucp.h: moved statistics file routines from file.c to log.c in preparation for supporting BNU log file routines. Mon Dec 9 12:00:52 1991 Ian Lance Taylor (ian@airs.com) * bnu.c (ubnu_read_systems): Arne Ludwig: the device entry for a system can be followed by a comma and a list of protocols. * sysh.unx, sys3.unx (fsdo_lock): Richard Todd: add HAVE_V2_LOCKFILES, in which the process ID is written out in binary. * uuxqt.c (uqdo_xqt_file): Richard Todd: the requestor address is relative to the requesting system. * config.c (uprocesscmds, umulti_pushback): Richard Todd: each line pushed back because of "#" is local to a particular smulti_file structure. * prtinf.c (asPdialer_cmds): Richard Todd: exit the current dialer if the special command "#" is seen. A similar thing should be put in for ports, but it's marginally more complex. * config.c (uprocesscmds): Richard Todd: don't warn if the special "#" command is unrecognized. Sat Dec 7 13:05:40 1991 Ian Lance Taylor (ian@airs.com) * config.c (uprocesscmds): Franc,ois Pinard: don't limit the number of arguments to a command! * chat.c (fchat): handle a chat script which consists only of a single string. Fri Dec 6 16:11:29 1991 Ian Lance Taylor (ian@airs.com) * sys5.unx (fsysdep_execute): David J. Fiander: if execve fails with ENOEXEC, try using /bin/sh with a quoted argument. * uux.c (main): split arguments the way /bin/sh does. If any shell metacharacters appears, request uuxqt to execute the command using /bin/sh. * tstuu.c (uprepare_test): allow the uux to test to send a failure message. * uuxqt.c (uqdo_xqt_file): don't send mail on successful execution unless specifically requested; pay attention to the 'n' line which requests mail on success; ignore the 'Z' line because it now specifies the default action. * sys1.unx (usysdep_initialize), sys6.unx (zsysdep_add_cwd): Franc,ois Pinard: getcwd may legitimately fail, so only give an error if we really need the result. * chat.c (ccescape): Franc,ois Pinard: ccescape should never return a negative number, since the callers don't know how to deal with that. Mon Dec 2 16:26:16 1991 Ian Lance Taylor (ian@airs.com) * bnu.c (ubnu_read_systems): Dave Buck: time strings with grades were parsed in an endless loop! * sys3.unx (fsdo_lock, fsdo_unlock): the alloca when using LOCKDIR was one byte too small. * config.c (tprocess_one_cmd): pass 10 to strtol rather than 0 to avoid surprises if a leading zero is used. * prtinf.c (tpproto_param, tpdialer_proto_param): Niels Baggesen: the ``protocol-parameter'' command didn't work for ports or dialers. Sun Dec 1 09:46:12 1991 Ian Lance Taylor (ian@airs.com) * tstuu.c: don't use the fd_set typedef at all. * tstuu.c (uprepare_test): don't read V2 or BNU configuration files while testing. * bnu.c, v2.c, config.c (uread_config): David Nugent: even if the code was compiled with HAVE_TAYLOR_CONFIG, read the V2 and BNU configuration files if the code was compiled to support them. * uuchk.c (fkshow_port): Bob Izenberg: report dialer/token pairs correctly. Sat Nov 30 17:40:00 1991 Ian Lance Taylor (ian@airs.com) * tstuu.c: Bob Izenberg: copied over conditional definitions of EAGAIN and EWOULDBLOCK from sys2.unx. * bnu.c (fbnu_read_dialer_info): Niels Baggesen: accept dialers with no substitutions. * bnu.c (ubnu_read_systems, ubadd_perm): Niels Baggesen: don't free up zline in ubadd_perm; in fact, changed the calling sequence to not even pass zline in at all. * bnu.c (ubadd_perm): Niels Baggesen: didn't handle multiple MACHINE= and multiple LOGNAME= values in a single Permissions line, because it was clobbering the machine name while processing the first logname. * bnu.c: Made zread and zwrite elements of sperm structure const char * to avoid warning. * copy.c, sys1.unx, sys2.unx, sys3.unx, sys5.unx, tstuu.c: Niels Baggesen: don't multiply include . Eventually there should be a macro controlling whether it gets included at all, for non-POSIX systems. * sys3.unx (fsysdep_get_status, isysdep_get_sequence): Marty Shannon: accept a truncated status file. I also eliminated scanf calls in sys3.unx, since that was the only place it was called; this was to make the executables smaller for systems which cared. * bnu.c (ubnu_read_sysfiles): Marty Shannon: accept comment characters in Sysfiles. * sysh.unx, sys3.unx: Marty Shannon: added HAVE_RENAME, put a fake rename system call in sys3.unx. * prtinf.c (ffind_port): Marty Shannon: failed to handle multiple ports in the port file since I forgot to reset my pointer variable. * bnu.c (ubnu_read_systems): Marty Shannon: don't initialize the auto array abpubdir, since old cc didn't permit initialization of auto aggregates. Mon Nov 25 20:56:39 1991 Ian Lance Taylor (ian@airs.com) * tstuu.c: Bob Denny: add definitions for FD_SET, FD_ZERO and FD_ISSET. * config.c: Bob Denny: add explicit externs for strcmp and strcasecmp. * sys2.unx: the fread_blocking and fwrite_blocking fields were not getting initialized correctly in the TCP support routines. * sysh.unx, sys2.unx, sys5.unx, tstuu.c: Marty Shannon: added configuration option HAVE_SYSWAIT_H. * bnu.c (fbnu_find_port, fbnu_read_dialer_info), v2.c (fv2_find_port): Marty Shannon: the ireliable field of ports and dialers was not getting initialized. Sun Nov 24 15:06:37 1991 Ian Lance Taylor (ian@airs.com) * tcp.c (itcp_port_number): Michael Haberler: wasn't calling htons if passed a numeric string. Sat Nov 23 13:43:52 1991 Ian Lance Taylor (ian@airs.com) * Released version 1.01 to alt.sources and uunet uucp-1.07/INSTALL0000664000076400007640000001722707665321754007171 Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.in' is used to create `configure' by a program called `autoconf'. You only need `configure.in' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Operation Controls ================== `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. uucp-1.07/Makefile.am0000664000076400007640000000564407665520557010176 # This is the auto-Makefile for Taylor UUCP # This is processed by automake to create Makefile.in. SUBDIRS = lib uuconf unix sbin_PROGRAMS = uucico uuxqt uuchk uuconv sbin_SCRIPTS = uusched bin_PROGRAMS = uux uucp uustat uuname uulog uupick cu bin_SCRIPTS = uuto noinst_PROGRAMS = tstuu info_TEXINFOS = uucp.texi man_MANS = uux.1 uucp.1 uustat.1 cu.1 uucico.8 uuxqt.8 # If you do not have the mkdir system call, this will install uudir as # an suid root program. This is necessary because invoking /bin/mkdir # from an suid program will leave the directories owned by the wrong # user. if HAVE_MKDIR uudirdir = uudir_PROGRAMS = else uudirdir = $(sbindir)/util uudir_PROGRAMS = uudir endif AM_CFLAGS = $(WARN_CFLAGS) $(UUDIRFLAGS) LDADD = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a UUHEADERS = uucp.h uudefs.h uuconf.h policy.h system.h sysdep.h getopt.h uucico_SOURCES = uucico.c trans.h trans.c send.c rec.c xcmd.c prot.h prot.c \ protg.c protf.c prott.c prote.c proti.c protj.c proty.c protz.c \ time.c log.c chat.c conn.h conn.c util.c copy.c $(UUHEADERS) uuxqt_SOURCES = uuxqt.c util.c log.c copy.c $(UUHEADERS) uux_SOURCES = uux.c util.c log.c copy.c $(UUHEADERS) uucp_SOURCES = uucp.c util.c log.c copy.c $(UUHEADERS) uustat_SOURCES = uustat.c util.c log.c copy.c $(UUHEADERS) uuname_SOURCES = uuname.c log.c $(UUHEADERS) uulog_SOURCES = uulog.c log.c $(UUHEADERS) uupick_SOURCES = uupick.c log.c copy.c $(UUHEADERS) cu_SOURCES = cu.h cu.c prot.c log.c chat.c conn.c copy.c $(UUHEADERS) uuchk_SOURCES = uuchk.c $(UUHEADERS) uuconv_SOURCES = uuconv.c $(UUHEADERS) tstuu_SOURCES = tstuu.c uudir_SOURCES = uudir.c uuconv_CFLAGS = -I$(srcdir)/uuconf $(AM_CFLAGS) uusched: uusched.in Makefile -rm -f $@ uusched.t if [ "x$(POUNDBANG)" = "xno" ]; then \ sed -e 's,#!/bin/sh,:,' -e 's,@SBINDIR@,$(sbindir),' < $(srcdir)/uusched.in > uusched.t; \ else \ sed -e 's,@SBINDIR@,$(sbindir),' < $(srcdir)/uusched.in > uusched.t; \ fi chmod 0555 uusched.t mv -f uusched.t $@ uuto: uuto.in Makefile -rm -f $@ uuto.t if [ "x$(POUNDBANG)" = "xno" ]; then \ sed -e 's,#!/bin/sh,:,' -e 's,@BINDIR@,$(bindir),' -e 's,@VERS@,$(VERSION),' < $(srcdir)/uuto.in > uuto.t; \ else \ sed -e 's,@BINDIR@,$(bindir),' -e 's,@VERS@,$(VERSION),' < $(srcdir)/uuto.in > uuto.t; \ fi chmod 0555 uuto.t mv -f uuto.t $@ CLEANFILES = uusched uuto EXTRA_DIST = uusched.in uuto.in \ cu.1 uucp.1 uustat.1 uux.1 uucico.8 uuxqt.8 \ contrib sample dist-hook: -rm -rf $(distdir)/contrib/CVS $(distdir)/sample/CVS install-exec-hook: for f in uucico uuxqt; do \ chown $(OWNER) $(DESTDIR)$(sbindir)/$${f}; \ chmod 4555 $(DESTDIR)$(sbindir)/$${f}; \ done for f in uux uucp uustat uuname cu; do \ chown $(OWNER) $(DESTDIR)$(bindir)/$${f}; \ chmod 4555 $(DESTDIR)$(bindir)/$${f}; \ done if HAVE_MKDIR else chown $(OWNER) $(DESTDIR)$(uudirdir) chmod 100 $(DESTDIR)$(uudirdir) chown root $(DESTDIR)$(uudirdir)/uudir chmod 4555 $(DESTDIR)$(uudirdir)/uudir endif uucp-1.07/Makefile.in0000664000076400007640000011114307665532174010175 # Makefile.in generated automatically by automake 1.5 from Makefile.am. # Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 # 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@ # This is the auto-Makefile for Taylor UUCP # This is processed by automake to create Makefile.in. SHELL = @SHELL@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ sharedstatedir = @sharedstatedir@ localstatedir = @localstatedir@ libdir = @libdir@ infodir = @infodir@ mandir = @mandir@ includedir = @includedir@ oldincludedir = /usr/include pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = . ACLOCAL = @ACLOCAL@ AUTOCONF = @AUTOCONF@ AUTOMAKE = @AUTOMAKE@ AUTOHEADER = @AUTOHEADER@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_HEADER = $(INSTALL_DATA) transform = @program_transform_name@ NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : AMTAR = @AMTAR@ AR = @AR@ AWK = @AWK@ CC = @CC@ DEPDIR = @DEPDIR@ EXEEXT = @EXEEXT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LIBOBJS = @LIBOBJS@ LN_S = @LN_S@ MAINT = @MAINT@ NEWCONFIGDIR = @NEWCONFIGDIR@ OBJEXT = @OBJEXT@ OLDCONFIGDIR = @OLDCONFIGDIR@ OWNER = @OWNER@ PACKAGE = @PACKAGE@ POUNDBANG = @POUNDBANG@ RANLIB = @RANLIB@ UNIXOBJS = @UNIXOBJS@ VERSION = @VERSION@ WARN_CFLAGS = @WARN_CFLAGS@ am__include = @am__include@ am__quote = @am__quote@ install_sh = @install_sh@ SUBDIRS = lib uuconf unix sbin_PROGRAMS = uucico uuxqt uuchk uuconv sbin_SCRIPTS = uusched bin_PROGRAMS = uux uucp uustat uuname uulog uupick cu bin_SCRIPTS = uuto noinst_PROGRAMS = tstuu info_TEXINFOS = uucp.texi man_MANS = uux.1 uucp.1 uustat.1 cu.1 uucico.8 uuxqt.8 # If you do not have the mkdir system call, this will install uudir as # an suid root program. This is necessary because invoking /bin/mkdir # from an suid program will leave the directories owned by the wrong # user. @HAVE_MKDIR_TRUE@uudirdir = @HAVE_MKDIR_FALSE@uudirdir = $(sbindir)/util @HAVE_MKDIR_TRUE@uudir_PROGRAMS = @HAVE_MKDIR_FALSE@uudir_PROGRAMS = uudir AM_CFLAGS = $(WARN_CFLAGS) $(UUDIRFLAGS) LDADD = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a UUHEADERS = uucp.h uudefs.h uuconf.h policy.h system.h sysdep.h getopt.h uucico_SOURCES = uucico.c trans.h trans.c send.c rec.c xcmd.c prot.h prot.c \ protg.c protf.c prott.c prote.c proti.c protj.c proty.c protz.c \ time.c log.c chat.c conn.h conn.c util.c copy.c $(UUHEADERS) uuxqt_SOURCES = uuxqt.c util.c log.c copy.c $(UUHEADERS) uux_SOURCES = uux.c util.c log.c copy.c $(UUHEADERS) uucp_SOURCES = uucp.c util.c log.c copy.c $(UUHEADERS) uustat_SOURCES = uustat.c util.c log.c copy.c $(UUHEADERS) uuname_SOURCES = uuname.c log.c $(UUHEADERS) uulog_SOURCES = uulog.c log.c $(UUHEADERS) uupick_SOURCES = uupick.c log.c copy.c $(UUHEADERS) cu_SOURCES = cu.h cu.c prot.c log.c chat.c conn.c copy.c $(UUHEADERS) uuchk_SOURCES = uuchk.c $(UUHEADERS) uuconv_SOURCES = uuconv.c $(UUHEADERS) tstuu_SOURCES = tstuu.c uudir_SOURCES = uudir.c uuconv_CFLAGS = -I$(srcdir)/uuconf $(AM_CFLAGS) CLEANFILES = uusched uuto EXTRA_DIST = uusched.in uuto.in \ cu.1 uucp.1 uustat.1 uux.1 uucico.8 uuxqt.8 \ contrib sample subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = bin_PROGRAMS = uux$(EXEEXT) uucp$(EXEEXT) uustat$(EXEEXT) \ uuname$(EXEEXT) uulog$(EXEEXT) uupick$(EXEEXT) cu$(EXEEXT) noinst_PROGRAMS = tstuu$(EXEEXT) sbin_PROGRAMS = uucico$(EXEEXT) uuxqt$(EXEEXT) uuchk$(EXEEXT) \ uuconv$(EXEEXT) @HAVE_MKDIR_TRUE@uudir_PROGRAMS = @HAVE_MKDIR_FALSE@uudir_PROGRAMS = uudir$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) $(sbin_PROGRAMS) \ $(uudir_PROGRAMS) am_cu_OBJECTS = cu.$(OBJEXT) prot.$(OBJEXT) log.$(OBJEXT) chat.$(OBJEXT) \ conn.$(OBJEXT) copy.$(OBJEXT) cu_OBJECTS = $(am_cu_OBJECTS) cu_LDADD = $(LDADD) cu_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a cu_LDFLAGS = am_tstuu_OBJECTS = tstuu.$(OBJEXT) tstuu_OBJECTS = $(am_tstuu_OBJECTS) tstuu_LDADD = $(LDADD) tstuu_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a tstuu_LDFLAGS = am_uuchk_OBJECTS = uuchk.$(OBJEXT) uuchk_OBJECTS = $(am_uuchk_OBJECTS) uuchk_LDADD = $(LDADD) uuchk_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uuchk_LDFLAGS = am_uucico_OBJECTS = uucico.$(OBJEXT) trans.$(OBJEXT) send.$(OBJEXT) \ rec.$(OBJEXT) xcmd.$(OBJEXT) prot.$(OBJEXT) protg.$(OBJEXT) \ protf.$(OBJEXT) prott.$(OBJEXT) prote.$(OBJEXT) proti.$(OBJEXT) \ protj.$(OBJEXT) proty.$(OBJEXT) protz.$(OBJEXT) time.$(OBJEXT) \ log.$(OBJEXT) chat.$(OBJEXT) conn.$(OBJEXT) util.$(OBJEXT) \ copy.$(OBJEXT) uucico_OBJECTS = $(am_uucico_OBJECTS) uucico_LDADD = $(LDADD) uucico_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uucico_LDFLAGS = am_uuconv_OBJECTS = uuconv-uuconv.$(OBJEXT) uuconv_OBJECTS = $(am_uuconv_OBJECTS) uuconv_LDADD = $(LDADD) uuconv_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uuconv_LDFLAGS = am_uucp_OBJECTS = uucp.$(OBJEXT) util.$(OBJEXT) log.$(OBJEXT) \ copy.$(OBJEXT) uucp_OBJECTS = $(am_uucp_OBJECTS) uucp_LDADD = $(LDADD) uucp_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uucp_LDFLAGS = am_uudir_OBJECTS = uudir.$(OBJEXT) uudir_OBJECTS = $(am_uudir_OBJECTS) uudir_LDADD = $(LDADD) uudir_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uudir_LDFLAGS = am_uulog_OBJECTS = uulog.$(OBJEXT) log.$(OBJEXT) uulog_OBJECTS = $(am_uulog_OBJECTS) uulog_LDADD = $(LDADD) uulog_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uulog_LDFLAGS = am_uuname_OBJECTS = uuname.$(OBJEXT) log.$(OBJEXT) uuname_OBJECTS = $(am_uuname_OBJECTS) uuname_LDADD = $(LDADD) uuname_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uuname_LDFLAGS = am_uupick_OBJECTS = uupick.$(OBJEXT) log.$(OBJEXT) copy.$(OBJEXT) uupick_OBJECTS = $(am_uupick_OBJECTS) uupick_LDADD = $(LDADD) uupick_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uupick_LDFLAGS = am_uustat_OBJECTS = uustat.$(OBJEXT) util.$(OBJEXT) log.$(OBJEXT) \ copy.$(OBJEXT) uustat_OBJECTS = $(am_uustat_OBJECTS) uustat_LDADD = $(LDADD) uustat_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uustat_LDFLAGS = am_uux_OBJECTS = uux.$(OBJEXT) util.$(OBJEXT) log.$(OBJEXT) \ copy.$(OBJEXT) uux_OBJECTS = $(am_uux_OBJECTS) uux_LDADD = $(LDADD) uux_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uux_LDFLAGS = am_uuxqt_OBJECTS = uuxqt.$(OBJEXT) util.$(OBJEXT) log.$(OBJEXT) \ copy.$(OBJEXT) uuxqt_OBJECTS = $(am_uuxqt_OBJECTS) uuxqt_LDADD = $(LDADD) uuxqt_DEPENDENCIES = unix/libunix.a uuconf/libuuconf.a lib/libuucp.a uuxqt_LDFLAGS = SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS) DEFS = @DEFS@ DEFAULT_INCLUDES = -I. -I$(srcdir) -I. CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ depcomp = $(SHELL) $(top_srcdir)/depcomp @AMDEP_TRUE@DEP_FILES = $(DEPDIR)/chat.Po $(DEPDIR)/conn.Po \ @AMDEP_TRUE@ $(DEPDIR)/copy.Po $(DEPDIR)/cu.Po $(DEPDIR)/log.Po \ @AMDEP_TRUE@ $(DEPDIR)/prot.Po $(DEPDIR)/prote.Po \ @AMDEP_TRUE@ $(DEPDIR)/protf.Po $(DEPDIR)/protg.Po \ @AMDEP_TRUE@ $(DEPDIR)/proti.Po $(DEPDIR)/protj.Po \ @AMDEP_TRUE@ $(DEPDIR)/prott.Po $(DEPDIR)/proty.Po \ @AMDEP_TRUE@ $(DEPDIR)/protz.Po $(DEPDIR)/rec.Po \ @AMDEP_TRUE@ $(DEPDIR)/send.Po $(DEPDIR)/time.Po \ @AMDEP_TRUE@ $(DEPDIR)/trans.Po $(DEPDIR)/tstuu.Po \ @AMDEP_TRUE@ $(DEPDIR)/util.Po $(DEPDIR)/uuchk.Po \ @AMDEP_TRUE@ $(DEPDIR)/uucico.Po $(DEPDIR)/uuconv-uuconv.Po \ @AMDEP_TRUE@ $(DEPDIR)/uucp.Po $(DEPDIR)/uudir.Po \ @AMDEP_TRUE@ $(DEPDIR)/uulog.Po $(DEPDIR)/uuname.Po \ @AMDEP_TRUE@ $(DEPDIR)/uupick.Po $(DEPDIR)/uustat.Po \ @AMDEP_TRUE@ $(DEPDIR)/uux.Po $(DEPDIR)/uuxqt.Po \ @AMDEP_TRUE@ $(DEPDIR)/xcmd.Po COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ CFLAGS = @CFLAGS@ DIST_SOURCES = $(cu_SOURCES) $(tstuu_SOURCES) $(uuchk_SOURCES) \ $(uucico_SOURCES) $(uuconv_SOURCES) $(uucp_SOURCES) \ $(uudir_SOURCES) $(uulog_SOURCES) $(uuname_SOURCES) \ $(uupick_SOURCES) $(uustat_SOURCES) $(uux_SOURCES) \ $(uuxqt_SOURCES) INFO_DEPS = uucp.info DVIS = uucp.dvi TEXINFOS = uucp.texi NROFF = nroff MANS = $(man_MANS) RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ uninstall-info-recursive all-recursive install-data-recursive \ install-exec-recursive installdirs-recursive install-recursive \ uninstall-recursive check-recursive installcheck-recursive DIST_COMMON = README ./stamp-h.in AUTHORS COPYING ChangeLog INSTALL \ Makefile.am Makefile.in NEWS TODO aclocal.m4 compile \ config.h.in configure configure.in depcomp install-sh missing \ mkinstalldirs texinfo.tex DIST_SUBDIRS = $(SUBDIRS) SOURCES = $(cu_SOURCES) $(tstuu_SOURCES) $(uuchk_SOURCES) $(uucico_SOURCES) $(uuconv_SOURCES) $(uucp_SOURCES) $(uudir_SOURCES) $(uulog_SOURCES) $(uuname_SOURCES) $(uupick_SOURCES) $(uustat_SOURCES) $(uux_SOURCES) $(uuxqt_SOURCES) all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: .SUFFIXES: .c .dvi .info .o .obj .ps .texi $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) cd $(top_srcdir) && \ $(AUTOMAKE) --gnu Makefile Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status cd $(top_builddir) && \ CONFIG_HEADERS= CONFIG_LINKS= \ CONFIG_FILES=$@ $(SHELL) ./config.status $(top_builddir)/config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES) cd $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ configure.in cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) config.h: stamp-h @if test ! -f $@; then \ rm -f stamp-h; \ $(MAKE) stamp-h; \ else :; fi stamp-h: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h stamp-hT @echo timestamp > stamp-hT 2> /dev/null cd $(top_builddir) \ && CONFIG_FILES= CONFIG_HEADERS=config.h \ $(SHELL) ./config.status @mv stamp-hT stamp-h $(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/./stamp-h.in @if test ! -f $@; then \ rm -f $(srcdir)/./stamp-h.in; \ $(MAKE) $(srcdir)/./stamp-h.in; \ else :; fi $(srcdir)/./stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) @rm -f $(srcdir)/./stamp-h.in $(srcdir)/./stamp-h.inT @echo timestamp > $(srcdir)/./stamp-h.inT 2> /dev/null cd $(top_srcdir) && $(AUTOHEADER) @mv $(srcdir)/./stamp-h.inT $(srcdir)/./stamp-h.in distclean-hdr: -rm -f config.h install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(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 '$(transform);s/$$/$(EXEEXT)/'`; \ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$f"; \ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$f; \ else :; fi; \ done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; for p in $$list; do \ f=`echo $$p|sed '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) clean-noinstPROGRAMS: -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(DESTDIR)$(sbindir) @list='$(sbin_PROGRAMS)'; for p in $$list; do \ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ if test -f $$p \ ; then \ f=`echo $$p1|sed '$(transform);s/$$/$(EXEEXT)/'`; \ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/$$f"; \ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/$$f; \ else :; fi; \ done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(sbin_PROGRAMS)'; for p in $$list; do \ f=`echo $$p|sed 's/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ echo " rm -f $(DESTDIR)$(sbindir)/$$f"; \ rm -f $(DESTDIR)$(sbindir)/$$f; \ done clean-sbinPROGRAMS: -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) install-uudirPROGRAMS: $(uudir_PROGRAMS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(DESTDIR)$(uudirdir) @list='$(uudir_PROGRAMS)'; for p in $$list; do \ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ if test -f $$p \ ; then \ f=`echo $$p1|sed '$(transform);s/$$/$(EXEEXT)/'`; \ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(uudirdir)/$$f"; \ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(uudirdir)/$$f; \ else :; fi; \ done uninstall-uudirPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(uudir_PROGRAMS)'; for p in $$list; do \ f=`echo $$p|sed 's/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ echo " rm -f $(DESTDIR)$(uudirdir)/$$f"; \ rm -f $(DESTDIR)$(uudirdir)/$$f; \ done clean-uudirPROGRAMS: -test -z "$(uudir_PROGRAMS)" || rm -f $(uudir_PROGRAMS) cu$(EXEEXT): $(cu_OBJECTS) $(cu_DEPENDENCIES) @rm -f cu$(EXEEXT) $(LINK) $(cu_LDFLAGS) $(cu_OBJECTS) $(cu_LDADD) $(LIBS) tstuu$(EXEEXT): $(tstuu_OBJECTS) $(tstuu_DEPENDENCIES) @rm -f tstuu$(EXEEXT) $(LINK) $(tstuu_LDFLAGS) $(tstuu_OBJECTS) $(tstuu_LDADD) $(LIBS) uuchk$(EXEEXT): $(uuchk_OBJECTS) $(uuchk_DEPENDENCIES) @rm -f uuchk$(EXEEXT) $(LINK) $(uuchk_LDFLAGS) $(uuchk_OBJECTS) $(uuchk_LDADD) $(LIBS) uucico$(EXEEXT): $(uucico_OBJECTS) $(uucico_DEPENDENCIES) @rm -f uucico$(EXEEXT) $(LINK) $(uucico_LDFLAGS) $(uucico_OBJECTS) $(uucico_LDADD) $(LIBS) uuconv-uuconv.$(OBJEXT): uuconv.c uuconv$(EXEEXT): $(uuconv_OBJECTS) $(uuconv_DEPENDENCIES) @rm -f uuconv$(EXEEXT) $(LINK) $(uuconv_LDFLAGS) $(uuconv_OBJECTS) $(uuconv_LDADD) $(LIBS) uucp$(EXEEXT): $(uucp_OBJECTS) $(uucp_DEPENDENCIES) @rm -f uucp$(EXEEXT) $(LINK) $(uucp_LDFLAGS) $(uucp_OBJECTS) $(uucp_LDADD) $(LIBS) uudir$(EXEEXT): $(uudir_OBJECTS) $(uudir_DEPENDENCIES) @rm -f uudir$(EXEEXT) $(LINK) $(uudir_LDFLAGS) $(uudir_OBJECTS) $(uudir_LDADD) $(LIBS) uulog$(EXEEXT): $(uulog_OBJECTS) $(uulog_DEPENDENCIES) @rm -f uulog$(EXEEXT) $(LINK) $(uulog_LDFLAGS) $(uulog_OBJECTS) $(uulog_LDADD) $(LIBS) uuname$(EXEEXT): $(uuname_OBJECTS) $(uuname_DEPENDENCIES) @rm -f uuname$(EXEEXT) $(LINK) $(uuname_LDFLAGS) $(uuname_OBJECTS) $(uuname_LDADD) $(LIBS) uupick$(EXEEXT): $(uupick_OBJECTS) $(uupick_DEPENDENCIES) @rm -f uupick$(EXEEXT) $(LINK) $(uupick_LDFLAGS) $(uupick_OBJECTS) $(uupick_LDADD) $(LIBS) uustat$(EXEEXT): $(uustat_OBJECTS) $(uustat_DEPENDENCIES) @rm -f uustat$(EXEEXT) $(LINK) $(uustat_LDFLAGS) $(uustat_OBJECTS) $(uustat_LDADD) $(LIBS) uux$(EXEEXT): $(uux_OBJECTS) $(uux_DEPENDENCIES) @rm -f uux$(EXEEXT) $(LINK) $(uux_LDFLAGS) $(uux_OBJECTS) $(uux_LDADD) $(LIBS) uuxqt$(EXEEXT): $(uuxqt_OBJECTS) $(uuxqt_DEPENDENCIES) @rm -f uuxqt$(EXEEXT) $(LINK) $(uuxqt_LDFLAGS) $(uuxqt_OBJECTS) $(uuxqt_LDADD) $(LIBS) install-binSCRIPTS: $(bin_SCRIPTS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(DESTDIR)$(bindir) @list='$(bin_SCRIPTS)'; for p in $$list; do \ f="`echo $$p|sed '$(transform)'`"; \ if test -f $$p; then \ echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(bindir)/$$f"; \ $(INSTALL_SCRIPT) $$p $(DESTDIR)$(bindir)/$$f; \ elif test -f $(srcdir)/$$p; then \ echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(bindir)/$$f"; \ $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(bindir)/$$f; \ else :; fi; \ done uninstall-binSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(bin_SCRIPTS)'; for p in $$list; do \ f="`echo $$p|sed '$(transform)'`"; \ echo " rm -f $(DESTDIR)$(bindir)/$$f"; \ rm -f $(DESTDIR)$(bindir)/$$f; \ done install-sbinSCRIPTS: $(sbin_SCRIPTS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(DESTDIR)$(sbindir) @list='$(sbin_SCRIPTS)'; for p in $$list; do \ f="`echo $$p|sed '$(transform)'`"; \ if test -f $$p; then \ echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(sbindir)/$$f"; \ $(INSTALL_SCRIPT) $$p $(DESTDIR)$(sbindir)/$$f; \ elif test -f $(srcdir)/$$p; then \ echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(sbindir)/$$f"; \ $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(sbindir)/$$f; \ else :; fi; \ done uninstall-sbinSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(sbin_SCRIPTS)'; for p in $$list; do \ f="`echo $$p|sed '$(transform)'`"; \ echo " rm -f $(DESTDIR)$(sbindir)/$$f"; \ rm -f $(DESTDIR)$(sbindir)/$$f; \ done mostlyclean-compile: -rm -f *.$(OBJEXT) core *.core distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/chat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/conn.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/copy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/cu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/prot.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/prote.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/protf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/protg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/proti.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/protj.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/prott.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/proty.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/protz.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/rec.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/send.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/time.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/trans.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tstuu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/util.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uuchk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uucico.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uuconv-uuconv.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uucp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uudir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uulog.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uuname.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uupick.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uustat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uux.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uuxqt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/xcmd.Po@am__quote@ distclean-depend: -rm -rf $(DEPDIR) .c.o: @AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< .c.obj: @AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(COMPILE) -c `cygpath -w $<` uuconv-uuconv.o: uuconv.c @AMDEP_TRUE@ source='uuconv.c' object='uuconv-uuconv.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/uuconv-uuconv.Po' tmpdepfile='$(DEPDIR)/uuconv-uuconv.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(uuconv_CFLAGS) $(CFLAGS) -c -o uuconv-uuconv.o `test -f uuconv.c || echo '$(srcdir)/'`uuconv.c uuconv-uuconv.obj: uuconv.c @AMDEP_TRUE@ source='uuconv.c' object='uuconv-uuconv.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/uuconv-uuconv.Po' tmpdepfile='$(DEPDIR)/uuconv-uuconv.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(uuconv_CFLAGS) $(CFLAGS) -c -o uuconv-uuconv.obj `cygpath -w uuconv.c` CCDEPMODE = @CCDEPMODE@ uucp.info: uucp.texi uucp.dvi: uucp.texi .texi.info: @cd $(srcdir) && rm -f $@ $@-[0-9] $@-[0-9][0-9] cd $(srcdir) \ && $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) \ `echo $< | sed 's,.*/,,'` .texi.dvi: TEXINPUTS=$(srcdir):$$TEXINPUTS \ MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ $(TEXI2DVI) $< .texi: @cd $(srcdir) && rm -f $@ $@-[0-9] $@-[0-9][0-9] cd $(srcdir) \ && $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) \ `echo $< | sed 's,.*/,,'` MAKEINFO = @MAKEINFO@ TEXI2DVI = texi2dvi DVIPS = dvips .dvi.ps: $(DVIPS) $< -o $@ uninstall-info-am: $(PRE_UNINSTALL) @if (install-info --version && \ install-info --version | fgrep -i -v debian) >/dev/null 2>&1; then \ list='$(INFO_DEPS)'; \ for file in $$list; do \ echo " install-info --info-dir=$(DESTDIR)$(infodir) --remove $(DESTDIR)$(infodir)/$$file"; \ install-info --info-dir=$(DESTDIR)$(infodir) --remove $(DESTDIR)$(infodir)/$$file; \ done; \ else :; fi @$(NORMAL_UNINSTALL) @list='$(INFO_DEPS)'; \ for file in $$list; do \ (if cd $(DESTDIR)$(infodir); then \ echo " rm -f $$file $$file-[0-9] $$file-[0-9][0-9])"; \ rm -f $$file $$file-[0-9] $$file-[0-9][0-9]; \ else :; fi); \ done dist-info: $(INFO_DEPS) list='$(INFO_DEPS)'; \ for base in $$list; do \ d=$(srcdir); \ for file in `CDPATH=: && cd $$d && eval echo $$base*`; do \ test -f $(distdir)/$$file \ || cp -p $$d/$$file $(distdir)/$$file; \ done; \ done mostlyclean-aminfo: -rm -f uucp.aux uucp.cp uucp.cps uucp.dvi uucp.fn uucp.fns uucp.ky uucp.log \ uucp.pg uucp.ps uucp.toc uucp.tp uucp.vr maintainer-clean-aminfo: cd $(srcdir) && \ for i in $(INFO_DEPS); do \ rm -f $$i; \ if test "`echo $$i-[0-9]*`" != "$$i-[0-9]*"; then \ rm -f $$i-[0-9]*; \ fi; \ done man1dir = $(mandir)/man1 install-man1: $(man1_MANS) $(man_MANS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(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/^.*\\.//'`; \ 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/^.*\\.//'`; \ 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 man8dir = $(mandir)/man8 install-man8: $(man8_MANS) $(man_MANS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(DESTDIR)$(man8dir) @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ for i in $$l2; do \ case "$$i" in \ *.8*) 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/^.*\\.//'`; \ 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)$(man8dir)/$$inst"; \ $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst; \ done uninstall-man8: @$(NORMAL_UNINSTALL) @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ for i in $$l2; do \ case "$$i" in \ *.8*) list="$$list $$i" ;; \ esac; \ done; \ for i in $$list; do \ ext=`echo $$i | sed -e 's/^.*\\.//'`; \ 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)$(man8dir)/$$inst"; \ rm -f $(DESTDIR)$(man8dir)/$$inst; \ done # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): @set fnord $(MAKEFLAGS); amf=$$2; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" mostlyclean-recursive clean-recursive distclean-recursive \ maintainer-clean-recursive: @set fnord $(MAKEFLAGS); amf=$$2; \ dot_seen=no; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ rev=''; for subdir in $$list; do \ if test "$$subdir" = "."; then :; else \ rev="$$subdir $$rev"; \ fi; \ done; \ rev="$$rev ."; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done tags: TAGS ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(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 $(LISP) TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ here=`pwd`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS) $(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 "$(ETAGS_ARGS)config.h.in$$unique$(LISP)$$tags" \ || etags $(ETAGS_ARGS) $$tags config.h.in $$unique $(LISP) GTAGS: here=`CDPATH=: && cd $(top_builddir) && pwd` \ && cd $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) $$here distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) top_distdir = . # Avoid unsightly `./'. distdir = $(PACKAGE)-$(VERSION) GZIP_ENV = --best distdir: $(DISTFILES) -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) mkdir $(distdir) @for file in $(DISTFILES); do \ if test -f $$file; then d=.; else d=$(srcdir); fi; \ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ if test "$$dir" != "$$file" && test "$$dir" != "."; then \ $(mkinstalldirs) "$(distdir)/$$dir"; \ fi; \ if test -d $$d/$$file; then \ cp -pR $$d/$$file $(distdir) \ || exit 1; \ else \ test -f $(distdir)/$$file \ || cp -p $$d/$$file $(distdir)/$$file \ || exit 1; \ fi; \ done for subdir in $(SUBDIRS); do \ if test "$$subdir" = .; then :; else \ test -d $(distdir)/$$subdir \ || mkdir $(distdir)/$$subdir \ || exit 1; \ (cd $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" \ distdir=../$(distdir)/$$subdir \ distdir) \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="${top_distdir}" distdir="$(distdir)" \ dist-info dist-hook -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r $(distdir) dist: distdir $(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist -chmod -R a+w $(distdir) > /dev/null 2>&1; rm -rf $(distdir) GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(AMTAR) xf - chmod -R a-w $(distdir); chmod a+w $(distdir) mkdir $(distdir)/=build mkdir $(distdir)/=inst chmod a-w $(distdir) dc_install_base=`CDPATH=: && cd $(distdir)/=inst && pwd` \ && cd $(distdir)/=build \ && ../configure --srcdir=.. --prefix=$$dc_install_base \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && (test `find $$dc_install_base -type f -print | wc -l` -le 1 \ || (echo "Error: files left after uninstall" 1>&2; \ exit 1) ) \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && $(MAKE) $(AM_MAKEFLAGS) distclean \ && rm -f $(distdir).tar.gz \ && (test `find . -type f -print | wc -l` -eq 0 \ || (echo "Error: files left after distclean" 1>&2; \ exit 1) ) -chmod -R a+w $(distdir) > /dev/null 2>&1; rm -rf $(distdir) @echo "$(distdir).tar.gz is ready for distribution" | \ sed 'h;s/./=/g;p;x;p;x' check-am: all-am check: check-recursive all-am: Makefile $(INFO_DEPS) $(PROGRAMS) $(SCRIPTS) $(MANS) config.h installdirs: installdirs-recursive installdirs-am: $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(sbindir) $(DESTDIR)$(uudirdir) $(DESTDIR)$(bindir) $(DESTDIR)$(sbindir) $(DESTDIR)$(infodir) $(DESTDIR)$(man1dir) $(DESTDIR)$(man8dir) install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* 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-recursive clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \ clean-sbinPROGRAMS clean-uudirPROGRAMS mostlyclean-am dist-all: distdir $(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) distclean: distclean-recursive -rm -f config.status config.cache config.log distclean-am: clean-am distclean-compile distclean-depend \ distclean-generic distclean-hdr distclean-tags dvi: dvi-recursive dvi-am: $(DVIS) info: info-recursive info-am: $(INFO_DEPS) install-data-am: install-info-am install-man install-uudirPROGRAMS install-exec-am: install-binPROGRAMS install-binSCRIPTS \ install-sbinPROGRAMS install-sbinSCRIPTS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-info: install-info-recursive install-info-am: $(INFO_DEPS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(DESTDIR)$(infodir) @list='$(INFO_DEPS)'; \ for file in $$list; do \ d=$(srcdir); \ for ifile in `CDPATH=: && cd $$d && echo $$file $$file-[0-9] $$file-[0-9][0-9]`; do \ if test -f $$d/$$ifile; then \ echo " $(INSTALL_DATA) $$d/$$ifile $(DESTDIR)$(infodir)/$$ifile"; \ $(INSTALL_DATA) $$d/$$ifile $(DESTDIR)$(infodir)/$$ifile; \ else : ; fi; \ done; \ done @$(POST_INSTALL) @if (install-info --version && \ install-info --version | fgrep -i -v debian) >/dev/null 2>&1; then \ list='$(INFO_DEPS)'; \ for file in $$list; do \ echo " install-info --info-dir=$(DESTDIR)$(infodir) $(DESTDIR)$(infodir)/$$file";\ install-info --info-dir=$(DESTDIR)$(infodir) $(DESTDIR)$(infodir)/$$file || :;\ done; \ else : ; fi install-man: install-man1 install-man8 installcheck-am: maintainer-clean: maintainer-clean-recursive maintainer-clean-am: distclean-am maintainer-clean-aminfo \ maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-aminfo mostlyclean-compile \ mostlyclean-generic uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \ uninstall-info-am uninstall-man uninstall-sbinPROGRAMS \ uninstall-sbinSCRIPTS uninstall-uudirPROGRAMS uninstall-info: uninstall-info-recursive uninstall-man: uninstall-man1 uninstall-man8 .PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \ clean-recursive clean-sbinPROGRAMS clean-uudirPROGRAMS dist \ dist-all dist-info distcheck distclean distclean-compile \ distclean-depend distclean-generic distclean-hdr \ distclean-recursive distclean-tags distdir dvi dvi-am \ dvi-recursive info info-am info-recursive install install-am \ install-binPROGRAMS install-binSCRIPTS install-data \ install-data-am install-data-recursive install-exec \ install-exec-am install-exec-recursive install-info \ install-info-am install-info-recursive install-man install-man1 \ install-man8 install-recursive install-sbinPROGRAMS \ install-sbinSCRIPTS install-strip install-uudirPROGRAMS \ installcheck installcheck-am installdirs installdirs-am \ installdirs-recursive maintainer-clean maintainer-clean-aminfo \ maintainer-clean-generic maintainer-clean-recursive mostlyclean \ mostlyclean-aminfo mostlyclean-compile mostlyclean-generic \ mostlyclean-recursive tags tags-recursive uninstall \ uninstall-am uninstall-binPROGRAMS uninstall-binSCRIPTS \ uninstall-info-am uninstall-info-recursive uninstall-man \ uninstall-man1 uninstall-man8 uninstall-recursive \ uninstall-sbinPROGRAMS uninstall-sbinSCRIPTS \ uninstall-uudirPROGRAMS uusched: uusched.in Makefile -rm -f $@ uusched.t if [ "x$(POUNDBANG)" = "xno" ]; then \ sed -e 's,#!/bin/sh,:,' -e 's,@SBINDIR@,$(sbindir),' < $(srcdir)/uusched.in > uusched.t; \ else \ sed -e 's,@SBINDIR@,$(sbindir),' < $(srcdir)/uusched.in > uusched.t; \ fi chmod 0555 uusched.t mv -f uusched.t $@ uuto: uuto.in Makefile -rm -f $@ uuto.t if [ "x$(POUNDBANG)" = "xno" ]; then \ sed -e 's,#!/bin/sh,:,' -e 's,@BINDIR@,$(bindir),' -e 's,@VERS@,$(VERSION),' < $(srcdir)/uuto.in > uuto.t; \ else \ sed -e 's,@BINDIR@,$(bindir),' -e 's,@VERS@,$(VERSION),' < $(srcdir)/uuto.in > uuto.t; \ fi chmod 0555 uuto.t mv -f uuto.t $@ dist-hook: -rm -rf $(distdir)/contrib/CVS $(distdir)/sample/CVS install-exec-hook: for f in uucico uuxqt; do \ chown $(OWNER) $(DESTDIR)$(sbindir)/$${f}; \ chmod 4555 $(DESTDIR)$(sbindir)/$${f}; \ done for f in uux uucp uustat uuname cu; do \ chown $(OWNER) $(DESTDIR)$(bindir)/$${f}; \ chmod 4555 $(DESTDIR)$(bindir)/$${f}; \ done @HAVE_MKDIR_FALSE@ chown $(OWNER) $(DESTDIR)$(uudirdir) @HAVE_MKDIR_FALSE@ chmod 100 $(DESTDIR)$(uudirdir) @HAVE_MKDIR_FALSE@ chown root $(DESTDIR)$(uudirdir)/uudir @HAVE_MKDIR_FALSE@ chmod 4555 $(DESTDIR)$(uudirdir)/uudir # 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: uucp-1.07/NEWS0000664000076400007640000001426007665321754006631 Changes in version 1.07: As usual, many bugs were fixed. uuchk now reports the configuration file names which it uses. This makes it easier to figure out how to configure a binary installation. Certain options which could previously be set in Makefile.in are now set as options to configure: --with-user, --with-newconfigdir, --with-oldconfigdir. You can now use file names and notification addresses which contain spaces, but only when talking to another instance of version 1.07. The exit status of uux now uses values from . TCP ports now support IPv6 on modern systems. A new ``version'' command in the port file may be used to restrict a TCP port to just IPv4 or just IPv6. \M and \m may now be used in any chat script, not just a dialer chat script. Added ``max-file-time'' command to sys file. When uucico automatically invokes uuxqt, it no longer passes the -s option. Changes in version 1.06: As usual, many bugs were fixed. Support was added for the 'y' protocol, contributed by Jorge Cwik, the designer of the protocol. The name of command files was changed when using SPOOLDIR_TAYLOR; they now use a much longer sequence number. This affects the jobid strings reported by uustat. The change makes uux and uucp more efficient. Added ``called-timegrade'' command to sys file. Added --nostop and -E/--escape options to cu. Added -s option to uuchk. Added ``strip-login'' and ``strip-proto'' commands to config file. uux will now create a poll file if invoked as ``uux SYSTEM!''. If an execution fails, the data file is saved, if there is enough free disk space. uux now uses POSIX file locking, if available, when locking the sequence file. The man pages were incorporated into the Texinfo manual. The UUCP Internals FAQ was incorporated into the Texinfo manual. The spool directory layout is now documented in the Texinfo manual. Changes in version 1.05: As usual, many bugs were fixed. Support was added for the UUPC/extended 'v' protocol. Initial hardware flow control support was added, contributed by Peter Wemm. A new port type, ``pipe'', was added; this sends data to a program such as rlogin, and was contributed by Marc Boucher. The programs all now accept long option names with a leading --. They all support --help and --version. uuxqt now saves execution files it can not parse in $(SPOOLDIR)/.Corrupt. If a received file can not be moved to the final location, and there is enough disk space, it is kept in the spool directory. A run-uuxqt command was added to config, to permit specifying when uuxqt should be run. The 'i' protocol has a new ack-frequency protocol parameter. The remote-window protocol parameter was removed, as it did not work correctly. Chat scripts now permit /W in an expect string to set the timeout. TCP ports now support the ``dialer-sequence'' command. Direct ports now support the ``carrier'' command. Some support was added to read UUCP passwords from /etc/passwd, and to use encrypted passwords. uucico now accepts a -C argument to only call a system named by -S or -s if there is work for it. uucico accepts a -i TLI argument to use TLI I/O calls on standard input. uucico accepts a -u argument to set the user name. uucico accepts a -z argument to try the next alternate if a call fails. uustat accepts a -R argument to rejuvenate each listed job. Mailer configuration was moved from configure checking to policy.h. Support was added for QNX, contributed by Joe Wells. Changes in version 1.04: IMPORTANT: the default when talking to another version of 1.04 is to use the new bidirectional 'i' protocol. If you are using a half-duplex modem, such as a Telebit T2500, you will want to either mark the port as half-duplex with the ``half-duplex'' command, or force use of the 'g' protocol by using the ``protocol'' command in the sys or port file or by adding ``,g'' after the port name in the Systems or L.sys or Devices file. As usual, many bugs were fixed. Bidirectional transfers are supported with the new 'i' protocol; it requires an eight-bit clear datapath. New programs: uusched, cu, uuto and uupick. The 'G' protocol and a new Zmodem protocol were added. A number of uustat options were added to support uuclean, and a sample uuclean shell script was added to the contrib directory. The uustat output formats were changed slightly. A protocol extension eliminates transfer of the command file for simple commands, such as rmail or rnews, when talking to another version of 1.04. Some TLI support was added. UUCP forwarding was added, along with the ``forward-to'', ``forward-from'' and ``forward'' commands. If a file transfer fails in the middle, the retry will now start from where it left off. The implementation is compatible with SVR4. The work queue is checked every 10 minutes during a conversation; if there is new work and a bidirectional protocol is not in use, the receiving uucico requests the sender to transfer control. The amount of free disk space is checked periodically as a file is received, and if it drops too low the call is aborted. The UUCP configuration file reading routines were moved into a standalone library, uuconf. All known bugs in V2 and HDB configuration file reading were fixed. The ``half-duplex'' command was added for the port and dialer files. The ``max-retries'', ``success-wait'', ``send-request'' and ``receive-request'' commands were added for the sys file. The ``call-request'' and ``called-request'' commands were eliminated (they did not work correctly anyhow). \d in chat scripts now calls sleep (2) rather than sleep (1), so it will sleep longer (on some systems sleep(1) may delay much less than one second). SPOOLDIR_SVR4 was added for SVR4 style spool directories. Defaults are now permitted in the port and dialer files. The ALIAS field is supported in the HDB Permissions file. uucp-1.07/TODO0000664000076400007640000006254707665321754006635 This is a list of things to do for the Taylor UUCP package. Please feel free to work on any of them. You may want to check with me first to make sure that nobody else is working on them as well. Some of these are my thoughts, but most are suggestions from other people; I have tried to give credit. They are in the order I received them; the missing numbers have already been implemented. Just because something is on the list doesn't mean that I necessarily think it is a good idea. It does mean that I think it's worth thinking about. 2. John Cowan says: >I think you should accept a broader range of time specifications. >Consider using getdate() (from your handy Usenet news source code) >with its high-powered yacc parser. Of course, getdate() accepts a single date, but we want a range. A better syntax would be certainly be nice. 9. Gordon Burditt warns about modifications to the TZ environment variable, to fool uucico into dialing out at an inappropriate time. 10. Gordon Burditt says: >(4) Less important, because few people will have this problem, is a >port-specific dialcodes file. Why? Well, one system I had was connected >to 2 inside lines "dial 9 for outside line", and one outside line (which >doesn't want the 9). A number of the systems called were "inside", so >you didn't add the 9 on those lines dialing from inside, but you did add >"390" to the 4-digit number if you dialed it via "outside". Also not >unheard of are systems with 2 outside lines that are local to different >area codes, or one local outside line and one WATS line (which MUST >have an area code). >Example: > inside-line Dialcodes outside-line Dialcodes > pbx "" pbx "390" > local "9" local "" > nyc "9-1212" nyc "1212" 12. Ralf E. Stranzenbach says: >It would be nice to also have the option of running a shell script each time >uucico connects/disconnects a systen. I do not mean shell scripts for dial/in. >I would like to do some accounting and batching when the connection >establishes. 13. les@chinet.chi.il.us (Leslie Mikesell) writes: >>local-send /usr/spool/uucppublic !/usr/spool/uucpublic/private >> >>The directories are searched from left to right, and the last one to >>match determines whether the file may be sent or not. This is >>slightly more general than NOWRITE, since it permits a public >>directory within a private directory within a public directory, >>although probably nobody will ever want that. > >Interesting... The obvious enhancement is to generalize to shell-like >wild cards for the READ/WRITE/COMMANDS entries. 14. Should there be a way for chat scripts to specify the parity to generate? I don't think there's much point to specifying what parity to accept. 17. The -b and -s switches to uux are not implemented by uuxqt. 18. If we are supposed to call a system back, we should do it immediately rather than merely queuing up an empty command file. 22. Add an ftp port type which uses anonymous ftp rather than any of the UUCP protocols to do file transfers. This would allow ftp work to be done late at night, and allow neighbors of cooperative Internet sites to use UUCP forwarding for anonymous FTP. 32. It would be nice if uucico could sleep until a line was available. This is complicated by the possibility of wanting to wait for any of several different lines, and one would really want some sort of semaphore function to do it right. If the available lines could be sorted, then each could be assigned to a byte in a line lock file. Looking for a line could be done by sleeping on a read lock on all possible lines. Once it came through, write locks would be attempted. If they all failed, somebody else snuck in, so you would sleep on a read lock again. This isn't great because a process could be starved, but it might be better than nothing. This could be tied in to uucp and uux, such that they wouldn't actually fire up uucico unless a line was known to be available; an additional switch would be used to fire up uucico anyhow (or one could switch the default behaviour and the switch). So how do you sort the lines? You could just use the index in the port (or Devices) file, but what if multiple ports used the same physical device? Hmmm. 43. David Nugent: it would be nice to be able to set debugging, log, and statistics files on a site by site basis. Brian Murrell: heck, set those files on a port by port basis as well. 74. Yanek Martinson: allow each system to independently choose whether to permit shell execution. 81. Marty Shannon: log reason for dial failure (chat-fail string) in .Status file. 83. Switch between 'M' and 'S' correctly in the BNU log file output. 86. Les Mikesell: allow a separate program to be specified to handle the communications with a particular system. 105. T. William Wells: close and open the Debug file after each file transfer. Alternatively, cycle through a series of Debug file names every 1000 lines or so. 106. Marty Shannon: add a time command for ports, to specify when they may be used. 115. T. William Wells: new options for uustat: -i display job ids only Also, there should perhaps be a configuration option to request uustat to only display jobs submitted by the user running uustat, except for root and uucp. 117. Marc Unangst: provide some way to change the debugging level of a running uucico. T. William Wells suggests having it read a file to change arbitrary configuration information, although obviously one has to be careful of what gets changed while a connection is active. 120. Jarmo Raiha: new chat-fail commands: one to not update the status file and require a retry wait, and one to permit the string to occur a few times before reporting an error. 124. Peter da Silva: perhaps there should be a ``chat-end-program'' command to let a program be run after the initial handshake has been completed and the protocol has been selected and turned on. This would let people run stty to change their terminal parameters. 128. Richard Stallman: have an interactive program to set up a chat script. It would let you type directly to the port, recording what you type as send strings and recording what comes back from the other side as expect strings. 129. Use POSIX fcntl locks when possible instead of creating a lock file. 138. T. William Wells: BNU apparently uses a file named A.whatever to hold the line number reached in current C. file processing. This is a hack, and won't work right with size control anyhow, but fsysdep_did_work could, for example, clobber the first byte in the line to a # or something to mark that it had been finished. Still a hack, but a better one. 139. Patrick Smith: incorporate patches to generate full debugging traces with less debugging file overhead. The debugging file repeats too much information at great length right now--not good. 141. Franc,ois Pinard: batch up pauses and delays in chat scripts and do them all at once in a single system call. This is particularly useful for pauses on systems which don't support subsecond sleeps. For everything else it's a fairly minor optimization. 142. Franc,ois Pinard: give uustat an option to requeue jobs to another system. This only makes a lot of sense for rmail executions, but it's fairly easy to do for any type of command. I think uucico does all the file checking needed to ensure that this doesn't break security, but that should be double-checked. 144. T. William Wells: add a -g option to uucico to permit specifying the maximum grade to be transferred at that time. This could restrict the timegrade command further, but should not be permitted to override it. 145. T. William Wells: if uucico or uuxqt get started with bad arguments, put an indication in the log file since stderr may be /dev/null. 146. Richard Todd: it would be nice to sometimes be able to request the other side to turn on debugging. 147. Bart Schaefer: some more possible options for uucico: -R reverse roles (hangup immediately). Not too exciting. some method to restrict calling to particular systems. 148. Jarmo Raiha: some method to control the work queue at the remote end. This could get awfully general, though. 149. The interaction of the time command and defaults can be confusing, since any time command in the actual system entry, even a fairly specific one, will wipe out the default entry. Not sure what can be done about this. 150. Jarmo Raiha: should there be some way to specify modem initialization strings when uucico is hanging on a port with -l or -e? This would presumably require a new type of chat script associated with a dialer. 151. Petri Helenius: log complete CONNECT string reported by modem, so that the baud rate is recorded in the log file. 152. Marc Evans: let the protocol selection be based on the CONNECT string, so that different protocols could be selected based on what type of connection was made. 153. Chris Lewis: provide a signal to get a core dump even on systems which won't do core dumps if the uid is not the euid. One could catch a signal, call setuid (getuid ()), and then raise the signal again. Unfortunately the core dump has to wind up in a directory which is world writable, so that the process is able to create the core file, but is not world readable, since that would permit anybody to read the core dump file and extract private information from it. 154. Les Mikesell: write a new version of dial.o, with provisions for running a chat script. 155. Scott Blachowicz: perhaps there should be some way to telling uucico to not log certain errors. This could get fairly complex, though. 156. Franc,ois Pinard: have uustat -m report the time of the last successful conversation when reporting a failure. 158. Thomas Fischer: should there be a way to completely disable an entry in the sys, port or dial file? Such as a ``disable'' command? 159. Petri Helenius: when uuxqt -s is invoked, lock uuxqt for the system so that only one uuxqt is invoked per system. If the -c option is used, don't lock on a per system basis, and ignore any per system locks (regardless of -s). If neither option is used, respect existing system and command locks, and do any other type of file. 161. Scott Blachowicz: provide some sort of include mechanism for the configuration files. 164. Ed Carp: preserve files if uuxqt execution fails. 166. Chip Salzenberg: allow chat failure strings to specify a retry time. 168. Jose A. Manas: allow a maximum connect time, after which we try to hang up the connection. This requires a protocol extension, since there's no way to force the other side to hang up. The best we can do without an extension is refuse to send any new jobs ourselves. Of course, we could just drop the connection. 169. Franc,ois Pinard: when given uustat -k00FC, check each possible job ID and use it if there is an unambiguous one. 170. T. William Wells: if ! HAVE_SETREUID && ! HAVE_SAVED_SETUID, fork a subprocesses to revoke setuid and read the file over a pipe. 171. Provide some option to have the internal uuconf functions not start with an underscore. 172. T. William Wells: have some way to configure the parity for cu. 173. Gert Doering: uuchk should display unknown system information. 175. T. William Wells: Cu will not let itself be interrupted before the connection is established. If the chat script doesn't write something, cu does something odd, I've forgotten exactly what. Cu takes an inordinate amount of time after the line drops to exit. Somebody, cu, I think, but maybe uucico, drops dtr twice sometimes. Again, somebody will attempt to write after a hangup signal has been received. Once a hangup has been received, I/O should not be attempted. Among other things this will save the bacon of those who have brain damaged serial drivers (FAS, sigh, is among them) that don't handle output properly on a dropped line. Me: Note that sometimes you do want to write to a line after receiving a hangup signal. For example, you might want to use ATZ to reset a modem. 176. Hans-Dieter Doll: provide some way (another escape sequence) to pass the protocol to a chat-program. Or, allow the protocol as an argument to the chat script command, which is more general, but maybe a bit too fancy. 177. Nickolay Saukh: use a default port for cu, you can just do ``cu number''. 180. Nickolay Saukh: if we have received a partial file, request the remote system to start sending from that point. We currently accept SVR4 style remote file positioning requests, but we do not generate them. 181. Mark Powell: provide some way to restrict file transfer by size as well as grade? One way would be to let uux select the grade based on the file size. 182. Mark Powell: permit using multiple timetables in a single time statement. 183. Optionally check for interrupts in fcopy_file, since it can take a long time to copy a file named in a uucp request. 185. A syntax error in a command received from the remote system should not hold up the queue. Unfortunately, I don't know what can be done except deny the command and report it. Reporting a garbled command error should report the command correctly, rather than just the first character. 186. Franc,ois Pinard: have an option to control nostop vs. stop on the cu command line. 187. Fix the notion of %nostop to be SVID compatible. 188. Frank Conrad: provide a means to set the strip mode for a port, to make it easy to use it from cu. 189. Marc Unangst: there should be a way to specify that a system should only be called if there are jobs of a certain grade, but if the system is called then jobs of any grade should be transferred. This basically means splitting the ``timegrade'' command into two commands: ``place-call-timegrade'' and ``transfer-timegrade''. Or maybe another optional argument to ``timegrade'': timegrade grade time-string [retry] [transfer-any] not to mention time time-string [retry] [transfer-any] Or maybe a separate command for a system or port like transfer-any BOOL 190. Chip Salzenberg: it would be really nice if uucico could automatically figure out when it could use an E command, so that uux didn't have to generate it and so that uucico could use with other versions of uux. Unfortunately, it would require uucico to read the execution file to see if it were suitable; this would be complex, but it would probably be worth it since normally the execution file would wind up not being sent. Of course, the current method works too; it's just harder to combine with other versions of UUCP. 191. Brian J. Murrell: should there be a way to cu a specific alternate? 192. Andrew A. Chernov: Perhaps cu -pport system should be able to try different alternates for the system, because there might be different phone numbers to try. 193. Brian J. Murrell: it would be nice to be able to ^C a cu chat script if you know it's going to fail. Right now you have to use ^\. 194. Steven S. Dick: have some way to force uucico off the phone at a certain time. If that is done, it might be cool to have some way to predict how long a file transfer will take, and not do it if it will take too long. But, if doing file restart, you can just quit and then pick it up later. 195. Franc,ois Pinard: if the disk fills up, or some other error occurs, while receiving a file, perhaps it would make sense to turn the connection around immediately and see if the other side had anything to do, and then try again later. This would require a protocol extension. I don't know if it's worth it. The code should be checked to see how well it handles a disk full situation. 197. Try alternate IP addresses if there are any. 198. Lele Gaifax: mention the device in Stats, and provide some way to associate the entry in Log with the entry in Stats. 199. Michael Richardson: provide some way to turn on parity for the login chat, since some systems apparently require it. Provide some way for cu to control parity after connecting. 200. Chip Salzenberg: add max-remote-debug to config. 201. Gert Doering: change the timeout message in chat scripts to reflect which chat script timed out (dialer or login). 202. Bill Foote: have uuchk check whether a system is defined more than once. 203. Eric Ziegast: allow specification of the minimum grade to receive, as well as the maximum grade. Probably sending a second character after the -pM argument would work fine. 204. Tom Rushworth: perhaps there should be some program which can be used to retrieve the current spool directory. Perhaps on option on uustat. 207. James B. O'Connor: use additional messages in the status file when placing a call, such as Dialing, Chatting, and the like. Slightly less efficient. 208. When checking whether a file may be received into a directory, perhaps uucico should check using the real user ID rather than insisting that the directory be world writable. This should be a policy.h parameter. This would enable sites which use different uids for each incoming UUCP login to have better control over security. 209. Jon Vos: add an alias command for ports. 210. Joe Wells: I'd like to have a way so that if the dial chat fails due to "NO CARRIER", in addition to this log message: ERROR: Chat script failed: Got "NO\sCARRIER" I would get another log message right next to it that would look like this: ERROR: Chat script failed: 5 "RRING" strings seen Ian: I doubt this is worth implementing in uucico, but it might make sense for an external, or otherwise more independent and controllable, chat program. 211. Joe Wells: In some cases it would be nice to be able to change the set of chat-fail strings in the middle of the chat script. Personally, I think this is too complex for the simple chat scripts currently implemented. 212. Joe Wells: There should be an option to all programs directing them to send all debugging output to the log file. This would just involve calling ulog_to_file at some point just after reporting any usage messages. 213. Joe Wells: There should be a way to specify the execution directory used by uuxqt. This would avoid certain sorts of permissions problems. Some mechanism would still be needed for using multiple directories. 214. Joe Wells: uuto should be documented. 215. Joe Wells: Perhaps it should be possible to use multiple spool directories. It would be a lot of work, though. 216. Joe Wells: It should be possible to specify only one of complete or abort. 217. Dan Everhart: It would be nice if the chat-fail string could affect the error message reported by uustat, so that uustat could say something ``Line was busy''. 218. Andrew A. Chernov: Add a chat-char-delay xx configuration parameter, which has the effect of adding \p after each character, with delay xx. This is to accommodate modems which can't accept command characters at a given baud rate. 219. Gert Doering: Provide some mechanism for specifying the maximum length of a call. Convenient for anonymous UUCP sites. 220. Joe Wells: There should be some way for "cu" to obey user commands during the dial chat. Right now, the only thing the user can do is send signals (e.g. type Control-C). This leads to user complaints that "cu" is not obeying its documentation. 221. Joe Wells: Right now, if there is any failure in the dial or login chat scripts, the remote system alternate is skipped even though the cause of the failure may have been the local serial port or modem. "uucico" will not try another modem with the same remote system alternate. If the remote system only has one alternate, then it is skipped entirely. Thus, there should be a way to specify that when certain expect strings are not seen or certain chat-fail strings are seen that the port is skipped instead of the remote system alternate. 222. Richard H. Gumpertz: Support pipelines in uuxqt. Right now they are only supported if uux puts in an 'e' line (which it does) and shell executions are permitted (which they normally are not). It would be possible to permit restricted pipelines by handling the pipe character specially and making sure all commands in the pipeline were permitted. 223. Bill Sommerfeld: When dialing out, set the status to DIALING rather than CONNECTION CLOSED. Setting the status takes a bit of time; it's hard to tell where the right break-even point is. 224. Joe Wells: Keep track of the last successful incoming call separately from the last successful outgoing call. Currently the two times are both put together in the status file. 225. Joe Wells: It would be nice if uustat would provide a way to avoid bouncing mail that it sent itself, to avoid sending notification e-mail for notification e-mail. I can't think of a mechanism, though (using a special grade for uustat does not work because most mail programs do not provide a mechanism for passing a grade through to uux). 226. Joe Wells: It would be nice if uustat could know whether it had sent mail for a particular job, to avoid generating multiple messages for the job. 227. Joe Wells: It would be nice if dialcode suffixes were supported, as well as prefixes. 228. Joe Wells: It would be nice to support another spool directory scheme which split stuff up more to avoid very large directories. This would be most useful for the files which are named in C. and X. files, rather than for the C. and X. files themselves (since C. and X. files are rarely looked up by name). Basically, some sort of partition of the D. directory is called for. 229. Joe Wells: ``It would be nice if the exit sequence of "cu", where it runs the complete chat script and then disconnects could be aborted without disconnecting. (Yes, I know, this is a strange desire.) It would be nice to be able to reinvoke the dial chat by user command in "cu". It would be nice to be able to invoke an arbitrary named chat script in "cu".'' 230. Kevin Johnson: Provide some mechanism such that all requests to a particular system were automatically forwarded through some other system. This would be useful to hide details of a non-strongly- connected network, particularly if the details were subject to change. 231. Gert Doering: Perhaps it should be possible to -r the default for uucp and uux. This would require adding a new option to force the invocation of uucico. 232. Mark Davies: spaces are not handled correctly in the -a argument of uux. If an E command is generated, the requestor address is not quoted correctly, nor is it parsed correctly. If an execution file is generated, the R line is not parsed correctly. 233. Emmanuel Mogenet: provide some mechanism for a maximum number of garbage bytes during a chat script before giving up. 234. Scott Ballantyne: The address for a TCP port should be separate from the phone number, so that a TCP dialer can use \D. 235. Peter Wemm: The 'i' protocol default parameters do not work at 2400 baud, because the time it takes to transfer half the packets is less than the timeout time. Of course people can always change the parameters, but it would be nice if this were dealt with somehow. 236. Andrew A. Chernov: Perhaps uuxqt should log when it terminates. 237. dialer-sequence doesn't really do the right thing. There is no way to specify both the TCP address and a phone number. 238. cu -t doesn't work at all. It does nothing. 239. Paul Pryor: Perhaps a service command should be added to the sys and port files. This command would be used as ``service cu'' to indicate that the system or port was only available to cu, not to UUCP. 240. Tim Iverson: The 'g' and 'i' protocol code tends to parse data packet headers twice. It processes them once to find out how large the packet is, waits for that data to arrive, and then processes the header again the second time around. It would be possible to avoid this by changing the code to be a simple state machine which remembered where it was in the process of parsing the packet. 241. Klaus Dahlenburg: Log a more precise reason for failure in the .Status file: e.g., all ports in use, no matching ports, etc. 242. Steven S. Dick: It would be helpful if there was a way to get uuchk to dump the data from ports/dialers/etc that was NOT picked up from the sys file. In other words, add some option to scan the port file and the dial file, presumably using uuconf_find_port for the former and uuconf_dialer_names for the latter. 243. Kai Michael Kretschmann: Add a configure option to drop the connection if jobs are coming in too slowly, indicating a bad connection. 244. John Plate: Have uustat -k make a entry in the Log file when a job is removed, for symmetry with when a job is inserted. 245. Joerg Dorchain: Provide a mechanism to permit the system name to be interpolated into the command used for port type pipe. 246. Michael Ju. Tokarev: Add a way for uucico to receive a file into a directory which is not world-writable. Note that this would only be a convenience feature for remote systems, as any local user can effectively use uucico to write to such a directory, which means that the directory effectively becomes world writable for local users. 247. In HDB, if you make a hard link from cu to SYS, cu acts as though you ran ``cu SYS'' (presumably you could not use this to call a system named ``cu''). Is this feature worth having? 248. Christopher Ambler: When using the 'i' protocol, if I send a SYNC, and the other system sends a SYNC, but the other system doesn't get my SYNC, then I will proceed but the other system will not. The correct fix is probably to notice that we've never gotten an ACK, and to send another SYNC to encourage the other system to get going. 249. Allow some way to specify which alternate to use when calling another system. uucp-1.07/aclocal.m40000664000076400007640000004623607665532172010000 # aclocal.m4 generated automatically by aclocal 1.5 # Copyright 1996, 1997, 1998, 1999, 2000, 2001 # Free Software Foundation, Inc. # This file 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. # Do all the work for Automake. This macro actually does too much -- # some checks are only needed if your package does certain things. # But this isn't really a big deal. # serial 5 # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # We require 2.13 because we rely on SHELL being computed by configure. AC_PREREQ([2.13]) # AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) # ----------------------------------------------------------- # If MACRO-NAME is provided do IF-PROVIDED, else IF-NOT-PROVIDED. # The purpose of this macro is to provide the user with a means to # check macros which are provided without letting her know how the # information is coded. # If this macro is not defined by Autoconf, define it here. ifdef([AC_PROVIDE_IFELSE], [], [define([AC_PROVIDE_IFELSE], [ifdef([AC_PROVIDE_$1], [$2], [$3])])]) # AM_INIT_AUTOMAKE(PACKAGE,VERSION, [NO-DEFINE]) # ---------------------------------------------- AC_DEFUN([AM_INIT_AUTOMAKE], [AC_REQUIRE([AC_PROG_INSTALL])dnl # test to see if srcdir already configured if test "`CDPATH=:; cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run \"make distclean\" there first]) fi # Define the identity of the package. PACKAGE=$1 AC_SUBST(PACKAGE)dnl VERSION=$2 AC_SUBST(VERSION)dnl ifelse([$3],, [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])]) # Autoconf 2.50 wants to disallow AM_ names. We explicitly allow # the ones we care about. ifdef([m4_pattern_allow], [m4_pattern_allow([^AM_[A-Z]+FLAGS])])dnl # Autoconf 2.50 always computes EXEEXT. However we need to be # compatible with 2.13, for now. So we always define EXEEXT, but we # don't compute it. AC_SUBST(EXEEXT) # Similar for OBJEXT -- only we only use OBJEXT if the user actually # requests that it be used. This is a bit dumb. : ${OBJEXT=o} AC_SUBST(OBJEXT) # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG(ACLOCAL, aclocal) AM_MISSING_PROG(AUTOCONF, autoconf) AM_MISSING_PROG(AUTOMAKE, automake) AM_MISSING_PROG(AUTOHEADER, autoheader) AM_MISSING_PROG(MAKEINFO, makeinfo) AM_MISSING_PROG(AMTAR, tar) AM_PROG_INSTALL_SH AM_PROG_INSTALL_STRIP # We need awk for the "check" target. The system "awk" is bad on # some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_DEP_TRACK])dnl AC_REQUIRE([AM_SET_DEPDIR])dnl AC_PROVIDE_IFELSE([AC_PROG_][CC], [_AM_DEPENDENCIES(CC)], [define([AC_PROG_][CC], defn([AC_PROG_][CC])[_AM_DEPENDENCIES(CC)])])dnl AC_PROVIDE_IFELSE([AC_PROG_][CXX], [_AM_DEPENDENCIES(CXX)], [define([AC_PROG_][CXX], defn([AC_PROG_][CXX])[_AM_DEPENDENCIES(CXX)])])dnl ]) # # Check to make sure that the build environment is sane. # # serial 3 # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Just in case sleep 1 echo timestamp > conftest.file # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t $srcdir/configure conftest.file` fi rm -f conftest.file if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT(yes)]) # serial 2 # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it supports --run. # If it does, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" # Use eval to expand $SHELL if eval "$MISSING --run true"; then am_missing_run="$MISSING --run " else am_missing_run= am_backtick='`' AC_MSG_WARN([${am_backtick}missing' script is too old or missing]) fi ]) # AM_AUX_DIR_EXPAND # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to # `$srcdir', `$srcdir/..', or `$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is `.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [ # expand $ac_aux_dir to an absolute path am_aux_dir=`CDPATH=:; cd $ac_aux_dir && pwd` ]) # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl install_sh=${install_sh-"$am_aux_dir/install-sh"} AC_SUBST(install_sh)]) # One issue with vendor `install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in `make install-strip', and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # serial 4 -*- Autoconf -*- # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # --------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX" or "OBJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl ifelse([$1], CC, [depcc="$CC" am_compiler_list=], [$1], CXX, [depcc="$CXX" am_compiler_list=], [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'] [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named `D' -- because `-MD' means `put the output # in D'. mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi for depmode in $am_compiler_list; do # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. echo '#include "conftest.h"' > conftest.c echo 'int i;' > conftest.h echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf case $depmode in nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; none) break ;; esac # We check with `-c' and `-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle `-M -o', and we need to detect this. if depmode=$depmode \ source=conftest.c object=conftest.o \ depfile=conftest.Po tmpdepfile=conftest.TPo \ $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 && grep conftest.h conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then am_cv_$1_dependencies_compiler_type=$depmode break fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) $1DEPMODE="depmode=$am_cv_$1_dependencies_compiler_type" AC_SUBST([$1DEPMODE]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES AC_DEFUN([AM_SET_DEPDIR], [rm -f .deps 2>/dev/null mkdir .deps 2>/dev/null if test -d .deps; then DEPDIR=.deps else # MS-DOS does not allow filenames that begin with a dot. DEPDIR=_deps fi rmdir .deps 2>/dev/null AC_SUBST(DEPDIR) ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE(dependency-tracking, [ --disable-dependency-tracking Speeds up one-time builds --enable-dependency-tracking Do not reject slow dependency extractors]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) pushdef([subst], defn([AC_SUBST])) subst(AMDEPBACKSLASH) popdef([subst]) ]) # Generate code to set up dependency tracking. # This macro should only be invoked once -- use via AC_REQUIRE. # Usage: # AM_OUTPUT_DEPENDENCY_COMMANDS # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each `.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],[ AC_OUTPUT_COMMANDS([ test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do case "$mf" in Makefile) dirpart=.;; */Makefile) dirpart=`echo "$mf" | sed -e 's|/[^/]*$||'`;; *) continue;; esac grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue # Extract the definition of DEP_FILES from the Makefile without # running `make'. DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` test -z "$DEPDIR" && continue # When using ansi2knr, U may be empty or an underscore; expand it U=`sed -n -e '/^U = / s///p' < "$mf"` test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" # We invoke sed twice because it is the simplest approach to # changing $(DEPDIR) to its actual value in the expansion. for file in `sed -n -e ' /^DEP_FILES = .*\\\\$/ { s/^DEP_FILES = // :loop s/\\\\$// p n /\\\\$/ b loop p } /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`echo "$file" | sed -e 's|/[^/]*$||'` $ac_aux_dir/mkinstalldirs "$dirpart/$fdir" > /dev/null 2>&1 # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done ], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])]) # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' doit: @echo done END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include='#' am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # We grep out `Entering directory' and `Leaving directory' # messages which can occur if `w' ends up in MAKEFLAGS. # In particular we don't look at `^make:' because GNU make might # be invoked under some other name (usually "gmake"), in which # case it prints its new name instead of `make'. if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then am__include=include am__quote= _am_result=GNU fi # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then am__include=.include am__quote='"' _am_result=BSD fi fi AC_SUBST(am__include) AC_SUBST(am__quote) AC_MSG_RESULT($_am_result) rm -f confinc confmf ]) # serial 3 # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. # # FIXME: Once using 2.50, use this: # m4_match([$1], [^TRUE\|FALSE$], [AC_FATAL([$0: invalid condition: $1])])dnl AC_DEFUN([AM_CONDITIONAL], [ifelse([$1], [TRUE], [errprint(__file__:__line__: [$0: invalid condition: $1 ])dnl m4exit(1)])dnl ifelse([$1], [FALSE], [errprint(__file__:__line__: [$0: invalid condition: $1 ])dnl m4exit(1)])dnl AC_SUBST([$1_TRUE]) AC_SUBST([$1_FALSE]) if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi]) # Like AC_CONFIG_HEADER, but automatically create stamp file. # serial 3 # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. We must strip everything past the first ":", # and everything past the last "/". AC_PREREQ([2.12]) AC_DEFUN([AM_CONFIG_HEADER], [ifdef([AC_FOREACH],dnl [dnl init our file count if it isn't already m4_ifndef([_AM_Config_Header_Index], m4_define([_AM_Config_Header_Index], [0])) dnl prepare to store our destination file list for use in config.status AC_FOREACH([_AM_File], [$1], [m4_pushdef([_AM_Dest], m4_patsubst(_AM_File, [:.*])) m4_define([_AM_Config_Header_Index], m4_incr(_AM_Config_Header_Index)) dnl and add it to the list of files AC keeps track of, along dnl with our hook AC_CONFIG_HEADERS(_AM_File, dnl COMMANDS, [, INIT-CMDS] [# update the timestamp echo timestamp >"AS_ESCAPE(_AM_DIRNAME(]_AM_Dest[))/stamp-h]_AM_Config_Header_Index[" ][$2]m4_ifval([$3], [, [$3]]))dnl AC_CONFIG_HEADERS m4_popdef([_AM_Dest])])],dnl [AC_CONFIG_HEADER([$1]) AC_OUTPUT_COMMANDS( ifelse(patsubst([$1], [[^ ]], []), [], [test -z "$CONFIG_HEADERS" || echo timestamp >dnl patsubst([$1], [^\([^:]*/\)?.*], [\1])stamp-h]),dnl [am_indx=1 for am_file in $1; do case " \$CONFIG_HEADERS " in *" \$am_file "*) am_dir=\`echo \$am_file |sed 's%:.*%%;s%[^/]*\$%%'\` if test -n "\$am_dir"; then am_tmpdir=\`echo \$am_dir |sed 's%^\(/*\).*\$%\1%'\` for am_subdir in \`echo \$am_dir |sed 's%/% %'\`; do am_tmpdir=\$am_tmpdir\$am_subdir/ if test ! -d \$am_tmpdir; then mkdir \$am_tmpdir fi done fi echo timestamp > "\$am_dir"stamp-h\$am_indx ;; esac am_indx=\`expr \$am_indx + 1\` done]) ])]) # AM_CONFIG_HEADER # _AM_DIRNAME(PATH) # ----------------- # Like AS_DIRNAME, only do it during macro expansion AC_DEFUN([_AM_DIRNAME], [m4_if(m4_regexp([$1], [^.*[^/]//*[^/][^/]*/*$]), -1, m4_if(m4_regexp([$1], [^//\([^/]\|$\)]), -1, m4_if(m4_regexp([$1], [^/.*]), -1, [.], m4_patsubst([$1], [^\(/\).*], [\1])), m4_patsubst([$1], [^\(//\)\([^/].*\|$\)], [\1])), m4_patsubst([$1], [^\(.*[^/]\)//*[^/][^/]*/*$], [\1]))[]dnl ]) # _AM_DIRNAME # Add --enable-maintainer-mode option to configure. # From Jim Meyering # serial 1 AC_DEFUN([AM_MAINTAINER_MODE], [AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) dnl maintainer-mode is disabled by default AC_ARG_ENABLE(maintainer-mode, [ --enable-maintainer-mode enable make rules and dependencies not useful (and sometimes confusing) to the casual installer], USE_MAINTAINER_MODE=$enableval, USE_MAINTAINER_MODE=no) AC_MSG_RESULT([$USE_MAINTAINER_MODE]) AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes]) MAINT=$MAINTAINER_MODE_TRUE AC_SUBST(MAINT)dnl ] ) uucp-1.07/compile0000775000076400007640000000532607665321754007513 #! /bin/sh # Wrapper for compilers which do not understand `-c -o'. # Copyright 1999, 2000 Free Software Foundation, Inc. # Written by Tom Tromey . # # 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. # 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. # Usage: # compile PROGRAM [ARGS]... # `-o FOO.o' is removed from the args passed to the actual compile. prog=$1 shift ofile= cfile= args= while test $# -gt 0; do case "$1" in -o) # configure might choose to run compile as `compile cc -o foo foo.c'. # So we do something ugly here. ofile=$2 shift case "$ofile" in *.o | *.obj) ;; *) args="$args -o $ofile" ofile= ;; esac ;; *.c) cfile=$1 args="$args $1" ;; *) args="$args $1" ;; esac shift done if test -z "$ofile" || test -z "$cfile"; then # If no `-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # `.c' file was seen then we are probably linking. That is also # ok. exec "$prog" $args fi # Name of file we expect compiler to create. cofile=`echo $cfile | sed -e 's|^.*/||' -e 's/\.c$/.o/'` # Create the lock directory. # Note: use `[/.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo $cofile | sed -e 's|[/.-]|_|g'`.d while true; do if mkdir $lockdir > /dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir $lockdir; exit 1" 1 2 15 # Run the compile. "$prog" $args status=$? if test -f "$cofile"; then mv "$cofile" "$ofile" fi rmdir $lockdir exit $status uucp-1.07/config.h.in0000664000076400007640000002767107665321754010167 /* config.h.in. Generated automatically from configure.in by autoheader. */ /* Define 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 /* Define to empty if the keyword does not work. */ #undef const /* Define if you support file names longer than 14 characters. */ #undef HAVE_LONG_FILE_NAMES /* Define if system calls automatically restart after interruption by a signal. */ #undef HAVE_RESTARTABLE_SYSCALLS /* Define if major, minor, and makedev are declared in . */ #undef MAJOR_IN_MKDEV /* Define if major, minor, and makedev are declared in . */ #undef MAJOR_IN_SYSMACROS /* Define if on MINIX. */ #undef _MINIX /* Define if the system does not provide POSIX.1 features except with this defined. */ #undef _POSIX_1_SOURCE /* Define if you need to in order for stat and other things to work. */ #undef _POSIX_SOURCE /* Define as the return type of signal handlers (int or void). */ #undef RETSIGTYPE /* Define if the `S_IS*' macros in do not work properly. */ #undef STAT_MACROS_BROKEN /* Define if you can safely include both and . */ #undef TIME_WITH_SYS_TIME /* Define if your declares struct tm. */ #undef TM_IN_SYS_TIME /* Define if you have the bcmp function. */ #undef HAVE_BCMP /* Define if you have the bcopy function. */ #undef HAVE_BCOPY /* Define if you have the bsearch function. */ #undef HAVE_BSEARCH /* Define if you have the bzero function. */ #undef HAVE_BZERO /* Define if you have the dev_info function. */ #undef HAVE_DEV_INFO /* Define if you have the disk_space function. */ #undef HAVE_DISK_SPACE /* Define if you have the dup2 function. */ #undef HAVE_DUP2 /* Define if you have the ftruncate function. */ #undef HAVE_FTRUNCATE /* Define if you have the ftw function. */ #undef HAVE_FTW /* Define if you have the getaddrinfo function. */ #undef HAVE_GETADDRINFO /* Define if you have the getcwd function. */ #undef HAVE_GETCWD /* Define if you have the getdtablesize function. */ #undef HAVE_GETDTABLESIZE /* Define if you have the getgrent function. */ #undef HAVE_GETGRENT /* Define if you have the gethostname function. */ #undef HAVE_GETHOSTNAME /* Define if you have the gettimeofday function. */ #undef HAVE_GETTIMEOFDAY /* Define if you have the getwd function. */ #undef HAVE_GETWD /* Define if you have the glob function. */ #undef HAVE_GLOB /* Define if you have the index function. */ #undef HAVE_INDEX /* Define if you have the ltrunc function. */ #undef HAVE_LTRUNC /* Define if you have the memchr function. */ #undef HAVE_MEMCHR /* Define if you have the mkdir function. */ #undef HAVE_MKDIR /* Define if you have the nap function. */ #undef HAVE_NAP /* Define if you have the napms function. */ #undef HAVE_NAPMS /* Define if you have the opendir function. */ #undef HAVE_OPENDIR /* Define if you have the poll function. */ #undef HAVE_POLL /* Define if you have the remove function. */ #undef HAVE_REMOVE /* Define if you have the rename function. */ #undef HAVE_RENAME /* Define if you have the rindex function. */ #undef HAVE_RINDEX /* Define if you have the rmdir function. */ #undef HAVE_RMDIR /* Define if you have the select function. */ #undef HAVE_SELECT /* Define if you have the seteuid function. */ #undef HAVE_SETEUID /* Define if you have the setpgrp function. */ #undef HAVE_SETPGRP /* Define if you have the setret function. */ #undef HAVE_SETRET /* Define if you have the setreuid function. */ #undef HAVE_SETREUID /* Define if you have the setsid function. */ #undef HAVE_SETSID /* Define if you have the sigaction function. */ #undef HAVE_SIGACTION /* Define if you have the sigblock function. */ #undef HAVE_SIGBLOCK /* Define if you have the sighold function. */ #undef HAVE_SIGHOLD /* Define if you have the sigprocmask function. */ #undef HAVE_SIGPROCMASK /* Define if you have the sigset function. */ #undef HAVE_SIGSET /* Define if you have the sigsetjmp function. */ #undef HAVE_SIGSETJMP /* Define if you have the sigvec function. */ #undef HAVE_SIGVEC /* Define if you have the socket function. */ #undef HAVE_SOCKET /* Define if you have the statvfs function. */ #undef HAVE_STATVFS /* Define if you have the strcasecmp function. */ #undef HAVE_STRCASECMP /* Define if you have the strchr function. */ #undef HAVE_STRCHR /* Define if you have the strdup function. */ #undef HAVE_STRDUP /* Define if you have the strerror function. */ #undef HAVE_STRERROR /* Define if you have the stricmp function. */ #undef HAVE_STRICMP /* Define if you have the strncasecmp function. */ #undef HAVE_STRNCASECMP /* Define if you have the strnicmp function. */ #undef HAVE_STRNICMP /* Define if you have the strrchr function. */ #undef HAVE_STRRCHR /* Define if you have the strstr function. */ #undef HAVE_STRSTR /* Define if you have the strtol function. */ #undef HAVE_STRTOL /* Define if you have the strtoul function. */ #undef HAVE_STRTOUL /* Define if you have the sysconf function. */ #undef HAVE_SYSCONF /* Define if you have the t_open function. */ #undef HAVE_T_OPEN /* Define if you have the times function. */ #undef HAVE_TIMES /* Define if you have the uname function. */ #undef HAVE_UNAME /* Define if you have the usleep function. */ #undef HAVE_USLEEP /* Define if you have the ustat function. */ #undef HAVE_USTAT /* Define if you have the vfprintf function. */ #undef HAVE_VFPRINTF /* Define if you have the wait4 function. */ #undef HAVE_WAIT4 /* Define if you have the waitpid function. */ #undef HAVE_WAITPID /* Define if you have the header file. */ #undef HAVE_FCNTL_H /* Define if you have the header file. */ #undef HAVE_FTW_H /* Define if you have the header file. */ #undef HAVE_GLOB_H /* Define if you have the header file. */ #undef HAVE_LIBC_H /* Define if you have the header file. */ #undef HAVE_LIMITS_H /* Define if you have the header file. */ #undef HAVE_MEMORY_H /* Define if you have the header file. */ #undef HAVE_POLL_H /* Define if you have the header file. */ #undef HAVE_STDARG_H /* Define if you have the header file. */ #undef HAVE_STDDEF_H /* Define if you have the header file. */ #undef HAVE_STDLIB_H /* Define if you have the header file. */ #undef HAVE_STRING_H /* Define if you have the header file. */ #undef HAVE_STRINGS_H /* Define if you have the header file. */ #undef HAVE_STROPTS_H /* Define if you have the header file. */ #undef HAVE_SYS_DUSTAT_H /* Define if you have the header file. */ #undef HAVE_SYS_FILE_H /* Define if you have the header file. */ #undef HAVE_SYS_FILSYS_H /* Define if you have the header file. */ #undef HAVE_SYS_FS_TYPES_H /* Define if you have the header file. */ #undef HAVE_SYS_IOCTL_H /* Define if you have the header file. */ #undef HAVE_SYS_MOUNT_H /* Define if you have the header file. */ #undef HAVE_SYS_PARAM_H /* Define if you have the header file. */ #undef HAVE_SYS_STATFS_H /* Define if you have the header file. */ #undef HAVE_SYS_STATVFS_H /* Define if you have the header file. */ #undef HAVE_SYS_TERMIOX_H /* Define if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define if you have the header file. */ #undef HAVE_SYS_TIMES_H /* Define if you have the header file. */ #undef HAVE_SYS_TLI_H /* Define if you have the header file. */ #undef HAVE_SYS_TYPES_TCP_H /* Define if you have the header file. */ #undef HAVE_SYS_VFS_H /* Define if you have the header file. */ #undef HAVE_SYS_WAIT_H /* Define if you have the header file. */ #undef HAVE_SYSEXITS_H /* Define if you have the header file. */ #undef HAVE_TERMIOS_H /* Define if you have the header file. */ #undef HAVE_TIME_H /* Define if you have the header file. */ #undef HAVE_TIUSER_H /* Define if you have the header file. */ #undef HAVE_UNISTD_H /* Define if you have the header file. */ #undef HAVE_USTAT_H /* Define if you have the header file. */ #undef HAVE_XTI_H /* Define if you have the nsl library (-lnsl). */ #undef HAVE_LIBNSL /* Define if you have the nsl_s library (-lnsl_s). */ #undef HAVE_LIBNSL_S /* Define if you have the socket library (-lsocket). */ #undef HAVE_LIBSOCKET /* Define if you have the sun library (-lsun). */ #undef HAVE_LIBSUN /* Define if you have the xti library (-lxti). */ #undef HAVE_LIBXTI /* Name of package */ #undef PACKAGE /* Version number of package */ #undef VERSION /* user ID for programs; normally uucp */ #undef OWNER /* Whether the compiler supports prototypes */ #undef HAVE_PROTOTYPES /* echo program--if shell builtin, use echo */ #undef ECHO_PROGRAM /* Whether you have a which defines struct direct */ #undef HAVE_DIRENT_H /* Whether you have a which defines struct utimbuf */ #undef HAVE_UTIME_H /* Whether you have */ #undef HAVE_SYS_SELECT_H /* Whether and may both be included */ #undef HAVE_TERMIOS_AND_SYS_IOCTL_H /* Whether CBREAK is defined */ #undef HAVE_CBREAK /* Type to use for pid_t if not defined--typically int */ #undef PID_T /* Type to use for uid_t if not defined--typically int */ #undef UID_T /* Type to use for gid_t if not defined--typically int */ #undef GID_T /* Type to use for off_t if not defined--typically long */ #undef OFF_T /* Whether sig_atomic_t is defined in */ #undef HAVE_SIG_ATOMIC_T_IN_SIGNAL_H /* Whether sig_atomic_t is defined in */ #undef HAVE_SIG_ATOMIC_T_IN_TYPES_H /* Whether size_t is defined in */ #undef HAVE_SIZE_T_IN_STDDEF_H /* Whether size_t is defined in */ #undef HAVE_SIZE_T_IN_TYPES_H /* Whether time_t is defined in */ #undef HAVE_TIME_T_IN_TIME_H /* Whether time_t is defined in */ #undef HAVE_TIME_T_IN_TYPES_H /* Whether the compiler supports void */ #undef HAVE_VOID /* Whether the compiler supports unsigned char */ #undef HAVE_UNSIGNED_CHAR /* Whether errno is declared in */ #undef HAVE_ERRNO_DECLARATION /* Whether TXADDCD is defined */ #undef HAVE_TXADDCD /* Whether you have memset */ #undef HAVE_MEMSET /* Whether you have memcmp */ #undef HAVE_MEMCMP /* Whether you have memcpy */ #undef HAVE_MEMCPY /* Define if you have the getline function. */ #undef HAVE_GETLINE /* Whether you have ftime */ #undef HAVE_FTIME /* Program to print working directory */ #undef PWD_PROGRAM /* Program to make a directory */ #undef MKDIR_PROGRAM /* Program to remove a directory */ #undef RMDIR_PROGRAM /* Whether struct sigvec has sv_flags member */ #undef HAVE_SIGVEC_SV_FLAGS /* Whether you have statvfs */ #undef STAT_STATVFS /* Whether you have 3 argument statfs */ #undef STAT_STATFS3_OSF1 /* Whether you have two argument statfs with bsize */ #undef STAT_STATFS2_BSIZE /* Whether you have four argument statfs */ #undef STAT_STATFS4 /* Whether you have two argument statfs with fsize */ #undef STAT_STATFS2_FSIZE /* Whether you have two argument statfs with fd_req */ #undef STAT_STATFS2_FS_DATA /* Whether you have ustat */ #undef STAT_USTAT /* Whether you have disk_space */ #undef STAT_DISK_SPACE /* Whether times is declared as long */ #undef TIMES_DECLARATION_OK /* Whether getpwnam is declared as struct passwd * */ #undef GETPWNAM_DECLARATION_OK /* Whether getpwuid is declared as struct passwd * */ #undef GETPWUID_DECLARATION_OK /* Whether getgrent is declared as struct group * */ #undef GETGRENT_DECLARATION_OK /* Whether you have BSD style setpgrp */ #undef HAVE_BSD_PGRP /* Whether you have union wait */ #undef HAVE_UNION_WAIT /* Whether you have struct sockaddr_storage */ #undef HAVE_STRUCT_SOCKADDR_STORAGE uucp-1.07/configure0000775000076400007640000051346707665532174010056 #! /bin/sh # From configure.in Id: configure.in,v 1.99 2002/03/07 17:56:41 ian Rel # aclocal.m4 generated automatically by aclocal 1.5 # Copyright 1996, 1997, 1998, 1999, 2000, 2001 # Free Software Foundation, Inc. # This file 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. # Do all the work for Automake. This macro actually does too much -- # some checks are only needed if your package does certain things. # But this isn't really a big deal. # serial 5 # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # We require 2.13 because we rely on SHELL being computed by configure. # AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) # ----------------------------------------------------------- # If MACRO-NAME is provided do IF-PROVIDED, else IF-NOT-PROVIDED. # The purpose of this macro is to provide the user with a means to # check macros which are provided without letting her know how the # information is coded. # If this macro is not defined by Autoconf, define it here. # AM_INIT_AUTOMAKE(PACKAGE,VERSION, [NO-DEFINE]) # ---------------------------------------------- # # Check to make sure that the build environment is sane. # # serial 3 # AM_SANITY_CHECK # --------------- # serial 2 # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it supports --run. # If it does, set am_missing_run to use it, otherwise, to nothing. # AM_AUX_DIR_EXPAND # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to # `$srcdir', `$srcdir/..', or `$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is `.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. # One issue with vendor `install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in `make install-strip', and initialize # STRIPPROG with the value of the STRIP variable (set by the user). # serial 4 -*- Autoconf -*- # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # --------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX" or "OBJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES # AM_DEP_TRACK # ------------ # Generate code to set up dependency tracking. # This macro should only be invoked once -- use via AC_REQUIRE. # Usage: # AM_OUTPUT_DEPENDENCY_COMMANDS # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each `.P' file that we will # need in order to bootstrap the dependency handling code. # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. # serial 3 # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. # # FIXME: Once using 2.50, use this: # m4_match([$1], [^TRUE\|FALSE$], [AC_FATAL([$0: invalid condition: $1])])dnl # Like AC_CONFIG_HEADER, but automatically create stamp file. # serial 3 # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. We must strip everything past the first ":", # and everything past the last "/". # AM_CONFIG_HEADER # _AM_DIRNAME(PATH) # ----------------- # Like AS_DIRNAME, only do it during macro expansion # _AM_DIRNAME # Add --enable-maintainer-mode option to configure. # From Jim Meyering # serial 1 # Guess values for system-dependent variables and create Makefiles. # Generated automatically using autoconf version 2.13 # Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # Defaults: ac_help= ac_default_prefix=/usr/local # Any additions from configure.in: ac_help="$ac_help --disable-dependency-tracking Speeds up one-time builds --enable-dependency-tracking Do not reject slow dependency extractors" ac_help="$ac_help --enable-maintainer-mode enable make rules and dependencies not useful (and sometimes confusing) to the casual installer" ac_help="$ac_help --with-user=USERNAME user ID for programs; default uucp" ac_help="$ac_help --with-newconfigdir=DIRNAME new config file directory; default PREFIX/conf/uucp" ac_help="$ac_help --with-oldconfigdir=DIRNAME old config file directory; default /usr/lib/uucp" ac_help="$ac_help --enable-build-warnings Enable build-time compiler warnings if gcc is used" # Initialize some variables set by options. # The variables have the same names as the options, with # dashes changed to underlines. build=NONE cache_file=./config.cache exec_prefix=NONE host=NONE no_create= nonopt=NONE no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= target=NONE verbose= x_includes=NONE x_libraries=NONE bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' # Initialize some other variables. subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Maximum number of lines to put in a shell here document. ac_max_here_lines=12 ac_prev= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi case "$ac_option" in -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) ac_optarg= ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case "$ac_option" in -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir="$ac_optarg" ;; -build | --build | --buil | --bui | --bu) ac_prev=build ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build="$ac_optarg" ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file="$ac_optarg" ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir="$ac_optarg" ;; -disable-* | --disable-*) ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } fi ac_feature=`echo $ac_feature| sed 's/-/_/g'` eval "enable_${ac_feature}=no" ;; -enable-* | --enable-*) ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } fi ac_feature=`echo $ac_feature| sed 's/-/_/g'` case "$ac_option" in *=*) ;; *) ac_optarg=yes ;; esac eval "enable_${ac_feature}='$ac_optarg'" ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix="$ac_optarg" ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he) # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat << EOF Usage: configure [options] [host] Options: [defaults in brackets after descriptions] Configuration: --cache-file=FILE cache test results in FILE --help print this message --no-create do not create output files --quiet, --silent do not print \`checking...' messages --version print the version of autoconf that created configure Directory and file names: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [same as prefix] --bindir=DIR user executables in DIR [EPREFIX/bin] --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] --libexecdir=DIR program executables in DIR [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data in DIR [PREFIX/share] --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data in DIR [PREFIX/com] --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] --libdir=DIR object code libraries in DIR [EPREFIX/lib] --includedir=DIR C header files in DIR [PREFIX/include] --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] --infodir=DIR info documentation in DIR [PREFIX/info] --mandir=DIR man documentation in DIR [PREFIX/man] --srcdir=DIR find the sources in DIR [configure dir or ..] --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names EOF cat << EOF Host type: --build=BUILD configure for building on BUILD [BUILD=HOST] --host=HOST configure for HOST [guessed] --target=TARGET configure for TARGET [TARGET=HOST] Features and packages: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --x-includes=DIR X include files are in DIR --x-libraries=DIR X library files are in DIR EOF if test -n "$ac_help"; then echo "--enable and --with options recognized:$ac_help" fi exit 0 ;; -host | --host | --hos | --ho) ac_prev=host ;; -host=* | --host=* | --hos=* | --ho=*) host="$ac_optarg" ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir="$ac_optarg" ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir="$ac_optarg" ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir="$ac_optarg" ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir="$ac_optarg" ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) localstatedir="$ac_optarg" ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir="$ac_optarg" ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir="$ac_optarg" ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix="$ac_optarg" ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix="$ac_optarg" ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix="$ac_optarg" ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name="$ac_optarg" ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir="$ac_optarg" ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir="$ac_optarg" ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site="$ac_optarg" ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir="$ac_optarg" ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir="$ac_optarg" ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target="$ac_optarg" ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers) echo "configure generated by autoconf version 2.13" exit 0 ;; -with-* | --with-*) ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } fi ac_package=`echo $ac_package| sed 's/-/_/g'` case "$ac_option" in *=*) ;; *) ac_optarg=yes ;; esac eval "with_${ac_package}='$ac_optarg'" ;; -without-* | --without-*) ac_package=`echo $ac_option|sed -e 's/-*without-//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } fi ac_package=`echo $ac_package| sed 's/-/_/g'` eval "with_${ac_package}=no" ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes="$ac_optarg" ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries="$ac_optarg" ;; -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } ;; *) if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then echo "configure: warning: $ac_option: invalid host type" 1>&2 fi if test "x$nonopt" != xNONE; then { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } fi nonopt="$ac_option" ;; esac done if test -n "$ac_prev"; then { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } fi trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 # File descriptor usage: # 0 standard input # 1 file creation # 2 errors and warnings # 3 some systems may open it to /dev/tty # 4 used on the Kubota Titan # 6 checking for... messages and results # 5 compiler messages saved in config.log if test "$silent" = yes; then exec 6>/dev/null else exec 6>&1 fi exec 5>./config.log echo "\ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. " 1>&5 # Strip out --no-create and --no-recursion so they do not pile up. # Also quote any args containing shell metacharacters. ac_configure_args= for ac_arg do case "$ac_arg" in -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c) ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) ac_configure_args="$ac_configure_args '$ac_arg'" ;; *) ac_configure_args="$ac_configure_args $ac_arg" ;; esac done # NLS nuisances. # Only set these to C if already set. These must not be set unconditionally # because not all systems understand e.g. LANG=C (notably SCO). # Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! # Non-C LC_CTYPE values break the ctype check. if test "${LANG+set}" = set; then LANG=C; export LANG; fi if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo > confdefs.h # A filename unique to this package, relative to the directory that # configure is in, which we can look for to find out if srcdir is correct. ac_unique_file=policy.h # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then its parent. ac_prog=$0 ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. srcdir=$ac_confdir if test ! -r $srcdir/$ac_unique_file; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r $srcdir/$ac_unique_file; then if test "$ac_srcdir_defaulted" = yes; then { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } else { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } fi fi srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then echo "loading site script $ac_site_file" . "$ac_site_file" fi done if test -r "$cache_file"; then echo "loading cache $cache_file" . $cache_file else echo "creating cache $cache_file" > $cache_file fi ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross ac_exeext= ac_objext=o if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then ac_n= ac_c=' ' ac_t=' ' else ac_n=-n ac_c= ac_t= fi else ac_n= ac_c='\c' ac_t= fi ac_aux_dir= for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do if test -f $ac_dir/install-sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f $ac_dir/install.sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break fi done if test -z "$ac_aux_dir"; then { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } fi ac_config_guess=$ac_aux_dir/config.guess ac_config_sub=$ac_aux_dir/config.sub ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. # expand $ac_aux_dir to an absolute path am_aux_dir=`CDPATH=:; cd $ac_aux_dir && pwd` # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 echo "configure:785: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" for ac_dir in $PATH; do # Account for people who put trailing slashes in PATH elements. case "$ac_dir/" in /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do if test -f $ac_dir/$ac_prog; then if test $ac_prog = install && grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : else ac_cv_path_install="$ac_dir/$ac_prog -c" break 2 fi fi done ;; esac done IFS="$ac_save_IFS" fi if test "${ac_cv_path_install+set}" = set; then INSTALL="$ac_cv_path_install" else # As a last resort, use the slow shell script. We don't cache a # path for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the path is relative. INSTALL="$ac_install_sh" fi fi echo "$ac_t""$INSTALL" 1>&6 # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' echo $ac_n "checking whether build environment is sane""... $ac_c" 1>&6 echo "configure:838: checking whether build environment is sane" >&5 # Just in case sleep 1 echo timestamp > conftest.file # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t $srcdir/configure conftest.file` fi rm -f conftest.file if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". { echo "configure: error: ls -t appears to fail. Make sure there is not a broken alias in your environment" 1>&2; exit 1; } fi test "$2" = conftest.file ) then # Ok. : else { echo "configure: error: newly created file is older than distributed files! Check your system clock" 1>&2; exit 1; } fi echo "$ac_t""yes" 1>&6 if test "$program_transform_name" = s,x,x,; then program_transform_name= else # Double any \ or $. echo might interpret backslashes. cat <<\EOF_SED > conftestsed s,\\,\\\\,g; s,\$,$$,g EOF_SED program_transform_name="`echo $program_transform_name|sed -f conftestsed`" rm -f conftestsed fi test "$program_prefix" != NONE && program_transform_name="s,^,${program_prefix},; $program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" # sed with no file args requires a program. test "$program_transform_name" = "" && program_transform_name="s,x,x," test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" # Use eval to expand $SHELL if eval "$MISSING --run true"; then am_missing_run="$MISSING --run " else am_missing_run= am_backtick='`' echo "configure: warning: ${am_backtick}missing' script is too old or missing" 1>&2 fi for ac_prog in mawk gawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:909: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_AWK="$ac_prog" break fi done IFS="$ac_save_ifs" fi fi AWK="$ac_cv_prog_AWK" if test -n "$AWK"; then echo "$ac_t""$AWK" 1>&6 else echo "$ac_t""no" 1>&6 fi test -n "$AWK" && break done echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 echo "configure:939: checking whether ${MAKE-make} sets \${MAKE}" >&5 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftestmake <<\EOF all: @echo 'ac_maketemp="${MAKE}"' EOF # GNU make sometimes prints "make[1]: Entering...", which would confuse us. eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` if test -n "$ac_maketemp"; then eval ac_cv_prog_make_${ac_make}_set=yes else eval ac_cv_prog_make_${ac_make}_set=no fi rm -f conftestmake fi if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then echo "$ac_t""yes" 1>&6 SET_MAKE= else echo "$ac_t""no" 1>&6 SET_MAKE="MAKE=${MAKE-make}" fi # Check whether --enable-dependency-tracking or --disable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then enableval="$enable_dependency_tracking" : fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi rm -f .deps 2>/dev/null mkdir .deps 2>/dev/null if test -d .deps; then DEPDIR=.deps else # MS-DOS does not allow filenames that begin with a dot. DEPDIR=_deps fi rmdir .deps 2>/dev/null # test to see if srcdir already configured if test "`CDPATH=:; cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then { echo "configure: error: source directory already configured; run \"make distclean\" there first" 1>&2; exit 1; } fi # Define the identity of the package. PACKAGE=uucp VERSION=1.07 cat >> confdefs.h <> confdefs.h <&6 echo "configure:1059: checking whether to enable maintainer-specific portions of Makefiles" >&5 # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then enableval="$enable_maintainer_mode" USE_MAINTAINER_MODE=$enableval else USE_MAINTAINER_MODE=no fi echo "$ac_t""$USE_MAINTAINER_MODE" 1>&6 if test $USE_MAINTAINER_MODE = yes; then MAINTAINER_MODE_TRUE= MAINTAINER_MODE_FALSE='#' else MAINTAINER_MODE_TRUE='#' MAINTAINER_MODE_FALSE= fi MAINT=$MAINTAINER_MODE_TRUE # Check whether --with-user or --without-user was given. if test "${with_user+set}" = set; then withval="$with_user" OWNER=${withval} else OWNER="uucp" fi cat >> confdefs.h <&6 # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:1117: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_GCC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else case "$GCC" in /*) ac_cv_path_GCC="$GCC" # Let the user override the test with a path. ;; ?:/*) ac_cv_path_GCC="$GCC" # Let the user override the test with a dos path. ;; *) IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_path_GCC="$ac_dir/$ac_word" break fi done IFS="$ac_save_ifs" ;; esac fi GCC="$ac_cv_path_GCC" if test -n "$GCC"; then echo "$ac_t""$GCC" 1>&6 else echo "$ac_t""no" 1>&6 fi if test -n "$ac_cv_path_GCC"; then prefix=`echo $ac_cv_path_GCC|sed 's%/[^/][^/]*//*[^/][^/]*$%%'` fi fi # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:1157: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_CC="gcc" break fi done IFS="$ac_save_ifs" fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:1187: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_prog_rejected=no ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" break fi done IFS="$ac_save_ifs" if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# -gt 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift set dummy "$ac_dir/$ac_word" "$@" shift ac_cv_prog_CC="$@" fi fi fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 fi if test -z "$CC"; then case "`uname -s`" in *win32* | *WIN32*) # Extract the first word of "cl", so it can be a program name with args. set dummy cl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:1238: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_CC="cl" break fi done IFS="$ac_save_ifs" fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 fi ;; esac fi test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 echo "configure:1270: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext << EOF #line 1281 "configure" #include "confdefs.h" main(){return(0);} EOF if { (eval echo configure:1286: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then ac_cv_prog_cc_cross=no else ac_cv_prog_cc_cross=yes fi else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 ac_cv_prog_cc_works=no fi rm -fr conftest* ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 echo "configure:1312: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 echo "configure:1317: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no fi fi echo "$ac_t""$ac_cv_prog_gcc" 1>&6 if test $ac_cv_prog_gcc = yes; then GCC=yes else GCC= fi ac_test_CFLAGS="${CFLAGS+set}" ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 echo "configure:1345: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else echo 'void f(){}' > conftest.c if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then ac_cv_prog_cc_g=yes else ac_cv_prog_cc_g=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 if test "$ac_test_CFLAGS" = set; then CFLAGS="$ac_save_CFLAGS" elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi am_make=${MAKE-make} cat > confinc << 'END' doit: @echo done END # If we don't find an include directive, just comment out the code. echo $ac_n "checking for style of include used by $am_make""... $ac_c" 1>&6 echo "configure:1384: checking for style of include used by $am_make" >&5 am__include='#' am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # We grep out `Entering directory' and `Leaving directory' # messages which can occur if `w' ends up in MAKEFLAGS. # In particular we don't look at `^make:' because GNU make might # be invoked under some other name (usually "gmake"), in which # case it prints its new name instead of `make'. if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then am__include=include am__quote= _am_result=GNU fi # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then am__include=.include am__quote='"' _am_result=BSD fi fi echo "$ac_t""$_am_result" 1>&6 rm -f confinc confmf depcc="$CC" am_compiler_list= echo $ac_n "checking dependency style of $depcc""... $ac_c" 1>&6 echo "configure:1418: checking dependency style of $depcc" >&5 if eval "test \"`echo '$''{'am_cv_CC_dependencies_compiler_type'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named `D' -- because `-MD' means `put the output # in D'. mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi for depmode in $am_compiler_list; do # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. echo '#include "conftest.h"' > conftest.c echo 'int i;' > conftest.h echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf case $depmode in nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; none) break ;; esac # We check with `-c' and `-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle `-M -o', and we need to detect this. if depmode=$depmode \ source=conftest.c object=conftest.o \ depfile=conftest.Po tmpdepfile=conftest.TPo \ $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 && grep conftest.h conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then am_cv_CC_dependencies_compiler_type=$depmode break fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi echo "$ac_t""$am_cv_CC_dependencies_compiler_type" 1>&6 CCDEPMODE="depmode=$am_cv_CC_dependencies_compiler_type" echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 echo "configure:1485: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else # This must be in double quotes, not single quotes, because CPP may get # substituted into the Makefile and "${CC-cc}" will confuse make. CPP="${CC-cc} -E" # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1506: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1523: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP="${CC-cc} -nologo -E" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1540: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP=/lib/cpp fi rm -f conftest* fi rm -f conftest* fi rm -f conftest* ac_cv_prog_CPP="$CPP" fi CPP="$ac_cv_prog_CPP" else ac_cv_prog_CPP="$CPP" fi echo "$ac_t""$CPP" 1>&6 if test $ac_cv_prog_gcc = yes; then echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 echo "configure:1566: checking whether ${CC-cc} needs -traditional" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_pattern="Autoconf.*'x'" cat > conftest.$ac_ext < Autoconf TIOCGETP EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "$ac_pattern" >/dev/null 2>&1; then rm -rf conftest* ac_cv_prog_gcc_traditional=yes else rm -rf conftest* ac_cv_prog_gcc_traditional=no fi rm -f conftest* if test $ac_cv_prog_gcc_traditional = no; then cat > conftest.$ac_ext < Autoconf TCGETA EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "$ac_pattern" >/dev/null 2>&1; then rm -rf conftest* ac_cv_prog_gcc_traditional=yes fi rm -f conftest* fi fi echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 if test $ac_cv_prog_gcc_traditional = yes; then CC="$CC -traditional" fi fi build_warnings="-W -Wall -Wstrict-prototypes -Wmissing-prototypes" # Check whether --enable-build-warnings or --disable-build-warnings was given. if test "${enable_build_warnings+set}" = set; then enableval="$enable_build_warnings" case "${enableval}" in yes) ;; no) build_warnings="-w";; ,*) t=`echo "${enableval}" | sed -e "s/,/ /g"` build_warnings="${build_warnings} ${t}";; *,) t=`echo "${enableval}" | sed -e "s/,/ /g"` build_warnings="${t} ${build_warnings}";; *) build_warnings=`echo "${enableval}" | sed -e "s/,/ /g"`;; esac if test x"$silent" != x"yes" && test x"$build_warnings" != x""; then echo "Setting warning flags = $build_warnings" 6>&1 fi fi WARN_CFLAGS="" if test "x${build_warnings}" != x -a "x$GCC" = xyes ; then WARN_CFLAGS="${build_warnings}" fi AR=${AR-ar} # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:1638: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_RANLIB="ranlib" break fi done IFS="$ac_save_ifs" test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" fi fi RANLIB="$ac_cv_prog_RANLIB" if test -n "$RANLIB"; then echo "$ac_t""$RANLIB" 1>&6 else echo "$ac_t""no" 1>&6 fi echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&6 echo "configure:1666: checking for POSIXized ISC" >&5 if test -d /etc/conf/kconfig.d && grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1 then echo "$ac_t""yes" 1>&6 ISC=yes # If later tests want to check for ISC. cat >> confdefs.h <<\EOF #define _POSIX_SOURCE 1 EOF if test "$GCC" = yes; then CC="$CC -posix" else CC="$CC -Xp" fi else echo "$ac_t""no" 1>&6 ISC= fi ac_safe=`echo "minix/config.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for minix/config.h""... $ac_c" 1>&6 echo "configure:1688: checking for minix/config.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1698: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 MINIX=yes else echo "$ac_t""no" 1>&6 MINIX= fi if test "$MINIX" = yes; then cat >> confdefs.h <<\EOF #define _POSIX_SOURCE 1 EOF cat >> confdefs.h <<\EOF #define _POSIX_1_SOURCE 2 EOF cat >> confdefs.h <<\EOF #define _MINIX 1 EOF fi echo $ac_n "checking for AIX""... $ac_c" 1>&6 echo "configure:1736: checking for AIX" >&5 cat > conftest.$ac_ext <&5 | egrep "yes" >/dev/null 2>&1; then rm -rf conftest* echo "$ac_t""yes" 1>&6; cat >> confdefs.h <<\EOF #define _ALL_SOURCE 1 EOF else rm -rf conftest* echo "$ac_t""no" 1>&6 fi rm -f conftest* echo $ac_n "checking for getpwnam in -lsun""... $ac_c" 1>&6 echo "configure:1760: checking for getpwnam in -lsun" >&5 ac_lib_var=`echo sun'_'getpwnam | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lsun $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_lib=HAVE_LIB`echo sun | sed -e 's/[^a-zA-Z0-9_]/_/g' \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` cat >> confdefs.h <&6 fi echo $ac_n "checking for working const""... $ac_c" 1>&6 echo "configure:1807: checking for working const" >&5 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; } ; return 0; } EOF if { (eval echo configure:1861: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_const=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_c_const=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_c_const" 1>&6 if test $ac_cv_c_const = no; then cat >> confdefs.h <<\EOF #define const EOF fi echo $ac_n "checking for prototypes""... $ac_c" 1>&6 echo "configure:1882: checking for prototypes" >&5 if eval "test \"`echo '$''{'uucp_cv_c_prototypes'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_c_prototypes=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_c_prototypes=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_c_prototypes" 1>&6 if test $uucp_cv_c_prototypes = yes; then cat >> confdefs.h <<\EOF #define HAVE_PROTOTYPES 1 EOF fi # Pull the hash mark out of the macro call to avoid m4 problems. ac_msg="whether #! works in shell scripts" echo $ac_n "checking $ac_msg""... $ac_c" 1>&6 echo "configure:1917: checking $ac_msg" >&5 if eval "test \"`echo '$''{'ac_cv_sys_interpreter'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else echo '#! /bin/cat exit 69 ' > conftest chmod u+x conftest (SHELL=/bin/sh; export SHELL; ./conftest >/dev/null) if test $? -ne 69; then ac_cv_sys_interpreter=yes else ac_cv_sys_interpreter=no fi rm -f conftest fi echo "$ac_t""$ac_cv_sys_interpreter" 1>&6 interpval="$ac_cv_sys_interpreter" POUNDBANG=$ac_cv_sys_interpreter echo $ac_n "checking for echo""... $ac_c" 1>&6 echo "configure:1940: checking for echo" >&5 if (PATH= echo test) 2>/dev/null | grep test >/dev/null 2>&1; then echo_program='"echo"' echo "$ac_t""shell builtin" 1>&6 elif test -s /bin/echo; then echo_program="/bin/echo" echo "$ac_t""/bin/echo" 1>&6 else echo "$ac_t""not found" 1>&6 fi cat >> confdefs.h <&6 echo "configure:1955: checking whether ln -s works" >&5 if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else rm -f conftestdata if ln -s X conftestdata 2>/dev/null then rm -f conftestdata ac_cv_prog_LN_S="ln -s" else ac_cv_prog_LN_S=ln fi fi LN_S="$ac_cv_prog_LN_S" if test "$ac_cv_prog_LN_S" = "ln -s"; then echo "$ac_t""yes" 1>&6 else echo "$ac_t""no" 1>&6 fi for ac_hdr in stddef.h stdarg.h string.h strings.h unistd.h stdlib.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 echo "configure:1979: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1989: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done for ac_hdr in limits.h time.h sys/wait.h sys/ioctl.h memory.h termios.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 echo "configure:2019: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2029: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done for ac_hdr in fcntl.h sys/file.h sys/time.h sys/times.h libc.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 echo "configure:2059: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2069: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done for ac_hdr in sysexits.h poll.h tiuser.h xti.h sys/tli.h stropts.h ftw.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 echo "configure:2099: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2109: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done for ac_hdr in glob.h sys/param.h sys/types.tcp.h sys/mount.h sys/vfs.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 echo "configure:2139: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2149: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done for ac_hdr in sys/filsys.h sys/statfs.h sys/dustat.h sys/fs_types.h ustat.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 echo "configure:2179: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2189: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done for ac_hdr in sys/statvfs.h sys/termiox.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 echo "configure:2219: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2229: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done # Under Next 3.2 apparently does not define struct dirent # by default. echo $ac_n "checking for dirent.h""... $ac_c" 1>&6 echo "configure:2258: checking for dirent.h" >&5 if eval "test \"`echo '$''{'uucp_cv_header_dirent_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { struct dirent s; ; return 0; } EOF if { (eval echo configure:2270: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_header_dirent_h=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_header_dirent_h=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_header_dirent_h" 1>&6 if test $uucp_cv_header_dirent_h = yes; then cat >> confdefs.h <<\EOF #define HAVE_DIRENT_H 1 EOF fi # Under Next 3.2 apparently does not define struct utimbuf # by default. echo $ac_n "checking for utime.h""... $ac_c" 1>&6 echo "configure:2292: checking for utime.h" >&5 if eval "test \"`echo '$''{'uucp_cv_header_utime_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if HAVE_TIME_H #include #endif #include int main() { struct utimbuf s; ; return 0; } EOF if { (eval echo configure:2308: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_header_utime_h=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_header_utime_h=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_header_utime_h" 1>&6 if test $uucp_cv_header_utime_h = yes; then cat >> confdefs.h <<\EOF #define HAVE_UTIME_H 1 EOF fi echo $ac_n "checking for sys/select.h""... $ac_c" 1>&6 echo "configure:2328: checking for sys/select.h" >&5 if eval "test \"`echo '$''{'uucp_cv_header_sys_select_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if HAVE_SYS_TIME_H #include #endif int main() { int i; ; return 0; } EOF if { (eval echo configure:2343: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_header_sys_select_h=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_header_sys_select_h=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_header_sys_select_h" 1>&6 if test $uucp_cv_header_sys_select_h = yes; then cat >> confdefs.h <<\EOF #define HAVE_SYS_SELECT_H 1 EOF fi echo $ac_n "checking whether sys/types.h defines makedev""... $ac_c" 1>&6 echo "configure:2363: checking whether sys/types.h defines makedev" >&5 if eval "test \"`echo '$''{'ac_cv_header_sys_types_h_makedev'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { return makedev(0, 0); ; return 0; } EOF if { (eval echo configure:2375: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_header_sys_types_h_makedev=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_header_sys_types_h_makedev=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_header_sys_types_h_makedev" 1>&6 if test $ac_cv_header_sys_types_h_makedev = no; then ac_safe=`echo "sys/mkdev.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for sys/mkdev.h""... $ac_c" 1>&6 echo "configure:2393: checking for sys/mkdev.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2403: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define MAJOR_IN_MKDEV 1 EOF else echo "$ac_t""no" 1>&6 fi if test $ac_cv_header_sys_mkdev_h = no; then ac_safe=`echo "sys/sysmacros.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for sys/sysmacros.h""... $ac_c" 1>&6 echo "configure:2431: checking for sys/sysmacros.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2441: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define MAJOR_IN_SYSMACROS 1 EOF else echo "$ac_t""no" 1>&6 fi fi fi echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 echo "configure:2469: checking return type of signal handlers" >&5 if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #ifdef signal #undef signal #endif #ifdef __cplusplus extern "C" void (*signal (int, void (*)(int)))(int); #else void (*signal ()) (); #endif int main() { int i; ; return 0; } EOF if { (eval echo configure:2491: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_type_signal=void else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_type_signal=int fi rm -f conftest* fi echo "$ac_t""$ac_cv_type_signal" 1>&6 cat >> confdefs.h <&6 echo "configure:2510: checking whether stat file-mode macros are broken" >&5 if eval "test \"`echo '$''{'ac_cv_header_stat_broken'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #if defined(S_ISBLK) && defined(S_IFDIR) # if S_ISBLK (S_IFDIR) You lose. # endif #endif #if defined(S_ISBLK) && defined(S_IFCHR) # if S_ISBLK (S_IFCHR) You lose. # endif #endif #if defined(S_ISLNK) && defined(S_IFREG) # if S_ISLNK (S_IFREG) You lose. # endif #endif #if defined(S_ISSOCK) && defined(S_IFREG) # if S_ISSOCK (S_IFREG) You lose. # endif #endif EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "You lose" >/dev/null 2>&1; then rm -rf conftest* ac_cv_header_stat_broken=yes else rm -rf conftest* ac_cv_header_stat_broken=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_header_stat_broken" 1>&6 if test $ac_cv_header_stat_broken = yes; then cat >> confdefs.h <<\EOF #define STAT_MACROS_BROKEN 1 EOF fi echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 echo "configure:2566: checking whether time.h and sys/time.h may both be included" >&5 if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #include int main() { struct tm *tp; ; return 0; } EOF if { (eval echo configure:2580: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_header_time=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_header_time=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_header_time" 1>&6 if test $ac_cv_header_time = yes; then cat >> confdefs.h <<\EOF #define TIME_WITH_SYS_TIME 1 EOF fi echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6 echo "configure:2601: checking whether struct tm is in sys/time.h or time.h" >&5 if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include int main() { struct tm *tp; tp->tm_sec; ; return 0; } EOF if { (eval echo configure:2614: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_struct_tm=time.h else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_struct_tm=sys/time.h fi rm -f conftest* fi echo "$ac_t""$ac_cv_struct_tm" 1>&6 if test $ac_cv_struct_tm = sys/time.h; then cat >> confdefs.h <<\EOF #define TM_IN_SYS_TIME 1 EOF fi echo $ac_n "checking whether termios.h and sys/ioctl.h may both be included""... $ac_c" 1>&6 echo "configure:2635: checking whether termios.h and sys/ioctl.h may both be included" >&5 if eval "test \"`echo '$''{'uucp_cv_header_termios_and_ioctl'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include int main() { int i; ; return 0; } EOF if { (eval echo configure:2648: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_header_termios_and_ioctl=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_header_termios_and_ioctl=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_header_termios_and_ioctl" 1>&6 if test $uucp_cv_header_termios_and_ioctl = yes; then cat >> confdefs.h <<\EOF #define HAVE_TERMIOS_AND_SYS_IOCTL_H 1 EOF fi echo $ac_n "checking for CBREAK""... $ac_c" 1>&6 echo "configure:2668: checking for CBREAK" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_cbreak'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { int i = CBREAK; ; return 0; } EOF if { (eval echo configure:2680: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_cbreak=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_cbreak=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_cbreak" 1>&6 if test $uucp_cv_decl_cbreak = yes; then cat >> confdefs.h <<\EOF #define HAVE_CBREAK 1 EOF fi echo $ac_n "checking for pid_t in sys/types.h""... $ac_c" 1>&6 echo "configure:2700: checking for pid_t in sys/types.h" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_pid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { pid_t x; ; return 0; } EOF if { (eval echo configure:2712: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_pid_t=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_pid_t=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_pid_t" 1>&6 if test $uucp_cv_decl_pid_t = no; then cat >> confdefs.h <<\EOF #define PID_T int EOF fi echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6 echo "configure:2732: checking for uid_t in sys/types.h" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_uid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { uid_t x; ; return 0; } EOF if { (eval echo configure:2744: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_uid_t=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_uid_t=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_uid_t" 1>&6 if test $uucp_cv_decl_uid_t = no; then cat >> confdefs.h <<\EOF #define UID_T int EOF fi echo $ac_n "checking for gid_t in sys/types.h""... $ac_c" 1>&6 echo "configure:2764: checking for gid_t in sys/types.h" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_gid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { gid_t x; ; return 0; } EOF if { (eval echo configure:2776: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_gid_t=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_gid_t=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_gid_t" 1>&6 if test $uucp_cv_decl_gid_t = no; then cat >> confdefs.h <<\EOF #define GID_T int EOF fi echo $ac_n "checking for off_t in sys/types.h""... $ac_c" 1>&6 echo "configure:2796: checking for off_t in sys/types.h" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_off_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { off_t x; ; return 0; } EOF if { (eval echo configure:2808: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_off_t=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_off_t=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_off_t" 1>&6 if test $uucp_cv_decl_off_t = no; then cat >> confdefs.h <<\EOF #define OFF_T long EOF fi echo $ac_n "checking for sig_atomic_t in signal.h""... $ac_c" 1>&6 echo "configure:2828: checking for sig_atomic_t in signal.h" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_sig_atomic_t_signal_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { sig_atomic_t x; ; return 0; } EOF if { (eval echo configure:2840: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_sig_atomic_t_signal_h=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_sig_atomic_t_signal_h=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_sig_atomic_t_signal_h" 1>&6 if test $uucp_cv_decl_sig_atomic_t_signal_h = yes; then cat >> confdefs.h <<\EOF #define HAVE_SIG_ATOMIC_T_IN_SIGNAL_H 1 EOF fi echo $ac_n "checking for sig_atomic_t in sys/types.h""... $ac_c" 1>&6 echo "configure:2860: checking for sig_atomic_t in sys/types.h" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_sig_atomic_t_types_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { sig_atomic_t x; ; return 0; } EOF if { (eval echo configure:2872: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_sig_atomic_t_types_h=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_sig_atomic_t_types_h=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_sig_atomic_t_types_h" 1>&6 if test $uucp_cv_decl_sig_atomic_t_types_h = yes; then cat >> confdefs.h <<\EOF #define HAVE_SIG_ATOMIC_T_IN_TYPES_H 1 EOF fi if test $ac_cv_header_stddef_h = yes; then echo $ac_n "checking for size_t in stddef.h""... $ac_c" 1>&6 echo "configure:2893: checking for size_t in stddef.h" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_size_t_stddef_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { size_t x; ; return 0; } EOF if { (eval echo configure:2905: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_size_t_stddef_h=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_size_t_stddef_h=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_size_t_stddef_h" 1>&6 if test $uucp_cv_decl_size_t_stddef_h = yes; then cat >> confdefs.h <<\EOF #define HAVE_SIZE_T_IN_STDDEF_H 1 EOF fi fi echo $ac_n "checking for size_t in sys/types.h""... $ac_c" 1>&6 echo "configure:2926: checking for size_t in sys/types.h" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_size_t_types_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { size_t x; ; return 0; } EOF if { (eval echo configure:2938: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_size_t_types_h=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_size_t_types_h=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_size_t_types_h" 1>&6 if test $uucp_cv_decl_size_t_types_h = yes; then cat >> confdefs.h <<\EOF #define HAVE_SIZE_T_IN_TYPES_H 1 EOF fi echo $ac_n "checking for time_t in time.h""... $ac_c" 1>&6 echo "configure:2958: checking for time_t in time.h" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_time_t_time_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { time_t i; ; return 0; } EOF if { (eval echo configure:2970: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_time_t_time_h=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_time_t_time_h=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_time_t_time_h" 1>&6 if test $uucp_cv_decl_time_t_time_h = yes; then cat >> confdefs.h <<\EOF #define HAVE_TIME_T_IN_TIME_H 1 EOF fi echo $ac_n "checking time_t in sys/types.h""... $ac_c" 1>&6 echo "configure:2990: checking time_t in sys/types.h" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_time_t_types_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { time_t i; ; return 0; } EOF if { (eval echo configure:3002: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_time_t_types_h=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_time_t_types_h=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_time_t_types_h" 1>&6 if test $uucp_cv_decl_time_t_types_h = yes; then cat >> confdefs.h <<\EOF #define HAVE_TIME_T_IN_TYPES_H 1 EOF fi echo $ac_n "checking for void""... $ac_c" 1>&6 echo "configure:3022: checking for void" >&5 if eval "test \"`echo '$''{'uucp_cv_c_void'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_c_void=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_c_void=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_c_void" 1>&6 if test $uucp_cv_c_void = yes; then cat >> confdefs.h <<\EOF #define HAVE_VOID 1 EOF fi echo $ac_n "checking for unsigned char""... $ac_c" 1>&6 echo "configure:3054: checking for unsigned char" >&5 if eval "test \"`echo '$''{'uucp_cv_c_unsigned_char'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_c_unsigned_char=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_c_unsigned_char=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_c_unsigned_char" 1>&6 if test $uucp_cv_c_unsigned_char = yes; then cat >> confdefs.h <<\EOF #define HAVE_UNSIGNED_CHAR 1 EOF fi echo $ac_n "checking for errno declaration""... $ac_c" 1>&6 echo "configure:3086: checking for errno declaration" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_errno'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { int i = errno; errno = 1; ; return 0; } EOF if { (eval echo configure:3098: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_errno=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_errno=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_errno" 1>&6 if test $uucp_cv_decl_errno = yes; then cat >> confdefs.h <<\EOF #define HAVE_ERRNO_DECLARATION 1 EOF fi echo $ac_n "checking for TXADDCD""... $ac_c" 1>&6 echo "configure:3118: checking for TXADDCD" >&5 if eval "test \"`echo '$''{'uucp_cv_txaddcd'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { int i = (int) TXADDCD; ; return 0; } EOF if { (eval echo configure:3130: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_txaddcd=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_txaddcd=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_txaddcd" 1>&6 if test $uucp_cv_txaddcd = yes; then cat >> confdefs.h <<\EOF #define HAVE_TXADDCD 1 EOF fi echo $ac_n "checking for memset""... $ac_c" 1>&6 echo "configure:3150: checking for memset" >&5 if eval "test \"`echo '$''{'ac_cv_func_memset'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_func_memset=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_func_memset=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_func_memset" 1>&6 if test $ac_cv_func_memset = yes; then cat >> confdefs.h <<\EOF #define HAVE_MEMSET 1 EOF fi echo $ac_n "checking for memcmp""... $ac_c" 1>&6 echo "configure:3182: checking for memcmp" >&5 if eval "test \"`echo '$''{'ac_cv_func_memcmp'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_func_memcmp=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_func_memcmp=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_func_memcmp" 1>&6 if test $ac_cv_func_memcmp = yes; then cat >> confdefs.h <<\EOF #define HAVE_MEMCMP 1 EOF fi echo $ac_n "checking for memcpy""... $ac_c" 1>&6 echo "configure:3214: checking for memcpy" >&5 if eval "test \"`echo '$''{'ac_cv_func_memcpy'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_func_memcpy=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_func_memcpy=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_func_memcpy" 1>&6 if test $ac_cv_func_memcpy = yes; then cat >> confdefs.h <<\EOF #define HAVE_MEMCPY 1 EOF fi for ac_func in memchr bcopy bcmp bzero do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3249: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3277: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in strchr strrchr index rindex strerror strtol strtoul strstr do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3304: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3332: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in strdup strcasecmp strncasecmp stricmp strnicmp do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3359: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3387: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in bsearch vfprintf do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3414: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3442: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in remove ftruncate ltrunc rename opendir dup2 waitpid wait4 do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3469: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3497: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in sigsetjmp setret sigaction sigvec sigset do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3524: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3552: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in sigprocmask sigblock sighold getdtablesize sysconf do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3579: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3607: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in setpgrp setsid setreuid seteuid gethostname uname do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3634: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3662: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in gettimeofday ftw glob dev_info getaddrinfo do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3689: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3717: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done echo $ac_n "checking for getdelim""... $ac_c" 1>&6 echo "configure:3742: checking for getdelim" >&5 if eval "test \"`echo '$''{'ac_cv_func_getdelim'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char getdelim(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_getdelim) || defined (__stub___getdelim) choke me #else getdelim(); #endif ; return 0; } EOF if { (eval echo configure:3770: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_getdelim=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_getdelim=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'getdelim`\" = yes"; then echo "$ac_t""yes" 1>&6 echo $ac_n "checking for getline""... $ac_c" 1>&6 echo "configure:3785: checking for getline" >&5 if eval "test \"`echo '$''{'ac_cv_func_getline'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char getline(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_getline) || defined (__stub___getline) choke me #else getline(); #endif ; return 0; } EOF if { (eval echo configure:3813: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_getline=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_getline=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'getline`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_GETLINE 1 EOF else echo "$ac_t""no" 1>&6 LIBOBJS="$LIBOBJS getlin.o" fi else echo "$ac_t""no" 1>&6 LIBOBJS="$LIBOBJS getlin.o" fi echo $ac_n "checking for ftime""... $ac_c" 1>&6 echo "configure:3842: checking for ftime" >&5 if eval "test \"`echo '$''{'ac_cv_func_ftime'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char ftime(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_ftime) || defined (__stub___ftime) choke me #else ftime(); #endif ; return 0; } EOF if { (eval echo configure:3870: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_ftime=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_ftime=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'ftime`\" = yes"; then echo "$ac_t""yes" 1>&6 echo $ac_n "checking that ftime works correctly""... $ac_c" 1>&6 echo "configure:3885: checking that ftime works correctly" >&5 if eval "test \"`echo '$''{'uucp_cv_sys_ftime_ok'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then uucp_cv_sys_ftime_ok=runtime else cat > conftest.$ac_ext < #include main () { struct timeb s, slast; int c = 0; ftime (&slast); while (c < 10) { ftime (&s); if (s.time < slast.time || (s.time == slast.time && s.millitm < slast.millitm)) exit (1); if (s.time != slast.time) ++c; slast.time = s.time; slast.millitm = s.millitm; } exit (0); } EOF if { (eval echo configure:3918: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then uucp_cv_sys_ftime_ok=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* uucp_cv_sys_ftime_ok=no fi rm -fr conftest* fi fi case $uucp_cv_sys_ftime_ok in yes) echo "$ac_t""yes" 1>&6 ;; no) echo "$ac_t""no" 1>&6 echo "configure: warning: ftime seems to be buggy" 1>&2 ;; runtime) echo "$ac_t""will check at run time" 1>&6 ;; esac else echo "$ac_t""no" 1>&6 fi if test $uucp_cv_sys_ftime_ok = yes || test $uucp_cv_sys_ftime_ok = runtime; then cat >> confdefs.h <<\EOF #define HAVE_FTIME 1 EOF fi for ac_func in times do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3952: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3980: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in napms nap usleep poll select do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:4007: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:4035: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done if test $ac_cv_func_napms != yes \ && test $ac_cv_func_nap != yes \ && test $ac_cv_func_usleep != yes \ && test $ac_cv_func_poll != yes \ && test $ac_cv_func_select != yes; then echo "configure: warning: No way to sleep for less than one second" 1>&2 echo "configure: warning: \p in chat scripts will sleep for a full second" 1>&2 fi for ac_func in getgrent do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:4070: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:4098: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done case $LIBS in *-lnsl*) ;; *) echo $ac_n "checking for main in -lnsl_s""... $ac_c" 1>&6 echo "configure:4125: checking for main in -lnsl_s" >&5 ac_lib_var=`echo nsl_s'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lnsl_s $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_lib=HAVE_LIB`echo nsl_s | sed -e 's/[^a-zA-Z0-9_]/_/g' \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` cat >> confdefs.h <&6 fi ;; esac case $LIBS in *-lnsl*) ;; *) echo $ac_n "checking for main in -lnsl""... $ac_c" 1>&6 echo "configure:4171: checking for main in -lnsl" >&5 ac_lib_var=`echo nsl'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lnsl $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/[^a-zA-Z0-9_]/_/g' \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` cat >> confdefs.h <&6 fi ;; esac case $LIBS in *-lsocket*) ;; *) echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6 echo "configure:4217: checking for socket in -lsocket" >&5 ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lsocket $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` cat >> confdefs.h <&6 fi ;; esac case $LIBS in *-lxti*) ;; *) echo $ac_n "checking for t_open in -lxti""... $ac_c" 1>&6 echo "configure:4267: checking for t_open in -lxti" >&5 ac_lib_var=`echo xti'_'t_open | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lxti $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_lib=HAVE_LIB`echo xti | sed -e 's/[^a-zA-Z0-9_]/_/g' \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` cat >> confdefs.h <&6 fi ;; esac for ac_func in socket t_open do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:4317: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:4345: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in getcwd getwd do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:4372: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:4400: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done if test $ac_cv_func_getcwd != yes \ && test $ac_cv_func_getwd != yes; then UNIXOBJS="$UNIXOBJS getcwd.o" if test -s /bin/pwd; then cat >> confdefs.h <<\EOF #define PWD_PROGRAM "/bin/pwd" EOF fi fi for ac_func in mkdir do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:4437: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:4465: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done if test $ac_cv_func_mkdir = yes; then HAVE_MKDIR_TRUE= HAVE_MKDIR_FALSE='#' else HAVE_MKDIR_TRUE='#' HAVE_MKDIR_FALSE= fi if test $ac_cv_func_mkdir != yes; then UNIXOBJS="$UNIXOBJS mkdir.o" if test -s /bin/mkdir; then cat >> confdefs.h <<\EOF #define MKDIR_PROGRAM "/bin/mkdir" EOF fi fi for ac_func in rmdir do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:4510: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:4538: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done if test $ac_cv_func_rmdir != yes; then UNIXOBJS="$UNIXOBJS rmdir.o" if test -s /bin/rmdir; then cat >> confdefs.h <<\EOF #define RMDIR_PROGRAM "/bin/rmdir" EOF fi fi if test $ac_cv_func_bsearch != yes; then LIBOBJS="$LIBOBJS bsrch.o" fi if test $ac_cv_func_bzero != yes \ && test $ac_cv_func_memset != yes; then LIBOBJS="$LIBOBJS bzero.o" fi if test $ac_cv_func_memchr != yes; then LIBOBJS="$LIBOBJS memchr.o" fi if test $ac_cv_func_memcmp != yes \ && test $ac_cv_func_bcmp != yes; then LIBOBJS="$LIBOBJS memcmp.o" fi if test $ac_cv_func_memcpy != yes \ && test $ac_cv_func_bcopy != yes; then LIBOBJS="$LIBOBJS memcpy.o" fi if test $ac_cv_func_strcasecmp != yes \ && test $ac_cv_func_stricmp != yes; then LIBOBJS="$LIBOBJS strcas.o" fi if test $ac_cv_func_strchr != yes \ && test $ac_cv_func_index != yes; then LIBOBJS="$LIBOBJS strchr.o" fi if test $ac_cv_func_strdup != yes; then LIBOBJS="$LIBOBJS strdup.o" fi if test $ac_cv_func_strncasecmp != yes \ && test $ac_cv_func_strnicmp != yes; then LIBOBJS="$LIBOBJS strncs.o" fi if test $ac_cv_func_strrchr != yes \ && test $ac_cv_func_rindex != yes; then LIBOBJS="$LIBOBJS strrch.o" fi if test $ac_cv_func_strstr != yes; then LIBOBJS="$LIBOBJS strstr.o" fi if test $ac_cv_func_strtol != yes; then LIBOBJS="$LIBOBJS strtol.o" fi if test $ac_cv_func_strtoul != yes; then LIBOBJS="$LIBOBJS strtou.o" fi if test $ac_cv_func_opendir != yes; then UNIXOBJS="$UNIXOBJS dirent.o" fi if test $ac_cv_func_dup2 != yes; then UNIXOBJS="$UNIXOBJS dup2.o" fi if test $ac_cv_func_ftw != yes; then UNIXOBJS="$UNIXOBJS ftw.o" fi if test $ac_cv_func_remove != yes; then UNIXOBJS="$UNIXOBJS remove.o" fi if test $ac_cv_func_rename != yes; then UNIXOBJS="$UNIXOBJS rename.o" fi if test $ac_cv_func_strerror != yes; then UNIXOBJS="$UNIXOBJS strerr.o" fi if test $ac_cv_func_sigvec = yes; then echo $ac_n "checking for sv_flags""... $ac_c" 1>&6 echo "configure:4638: checking for sv_flags" >&5 if eval "test \"`echo '$''{'uucp_cv_struct_sv_flags'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { struct sigvec s; s.sv_flags = 0; ; return 0; } EOF if { (eval echo configure:4650: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_struct_sv_flags=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_struct_sv_flags=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_struct_sv_flags" 1>&6 if test $uucp_cv_struct_sv_flags = yes; then cat >> confdefs.h <<\EOF #define HAVE_SIGVEC_SV_FLAGS 1 EOF fi fi echo "checking how to get filesystem space usage" 1>&6 echo "configure:4672: checking how to get filesystem space usage" >&5 space=no # Here we'll compromise a little (and perform only the link test) # since it seems there are no variants of the statvfs function. if test $space = no; then # SVR4 for ac_func in statvfs do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:4682: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:4710: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done if test $ac_cv_func_statvfs = yes; then space=yes cat >> confdefs.h <<\EOF #define STAT_STATVFS 1 EOF fi fi if test $space = no; then # DEC Alpha running OSF/1 echo $ac_n "checking for 3-argument statfs function (DEC OSF/1)""... $ac_c" 1>&6 echo "configure:4746: checking for 3-argument statfs function (DEC OSF/1)" >&5 if eval "test \"`echo '$''{'uucp_cv_sys_stat_statfs3_osf1'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then uucp_cv_sys_stat_statfs3_osf1=no else cat > conftest.$ac_ext < #include #include main () { struct statfs fsd; fsd.f_fsize = 0; exit (statfs (".", &fsd, sizeof (struct statfs))); } EOF if { (eval echo configure:4767: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then uucp_cv_sys_stat_statfs3_osf1=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* uucp_cv_sys_stat_statfs3_osf1=no fi rm -fr conftest* fi fi echo "$ac_t""$uucp_cv_sys_stat_statfs3_osf1" 1>&6 if test $uucp_cv_sys_stat_statfs3_osf1 = yes; then space=yes cat >> confdefs.h <<\EOF #define STAT_STATFS3_OSF1 1 EOF fi fi if test $space = no; then # AIX echo $ac_n "checking for two-argument statfs with statfs.bsize member (AIX, 4.3BSD)""... $ac_c" 1>&6 echo "configure:4794: checking for two-argument statfs with statfs.bsize member (AIX, 4.3BSD)" >&5 if eval "test \"`echo '$''{'uucp_cv_sys_stat_statfs2_bsize'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then uucp_cv_sys_stat_statfs2_bsize=no else cat > conftest.$ac_ext < #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_VFS_H #include #endif main () { struct statfs fsd; fsd.f_bsize = 0; exit (statfs (".", &fsd)); } EOF if { (eval echo configure:4821: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then uucp_cv_sys_stat_statfs2_bsize=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* uucp_cv_sys_stat_statfs2_bsize=no fi rm -fr conftest* fi fi echo "$ac_t""$uucp_cv_sys_stat_statfs2_bsize" 1>&6 if test $uucp_cv_sys_stat_statfs2_bsize = yes; then space=yes cat >> confdefs.h <<\EOF #define STAT_STATFS2_BSIZE 1 EOF fi fi if test $space = no; then # SVR3 echo $ac_n "checking for four-argument statfs (AIX-3.2.5, SVR3)""... $ac_c" 1>&6 echo "configure:4848: checking for four-argument statfs (AIX-3.2.5, SVR3)" >&5 if eval "test \"`echo '$''{'uucp_cv_sys_stat_statfs4'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then uucp_cv_sys_stat_statfs4=no else cat > conftest.$ac_ext < #include main () { struct statfs fsd; exit (statfs (".", &fsd, sizeof fsd, 0)); } EOF if { (eval echo configure:4866: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then uucp_cv_sys_stat_statfs4=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* uucp_cv_sys_stat_statfs4=no fi rm -fr conftest* fi fi echo "$ac_t""$uucp_cv_sys_stat_statfs4" 1>&6 if test $uucp_cv_sys_stat_statfs4 = yes; then space=yes cat >> confdefs.h <<\EOF #define STAT_STATFS4 1 EOF fi fi if test $space = no; then # 4.4BSD and NetBSD echo $ac_n "checking for two-argument statfs with statfs.fsize member (4.4BSD and NetBSD)""... $ac_c" 1>&6 echo "configure:4893: checking for two-argument statfs with statfs.fsize member (4.4BSD and NetBSD)" >&5 if eval "test \"`echo '$''{'uucp_cv_sys_stat_statfs2_fsize'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then uucp_cv_sys_stat_statfs2_fsize=no else cat > conftest.$ac_ext < #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif main () { struct statfs fsd; fsd.f_fsize = 0; exit (statfs (".", &fsd)); } EOF if { (eval echo configure:4917: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then uucp_cv_sys_stat_statfs2_fsize=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* uucp_cv_sys_stat_statfs2_fsize=no fi rm -fr conftest* fi fi echo "$ac_t""$uucp_cv_sys_stat_statfs2_fsize" 1>&6 if test $uucp_cv_sys_stat_statfs2_fsize = yes; then space=yes cat >> confdefs.h <<\EOF #define STAT_STATFS2_FSIZE 1 EOF fi fi if test $space = no; then # Ultrix echo $ac_n "checking for two-argument statfs with struct fs_data (Ultrix)""... $ac_c" 1>&6 echo "configure:4944: checking for two-argument statfs with struct fs_data (Ultrix)" >&5 if eval "test \"`echo '$''{'uucp_cv_sys_stat_fs_data'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then uucp_cv_sys_stat_fs_data=no else cat > conftest.$ac_ext < #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_FS_TYPES_H #include #endif main () { struct fs_data fsd; /* Ultrix's statfs returns 1 for success, 0 for not mounted, -1 for failure. */ exit (statfs (".", &fsd) != 1); } EOF if { (eval echo configure:4972: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then uucp_cv_sys_stat_fs_data=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* uucp_cv_sys_stat_fs_data=no fi rm -fr conftest* fi fi echo "$ac_t""$uucp_cv_sys_stat_fs_data" 1>&6 if test $uucp_cv_sys_stat_fs_data = yes; then space=yes cat >> confdefs.h <<\EOF #define STAT_STATFS2_FS_DATA 1 EOF fi fi if test $space = no; then for ac_func in ustat do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:5000: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:5028: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done if test $ac_cv_func_ustat = yes; then space=yes cat >> confdefs.h <<\EOF #define STAT_USTAT 1 EOF fi fi if test $space = no; then # QNX for ac_func in disk_space do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:5066: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:5094: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done if test $ac_cv_func_disk_space = yes; then space=yes cat >> confdefs.h <<\EOF #define STAT_DISK_SPACE 1 EOF fi fi echo $ac_n "checking for times declared as long""... $ac_c" 1>&6 echo "configure:5129: checking for times declared as long" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_times'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #include #ifdef HAVE_LIBC_H #include #endif #ifdef HAVE_SYS_TIMES_H #include #endif extern long times (); int main() { int i = 0; ; return 0; } EOF if { (eval echo configure:5150: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "uucp_cv_decl_times=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "uucp_cv_decl_times=no" fi rm -f conftest* fi if eval "test \"`echo '$uucp_cv_decl_'times`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <&6 fi echo $ac_n "checking for getpwnam declared as struct passwd *""... $ac_c" 1>&6 echo "configure:5173: checking for getpwnam declared as struct passwd *" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_getpwnam'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #include #ifdef HAVE_LIBC_H #include #endif #ifdef HAVE_SYS_TIMES_H #include #endif extern struct passwd * getpwnam (); int main() { int i = 0; ; return 0; } EOF if { (eval echo configure:5194: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "uucp_cv_decl_getpwnam=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "uucp_cv_decl_getpwnam=no" fi rm -f conftest* fi if eval "test \"`echo '$uucp_cv_decl_'getpwnam`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <&6 fi echo $ac_n "checking for getpwuid declared as struct passwd *""... $ac_c" 1>&6 echo "configure:5217: checking for getpwuid declared as struct passwd *" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_getpwuid'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #include #ifdef HAVE_LIBC_H #include #endif #ifdef HAVE_SYS_TIMES_H #include #endif extern struct passwd * getpwuid (); int main() { int i = 0; ; return 0; } EOF if { (eval echo configure:5238: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "uucp_cv_decl_getpwuid=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "uucp_cv_decl_getpwuid=no" fi rm -f conftest* fi if eval "test \"`echo '$uucp_cv_decl_'getpwuid`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <&6 fi echo $ac_n "checking for getgrent declared as struct group *""... $ac_c" 1>&6 echo "configure:5261: checking for getgrent declared as struct group *" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_getgrent'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #include #ifdef HAVE_LIBC_H #include #endif #ifdef HAVE_SYS_TIMES_H #include #endif extern struct group * getgrent (); int main() { int i = 0; ; return 0; } EOF if { (eval echo configure:5282: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "uucp_cv_decl_getgrent=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "uucp_cv_decl_getgrent=no" fi rm -f conftest* fi if eval "test \"`echo '$uucp_cv_decl_'getgrent`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <&6 fi echo $ac_n "checking for BSD setpgrp""... $ac_c" 1>&6 echo "configure:5305: checking for BSD setpgrp" >&5 if eval "test \"`echo '$''{'uucp_cv_decl_setpgrp'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #endif int main() { getpgrp (0); setpgrp (0, 0); ; return 0; } EOF if { (eval echo configure:5319: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_decl_setpgrp=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_decl_setpgrp=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_decl_setpgrp" 1>&6 if test $uucp_cv_decl_setpgrp = yes; then cat >> confdefs.h <<\EOF #define HAVE_BSD_PGRP 1 EOF fi echo $ac_n "checking for union wait""... $ac_c" 1>&6 echo "configure:5339: checking for union wait" >&5 if eval "test \"`echo '$''{'uucp_cv_struct_wait'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #ifndef WIFEXITED #define WIFEXITED(u) ((u).w_termsig == 0) #endif int main() { union wait u; if (WIFEXITED (u)) wait (&u); ; return 0; } EOF if { (eval echo configure:5354: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_struct_wait=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_struct_wait=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_struct_wait" 1>&6 if test $uucp_cv_struct_wait = yes; then cat >> confdefs.h <<\EOF #define HAVE_UNION_WAIT 1 EOF fi echo $ac_n "checking for struct sockaddr_storage""... $ac_c" 1>&6 echo "configure:5374: checking for struct sockaddr_storage" >&5 if eval "test \"`echo '$''{'uucp_cv_struct_sockaddrstorage'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { struct sockaddr_storage s; ; return 0; } EOF if { (eval echo configure:5386: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* uucp_cv_struct_sockaddrstorage=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* uucp_cv_struct_sockaddrstorage=no fi rm -f conftest* fi echo "$ac_t""$uucp_cv_struct_sockaddrstorage" 1>&6 if test $uucp_cv_struct_sockaddrstorage = yes; then cat >> confdefs.h <<\EOF #define HAVE_STRUCT_SOCKADDR_STORAGE 1 EOF fi if test "$cross_compiling" = yes; then cat >> confdefs.h <<\EOF #define HAVE_LONG_FILE_NAMES 0 EOF cat >> confdefs.h <<\EOF #define HAVE_RESTARTABLE_SYSCALLS -1 EOF else echo $ac_n "checking for restartable system calls""... $ac_c" 1>&6 echo "configure:5416: checking for restartable system calls" >&5 if eval "test \"`echo '$''{'ac_cv_sys_restartable_syscalls'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext < #include ucatch (isig) { } main () { int i = fork (), status; if (i == 0) { sleep (3); kill (getppid (), SIGINT); sleep (3); exit (0); } signal (SIGINT, ucatch); status = wait(&i); if (status == -1) wait(&i); exit (status == -1); } EOF if { (eval echo configure:5442: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_sys_restartable_syscalls=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* ac_cv_sys_restartable_syscalls=no fi rm -fr conftest* fi fi echo "$ac_t""$ac_cv_sys_restartable_syscalls" 1>&6 if test $ac_cv_sys_restartable_syscalls = yes; then cat >> confdefs.h <<\EOF #define HAVE_RESTARTABLE_SYSCALLS 1 EOF fi echo $ac_n "checking for long file names""... $ac_c" 1>&6 echo "configure:5465: checking for long file names" >&5 if eval "test \"`echo '$''{'ac_cv_sys_long_file_names'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_cv_sys_long_file_names=yes # Test for long file names in all the places we know might matter: # . the current directory, where building will happen # $prefix/lib where we will be installing things # $exec_prefix/lib likewise # eval it to expand exec_prefix. # $TMPDIR if set, where it might want to write temporary files # if $TMPDIR is not set: # /tmp where it might want to write temporary files # /var/tmp likewise # /usr/tmp likewise if test -n "$TMPDIR" && test -d "$TMPDIR" && test -w "$TMPDIR"; then ac_tmpdirs="$TMPDIR" else ac_tmpdirs='/tmp /var/tmp /usr/tmp' fi for ac_dir in . $ac_tmpdirs `eval echo $prefix/lib $exec_prefix/lib` ; do test -d $ac_dir || continue test -w $ac_dir || continue # It is less confusing to not echo anything here. (echo 1 > $ac_dir/conftest9012345) 2>/dev/null (echo 2 > $ac_dir/conftest9012346) 2>/dev/null val=`cat $ac_dir/conftest9012345 2>/dev/null` if test ! -f $ac_dir/conftest9012345 || test "$val" != 1; then ac_cv_sys_long_file_names=no rm -f $ac_dir/conftest9012345 $ac_dir/conftest9012346 2>/dev/null break fi rm -f $ac_dir/conftest9012345 $ac_dir/conftest9012346 2>/dev/null done fi echo "$ac_t""$ac_cv_sys_long_file_names" 1>&6 if test $ac_cv_sys_long_file_names = yes; then cat >> confdefs.h <<\EOF #define HAVE_LONG_FILE_NAMES 1 EOF fi fi trap '' 1 2 15 cat > confcache <<\EOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs. It is not useful on other systems. # If it contains results you don't want to keep, you may remove or edit it. # # By default, configure uses ./config.cache as the cache file, # creating it if it does not exist already. You can give configure # the --cache-file=FILE option to use a different cache file; that is # what configure does when it calls configure scripts in # subdirectories, so they share the cache. # Giving --cache-file=/dev/null disables caching, for debugging configure. # config.status only pays attention to the cache file if you give it the # --recheck option to rerun configure. # EOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote substitution # turns \\\\ into \\, and sed turns \\ into \). sed -n \ -e "s/'/'\\\\''/g" \ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' ;; esac >> confcache if cmp -s $cache_file confcache; then : else if test -w $cache_file; then echo "updating cache $cache_file" cat confcache > $cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Any assignment to VPATH causes Sun make to only execute # the first set of double-colon rules, so remove it if not needed. # If there is a colon in the path, we need to keep it. if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' fi trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 DEFS=-DHAVE_CONFIG_H # Without the "./", some shells look in PATH for config.status. : ${CONFIG_STATUS=./config.status} echo creating $CONFIG_STATUS rm -f $CONFIG_STATUS cat > $CONFIG_STATUS </dev/null | sed 1q`: # # $0 $ac_configure_args # # Compiler output produced by configure, useful for debugging # configure, is in ./config.log if it exists. ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" for ac_option do case "\$ac_option" in -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v) echo "$CONFIG_STATUS generated by autoconf version 2.13" exit 0 ;; -help | --help | --hel | --he | --h) echo "\$ac_cs_usage"; exit 0 ;; *) echo "\$ac_cs_usage"; exit 1 ;; esac done ac_given_srcdir=$srcdir ac_given_INSTALL="$INSTALL" trap 'rm -fr `echo "Makefile uuconf/Makefile lib/Makefile unix/Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 EOF cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF $ac_vpsub $extrasub s%@SHELL@%$SHELL%g s%@CFLAGS@%$CFLAGS%g s%@CPPFLAGS@%$CPPFLAGS%g s%@CXXFLAGS@%$CXXFLAGS%g s%@FFLAGS@%$FFLAGS%g s%@DEFS@%$DEFS%g s%@LDFLAGS@%$LDFLAGS%g s%@LIBS@%$LIBS%g s%@exec_prefix@%$exec_prefix%g s%@prefix@%$prefix%g s%@program_transform_name@%$program_transform_name%g s%@bindir@%$bindir%g s%@sbindir@%$sbindir%g s%@libexecdir@%$libexecdir%g s%@datadir@%$datadir%g s%@sysconfdir@%$sysconfdir%g s%@sharedstatedir@%$sharedstatedir%g s%@localstatedir@%$localstatedir%g s%@libdir@%$libdir%g s%@includedir@%$includedir%g s%@oldincludedir@%$oldincludedir%g s%@infodir@%$infodir%g s%@mandir@%$mandir%g s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g s%@INSTALL_DATA@%$INSTALL_DATA%g s%@PACKAGE@%$PACKAGE%g s%@VERSION@%$VERSION%g s%@EXEEXT@%$EXEEXT%g s%@OBJEXT@%$OBJEXT%g s%@ACLOCAL@%$ACLOCAL%g s%@AUTOCONF@%$AUTOCONF%g s%@AUTOMAKE@%$AUTOMAKE%g s%@AUTOHEADER@%$AUTOHEADER%g s%@MAKEINFO@%$MAKEINFO%g s%@AMTAR@%$AMTAR%g s%@install_sh@%$install_sh%g s%@INSTALL_STRIP_PROGRAM@%$INSTALL_STRIP_PROGRAM%g s%@AWK@%$AWK%g s%@SET_MAKE@%$SET_MAKE%g s%@AMDEP_TRUE@%$AMDEP_TRUE%g s%@AMDEP_FALSE@%$AMDEP_FALSE%g s%@AMDEPBACKSLASH@%$AMDEPBACKSLASH%g s%@DEPDIR@%$DEPDIR%g s%@MAINTAINER_MODE_TRUE@%$MAINTAINER_MODE_TRUE%g s%@MAINTAINER_MODE_FALSE@%$MAINTAINER_MODE_FALSE%g s%@MAINT@%$MAINT%g s%@OWNER@%$OWNER%g s%@NEWCONFIGDIR@%$NEWCONFIGDIR%g s%@OLDCONFIGDIR@%$OLDCONFIGDIR%g s%@GCC@%$GCC%g s%@CC@%$CC%g s%@am__include@%$am__include%g s%@am__quote@%$am__quote%g s%@CCDEPMODE@%$CCDEPMODE%g s%@CPP@%$CPP%g s%@WARN_CFLAGS@%$WARN_CFLAGS%g s%@AR@%$AR%g s%@RANLIB@%$RANLIB%g s%@POUNDBANG@%$POUNDBANG%g s%@LN_S@%$LN_S%g s%@HAVE_MKDIR_TRUE@%$HAVE_MKDIR_TRUE%g s%@HAVE_MKDIR_FALSE@%$HAVE_MKDIR_FALSE%g s%@LIBOBJS@%$LIBOBJS%g s%@UNIXOBJS@%$UNIXOBJS%g CEOF EOF cat >> $CONFIG_STATUS <<\EOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. ac_file=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_cmds # Line after last line for current file. ac_more_lines=: ac_sed_cmds="" while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file else sed "${ac_end}q" conftest.subs > conftest.s$ac_file fi if test ! -s conftest.s$ac_file; then ac_more_lines=false rm -f conftest.s$ac_file else if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f conftest.s$ac_file" else ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" fi ac_file=`expr $ac_file + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_cmds` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi EOF cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case "$ac_file" in *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; *) ac_file_in="${ac_file}.in" ;; esac # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. # Remove last slash and all that follows it. Not all systems have dirname. ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then # The file is in a subdirectory. test ! -d "$ac_dir" && mkdir "$ac_dir" ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" # A "../" for each directory in $ac_dir_suffix. ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` else ac_dir_suffix= ac_dots= fi case "$ac_given_srcdir" in .) srcdir=. if test -z "$ac_dots"; then top_srcdir=. else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; *) # Relative path. srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" top_srcdir="$ac_dots$ac_given_srcdir" ;; esac case "$ac_given_INSTALL" in [/$]*) INSTALL="$ac_given_INSTALL" ;; *) INSTALL="$ac_dots$ac_given_INSTALL" ;; esac echo creating "$ac_file" rm -f "$ac_file" configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." case "$ac_file" in *Makefile*) ac_comsub="1i\\ # $configure_input" ;; *) ac_comsub= ;; esac ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` sed -e "$ac_comsub s%@configure_input@%$configure_input%g s%@srcdir@%$srcdir%g s%@top_srcdir@%$top_srcdir%g s%@INSTALL@%$INSTALL%g " $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file fi; done rm -f conftest.s* # These sed commands are passed to sed as "A NAME B NAME C VALUE D", where # NAME is the cpp macro being defined and VALUE is the value it is being given. # # ac_d sets the value in "#define NAME VALUE" lines. ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' ac_dC='\3' ac_dD='%g' # ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' ac_uB='\([ ]\)%\1#\2define\3' ac_uC=' ' ac_uD='\4%g' # ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' ac_eB='$%\1#\2define\3' ac_eC=' ' ac_eD='%g' if test "${CONFIG_HEADERS+set}" != set; then EOF cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF fi for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case "$ac_file" in *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; *) ac_file_in="${ac_file}.in" ;; esac echo creating $ac_file rm -f conftest.frag conftest.in conftest.out ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` cat $ac_file_inputs > conftest.in EOF # Transform confdefs.h into a sed script conftest.vals that substitutes # the proper values into config.h.in to produce config.h. And first: # Protect against being on the right side of a sed subst in config.status. # Protect against being in an unquoted here document in config.status. rm -f conftest.vals cat > conftest.hdr <<\EOF s/[\\&%]/\\&/g s%[\\$`]%\\&%g s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp s%ac_d%ac_u%gp s%ac_u%ac_e%gp EOF sed -n -f conftest.hdr confdefs.h > conftest.vals rm -f conftest.hdr # This sed command replaces #undef with comments. This is necessary, for # example, in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. cat >> conftest.vals <<\EOF s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% EOF # Break up conftest.vals because some shells have a limit on # the size of here documents, and old seds have small limits too. rm -f conftest.tail while : do ac_lines=`grep -c . conftest.vals` # grep -c gives empty output for an empty file on some AIX systems. if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi # Write a limited-size here document to conftest.frag. echo ' cat > conftest.frag <> $CONFIG_STATUS sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS echo 'CEOF sed -f conftest.frag conftest.in > conftest.out rm -f conftest.in mv conftest.out conftest.in ' >> $CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail rm -f conftest.vals mv conftest.tail conftest.vals done rm -f conftest.vals cat >> $CONFIG_STATUS <<\EOF rm -f conftest.frag conftest.h echo "/* $ac_file. Generated automatically by configure. */" > conftest.h cat conftest.in >> conftest.h rm -f conftest.in if cmp -s $ac_file conftest.h 2>/dev/null; then echo "$ac_file is unchanged" rm -f conftest.h else # Remove last slash and all that follows it. Not all systems have dirname. ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then # The file is in a subdirectory. test ! -d "$ac_dir" && mkdir "$ac_dir" fi rm -f $ac_file mv conftest.h $ac_file fi fi; done EOF cat >> $CONFIG_STATUS < "\$am_dir"stamp-h\$am_indx ;; esac am_indx=\`expr \$am_indx + 1\` done AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" EOF cat >> $CONFIG_STATUS <<\EOF test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do case "$mf" in Makefile) dirpart=.;; */Makefile) dirpart=`echo "$mf" | sed -e 's|/[^/]*$||'`;; *) continue;; esac grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue # Extract the definition of DEP_FILES from the Makefile without # running `make'. DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` test -z "$DEPDIR" && continue # When using ansi2knr, U may be empty or an underscore; expand it U=`sed -n -e '/^U = / s///p' < "$mf"` test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" # We invoke sed twice because it is the simplest approach to # changing $(DEPDIR) to its actual value in the expansion. for file in `sed -n -e ' /^DEP_FILES = .*\\\\$/ { s/^DEP_FILES = // :loop s/\\\\$// p n /\\\\$/ b loop p } /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`echo "$file" | sed -e 's|/[^/]*$||'` $ac_aux_dir/mkinstalldirs "$dirpart/$fdir" > /dev/null 2>&1 # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done exit 0 EOF chmod +x $CONFIG_STATUS rm -fr confdefs* $ac_clean_files test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 uucp-1.07/configure.in0000664000076400007640000006006507665532041010440 dnl Taylor UUCP configuration file dnl This should be processed with autoconf to produce a configure script. dnl Copyright (c) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor dnl AC_REVISION([$Id: configure.in,v 1.99 2002/03/07 17:56:41 ian Rel $]) AC_INIT(policy.h) AM_INIT_AUTOMAKE(uucp, 1.07) AM_CONFIG_HEADER(config.h) AM_MAINTAINER_MODE dnl AC_ARG_WITH(user, [ --with-user=USERNAME user ID for programs; default uucp], [OWNER=${withval}], [OWNER="uucp"]) AC_DEFINE_UNQUOTED(OWNER, "$OWNER", [user ID for programs; normally uucp]) AC_SUBST(OWNER) dnl AC_ARG_WITH(newconfigdir, [ --with-newconfigdir=DIRNAME new config file directory; default PREFIX/conf/uucp], [NEWCONFIGDIR=${withval}], [NEWCONFIGDIR='${prefix}/conf/uucp']) AC_SUBST(NEWCONFIGDIR) dnl AC_ARG_WITH(oldconfigdir, [ --with-oldconfigdir=DIRNAME old config file directory; default /usr/lib/uucp], [OLDCONFIGDIR=${withval}], [OLDCONFIGDIR="/usr/lib/uucp"]) AC_SUBST(OLDCONFIGDIR) dnl AC_PREFIX_PROGRAM(gcc) AC_PROG_CC AC_PROG_GCC_TRADITIONAL dnl build_warnings="-W -Wall -Wstrict-prototypes -Wmissing-prototypes" AC_ARG_ENABLE(build-warnings, [ --enable-build-warnings Enable build-time compiler warnings if gcc is used], [case "${enableval}" in yes) ;; no) build_warnings="-w";; ,*) t=`echo "${enableval}" | sed -e "s/,/ /g"` build_warnings="${build_warnings} ${t}";; *,) t=`echo "${enableval}" | sed -e "s/,/ /g"` build_warnings="${t} ${build_warnings}";; *) build_warnings=`echo "${enableval}" | sed -e "s/,/ /g"`;; esac if test x"$silent" != x"yes" && test x"$build_warnings" != x""; then echo "Setting warning flags = $build_warnings" 6>&1 fi])dnl WARN_CFLAGS="" if test "x${build_warnings}" != x -a "x$GCC" = xyes ; then WARN_CFLAGS="${build_warnings}" fi AC_SUBST(WARN_CFLAGS) dnl dnl AR was added to the Makefiles to allow building on funny systems. dnl Adding it here allows the environment value of AR at configure time to dnl be cached in config.status and in the Makefiles. AR=${AR-ar} AC_SUBST(AR) AC_PROG_RANLIB AC_ISC_POSIX AC_MINIX AC_AIX AC_CHECK_LIB(sun, getpwnam) AC_C_CONST AC_MSG_CHECKING([for prototypes]) AC_CACHE_VAL(uucp_cv_c_prototypes, [AC_TRY_COMPILE([extern int foo (short); int foo(short i) { return i; }], [int i;], uucp_cv_c_prototypes=yes, uucp_cv_c_prototypes=no)]) AC_MSG_RESULT($uucp_cv_c_prototypes) if test $uucp_cv_c_prototypes = yes; then AC_DEFINE(HAVE_PROTOTYPES, 1, [Whether the compiler supports prototypes]) fi dnl AC_SYS_INTERPRETER POUNDBANG=$ac_cv_sys_interpreter AC_SUBST(POUNDBANG) dnl AC_MSG_CHECKING(for echo) if (PATH= echo test) 2>/dev/null | grep test >/dev/null 2>&1; then echo_program='"echo"' AC_MSG_RESULT(shell builtin) elif test -s /bin/echo; then echo_program="/bin/echo" AC_MSG_RESULT(/bin/echo) else AC_MSG_RESULT(not found) fi AC_DEFINE_UNQUOTED([ECHO_PROGRAM], $echo_program, [echo program--if shell builtin, use "echo"]) AC_PROG_LN_S dnl AC_CHECK_HEADERS(stddef.h stdarg.h string.h strings.h unistd.h stdlib.h) AC_CHECK_HEADERS(limits.h time.h sys/wait.h sys/ioctl.h memory.h termios.h) AC_CHECK_HEADERS(fcntl.h sys/file.h sys/time.h sys/times.h libc.h) AC_CHECK_HEADERS(sysexits.h poll.h tiuser.h xti.h sys/tli.h stropts.h ftw.h) AC_CHECK_HEADERS(glob.h sys/param.h sys/types.tcp.h sys/mount.h sys/vfs.h) AC_CHECK_HEADERS(sys/filsys.h sys/statfs.h sys/dustat.h sys/fs_types.h ustat.h) AC_CHECK_HEADERS(sys/statvfs.h sys/termiox.h) dnl # Under Next 3.2 apparently does not define struct dirent # by default. AC_MSG_CHECKING([for dirent.h]) AC_CACHE_VAL(uucp_cv_header_dirent_h, [AC_TRY_COMPILE([#include ], [struct dirent s;], uucp_cv_header_dirent_h=yes, uucp_cv_header_dirent_h=no)]) AC_MSG_RESULT($uucp_cv_header_dirent_h) if test $uucp_cv_header_dirent_h = yes; then AC_DEFINE(HAVE_DIRENT_H, 1, [Whether you have a which defines struct direct]) fi dnl # Under Next 3.2 apparently does not define struct utimbuf # by default. AC_MSG_CHECKING([for utime.h]) AC_CACHE_VAL(uucp_cv_header_utime_h, [AC_TRY_COMPILE([#include #if HAVE_TIME_H #include #endif #include ], [struct utimbuf s;], uucp_cv_header_utime_h=yes, uucp_cv_header_utime_h=no)]) AC_MSG_RESULT($uucp_cv_header_utime_h) if test $uucp_cv_header_utime_h = yes; then AC_DEFINE(HAVE_UTIME_H, 1, [Whether you have a which defines struct utimbuf]) fi dnl AC_MSG_CHECKING([for sys/select.h]) AC_CACHE_VAL(uucp_cv_header_sys_select_h, [AC_TRY_COMPILE([#include #if HAVE_SYS_TIME_H #include #endif], [int i;], uucp_cv_header_sys_select_h=yes, uucp_cv_header_sys_select_h=no)]) AC_MSG_RESULT($uucp_cv_header_sys_select_h) if test $uucp_cv_header_sys_select_h = yes; then AC_DEFINE(HAVE_SYS_SELECT_H, 1, [Whether you have ]) fi dnl AC_HEADER_MAJOR dnl AC_TYPE_SIGNAL dnl AC_HEADER_STAT dnl AC_HEADER_TIME AC_STRUCT_TM dnl AC_MSG_CHECKING([whether termios.h and sys/ioctl.h may both be included]) AC_CACHE_VAL(uucp_cv_header_termios_and_ioctl, [AC_TRY_COMPILE([#include #include ], [int i;], uucp_cv_header_termios_and_ioctl=yes, uucp_cv_header_termios_and_ioctl=no)]) AC_MSG_RESULT($uucp_cv_header_termios_and_ioctl) if test $uucp_cv_header_termios_and_ioctl = yes; then AC_DEFINE(HAVE_TERMIOS_AND_SYS_IOCTL_H, 1, [Whether and may both be included]) fi dnl AC_MSG_CHECKING(for CBREAK) AC_CACHE_VAL(uucp_cv_decl_cbreak, [AC_TRY_COMPILE([#include ], [int i = CBREAK;], uucp_cv_decl_cbreak=yes, uucp_cv_decl_cbreak=no)]) AC_MSG_RESULT($uucp_cv_decl_cbreak) if test $uucp_cv_decl_cbreak = yes; then AC_DEFINE(HAVE_CBREAK, 1, [Whether CBREAK is defined]) fi dnl AC_MSG_CHECKING(for pid_t in sys/types.h) AC_CACHE_VAL(uucp_cv_decl_pid_t, [AC_TRY_COMPILE([#include ], [pid_t x;], uucp_cv_decl_pid_t=yes, uucp_cv_decl_pid_t=no)]) AC_MSG_RESULT($uucp_cv_decl_pid_t) if test $uucp_cv_decl_pid_t = no; then AC_DEFINE(PID_T, int, [Type to use for pid_t if not defined--typically int]) fi dnl AC_MSG_CHECKING(for uid_t in sys/types.h) AC_CACHE_VAL(uucp_cv_decl_uid_t, [AC_TRY_COMPILE([#include ], [uid_t x;], uucp_cv_decl_uid_t=yes, uucp_cv_decl_uid_t=no)]) AC_MSG_RESULT($uucp_cv_decl_uid_t) if test $uucp_cv_decl_uid_t = no; then AC_DEFINE(UID_T, int, [Type to use for uid_t if not defined--typically int]) fi dnl AC_MSG_CHECKING(for gid_t in sys/types.h) AC_CACHE_VAL(uucp_cv_decl_gid_t, [AC_TRY_COMPILE([#include ], [gid_t x;], uucp_cv_decl_gid_t=yes, uucp_cv_decl_gid_t=no)]) AC_MSG_RESULT($uucp_cv_decl_gid_t) if test $uucp_cv_decl_gid_t = no; then AC_DEFINE(GID_T, int, [Type to use for gid_t if not defined--typically int]) fi dnl AC_MSG_CHECKING(for off_t in sys/types.h) AC_CACHE_VAL(uucp_cv_decl_off_t, [AC_TRY_COMPILE([#include ], [off_t x;], uucp_cv_decl_off_t=yes, uucp_cv_decl_off_t=no)]) AC_MSG_RESULT($uucp_cv_decl_off_t) if test $uucp_cv_decl_off_t = no; then AC_DEFINE(OFF_T, long, [Type to use for off_t if not defined--typically long]) fi dnl dnl On SCO 3.2.2 sig_atomic_t is in but not . AC_MSG_CHECKING(for sig_atomic_t in signal.h) AC_CACHE_VAL(uucp_cv_decl_sig_atomic_t_signal_h, [AC_TRY_COMPILE([#include ], [sig_atomic_t x;], uucp_cv_decl_sig_atomic_t_signal_h=yes, uucp_cv_decl_sig_atomic_t_signal_h=no)]) AC_MSG_RESULT($uucp_cv_decl_sig_atomic_t_signal_h) if test $uucp_cv_decl_sig_atomic_t_signal_h = yes; then AC_DEFINE([HAVE_SIG_ATOMIC_T_IN_SIGNAL_H], 1, [Whether sig_atomic_t is defined in ]) fi dnl AC_MSG_CHECKING(for sig_atomic_t in sys/types.h) AC_CACHE_VAL(uucp_cv_decl_sig_atomic_t_types_h, [AC_TRY_COMPILE([#include ], [sig_atomic_t x;], uucp_cv_decl_sig_atomic_t_types_h=yes, uucp_cv_decl_sig_atomic_t_types_h=no)]) AC_MSG_RESULT($uucp_cv_decl_sig_atomic_t_types_h) if test $uucp_cv_decl_sig_atomic_t_types_h = yes; then AC_DEFINE([HAVE_SIG_ATOMIC_T_IN_TYPES_H], 1, [Whether sig_atomic_t is defined in ]) fi dnl if test $ac_cv_header_stddef_h = yes; then AC_MSG_CHECKING(for size_t in stddef.h) AC_CACHE_VAL(uucp_cv_decl_size_t_stddef_h, [AC_TRY_COMPILE([#include ], [size_t x;], uucp_cv_decl_size_t_stddef_h=yes, uucp_cv_decl_size_t_stddef_h=no)]) AC_MSG_RESULT($uucp_cv_decl_size_t_stddef_h) if test $uucp_cv_decl_size_t_stddef_h = yes; then AC_DEFINE([HAVE_SIZE_T_IN_STDDEF_H], 1, [Whether size_t is defined in ]) fi fi dnl AC_MSG_CHECKING(for size_t in sys/types.h) AC_CACHE_VAL(uucp_cv_decl_size_t_types_h, [AC_TRY_COMPILE([#include ], [size_t x;], uucp_cv_decl_size_t_types_h=yes, uucp_cv_decl_size_t_types_h=no)]) AC_MSG_RESULT($uucp_cv_decl_size_t_types_h) if test $uucp_cv_decl_size_t_types_h = yes; then AC_DEFINE([HAVE_SIZE_T_IN_TYPES_H], 1, [Whether size_t is defined in ]) fi dnl AC_MSG_CHECKING(for time_t in time.h) AC_CACHE_VAL(uucp_cv_decl_time_t_time_h, [AC_TRY_COMPILE([#include ], [time_t i;], uucp_cv_decl_time_t_time_h=yes, uucp_cv_decl_time_t_time_h=no)]) AC_MSG_RESULT($uucp_cv_decl_time_t_time_h) if test $uucp_cv_decl_time_t_time_h = yes; then AC_DEFINE([HAVE_TIME_T_IN_TIME_H], 1, [Whether time_t is defined in ]) fi dnl AC_MSG_CHECKING(time_t in sys/types.h) AC_CACHE_VAL(uucp_cv_decl_time_t_types_h, [AC_TRY_COMPILE([#include ], [time_t i;], uucp_cv_decl_time_t_types_h=yes, uucp_cv_decl_time_t_types_h=no)]) AC_MSG_RESULT($uucp_cv_decl_time_t_types_h) if test $uucp_cv_decl_time_t_types_h = yes; then AC_DEFINE([HAVE_TIME_T_IN_TYPES_H], 1, [Whether time_t is defined in ]) fi dnl AC_MSG_CHECKING(for void) AC_CACHE_VAL(uucp_cv_c_void, [AC_TRY_COMPILE([], [extern void foo (); (void) exit (0);], uucp_cv_c_void=yes, uucp_cv_c_void=no)]) AC_MSG_RESULT($uucp_cv_c_void) if test $uucp_cv_c_void = yes; then AC_DEFINE([HAVE_VOID], 1, [Whether the compiler supports void]) fi dnl AC_MSG_CHECKING(for unsigned char) AC_CACHE_VAL(uucp_cv_c_unsigned_char, [AC_TRY_COMPILE([], [unsigned char i = (unsigned char) -1;], uucp_cv_c_unsigned_char=yes, uucp_cv_c_unsigned_char=no)]) AC_MSG_RESULT($uucp_cv_c_unsigned_char) if test $uucp_cv_c_unsigned_char = yes; then AC_DEFINE([HAVE_UNSIGNED_CHAR], 1, [Whether the compiler supports unsigned char]) fi dnl AC_MSG_CHECKING(for errno declaration) AC_CACHE_VAL(uucp_cv_decl_errno, [AC_TRY_COMPILE([#include ], [int i = errno; errno = 1;], uucp_cv_decl_errno=yes, uucp_cv_decl_errno=no)]) AC_MSG_RESULT($uucp_cv_decl_errno) if test $uucp_cv_decl_errno = yes; then AC_DEFINE([HAVE_ERRNO_DECLARATION], 1, [Whether errno is declared in ]) fi dnl AC_MSG_CHECKING(for TXADDCD) AC_CACHE_VAL(uucp_cv_txaddcd, [AC_TRY_COMPILE([#include ], [int i = (int) TXADDCD;], uucp_cv_txaddcd=yes, uucp_cv_txaddcd=no)]) AC_MSG_RESULT($uucp_cv_txaddcd) if test $uucp_cv_txaddcd = yes; then AC_DEFINE([HAVE_TXADDCD], 1, [Whether TXADDCD is defined]) fi dnl dnl On some systems, memset, memcmp, and memcpy must be called with dnl the right number of arguments. AC_MSG_CHECKING(for memset) AC_CACHE_VAL(ac_cv_func_memset, [AC_TRY_LINK([], [ char *i; int j, k; memset(i, j, k); ], ac_cv_func_memset=yes, ac_cv_func_memset=no)]) AC_MSG_RESULT($ac_cv_func_memset) if test $ac_cv_func_memset = yes; then AC_DEFINE([HAVE_MEMSET], 1, [Whether you have memset]) fi dnl AC_MSG_CHECKING(for memcmp) AC_CACHE_VAL(ac_cv_func_memcmp, [AC_TRY_LINK([], [ char *i, *j; int k; memcmp(i, j, k); ], ac_cv_func_memcmp=yes, ac_cv_func_memcmp=no)]) AC_MSG_RESULT($ac_cv_func_memcmp) if test $ac_cv_func_memcmp = yes; then AC_DEFINE([HAVE_MEMCMP], 1, [Whether you have memcmp]) fi dnl AC_MSG_CHECKING(for memcpy) AC_CACHE_VAL(ac_cv_func_memcpy, [AC_TRY_LINK([], [ char *i, *j; int k; memcpy(i, j, k); ], ac_cv_func_memcpy=yes, ac_cv_func_memcpy=no)]) AC_MSG_RESULT($ac_cv_func_memcpy) if test $ac_cv_func_memcpy = yes; then AC_DEFINE([HAVE_MEMCPY], 1, [Whether you have memcpy]) fi dnl undefine([index]) AC_CHECK_FUNCS(memchr bcopy bcmp bzero) AC_CHECK_FUNCS(strchr strrchr index rindex strerror strtol strtoul strstr) AC_CHECK_FUNCS(strdup strcasecmp strncasecmp stricmp strnicmp) AC_CHECK_FUNCS(bsearch vfprintf) AC_CHECK_FUNCS(remove ftruncate ltrunc rename opendir dup2 waitpid wait4) AC_CHECK_FUNCS(sigsetjmp setret sigaction sigvec sigset) AC_CHECK_FUNCS(sigprocmask sigblock sighold getdtablesize sysconf) AC_CHECK_FUNCS(setpgrp setsid setreuid seteuid gethostname uname) AC_CHECK_FUNCS(gettimeofday ftw glob dev_info getaddrinfo) dnl dnl Check for getline, but try to avoid inappropriate getline dnl functions found on ISC and HP/UX by also checking for getdelim; dnl the version of getline we want is normally implemented by calling dnl getdelim. AC_CHECK_FUNC(getdelim, [AC_CHECK_FUNC(getline, [AC_DEFINE(HAVE_GETLINE, 1, [Define if you have the getline function.])], [LIBOBJS="$LIBOBJS getlin.o"])], [LIBOBJS="$LIBOBJS getlin.o"]) dnl dnl Check for the SCO buggy ftime; the code can cope with the bug, dnl though it would prefer not to, so if we're cross-configuring we dnl accept that ftime exists. AC_CHECK_FUNC(ftime, [AC_MSG_CHECKING(that ftime works correctly) AC_CACHE_VAL(uucp_cv_sys_ftime_ok, [AC_TRY_RUN([ #include #include main () { struct timeb s, slast; int c = 0; ftime (&slast); while (c < 10) { ftime (&s); if (s.time < slast.time || (s.time == slast.time && s.millitm < slast.millitm)) exit (1); if (s.time != slast.time) ++c; slast.time = s.time; slast.millitm = s.millitm; } exit (0); } ], uucp_cv_sys_ftime_ok=yes, uucp_cv_sys_ftime_ok=no, uucp_cv_sys_ftime_ok=runtime)]) case $uucp_cv_sys_ftime_ok in yes) AC_MSG_RESULT(yes) ;; no) AC_MSG_RESULT(no) AC_MSG_WARN(ftime seems to be buggy) ;; runtime) AC_MSG_RESULT(will check at run time) ;; esac ]) if test $uucp_cv_sys_ftime_ok = yes || test $uucp_cv_sys_ftime_ok = runtime; then AC_DEFINE(HAVE_FTIME, 1, [Whether you have ftime]) fi dnl AC_CHECK_FUNCS(times) AC_CHECK_FUNCS(napms nap usleep poll select) if test $ac_cv_func_napms != yes \ && test $ac_cv_func_nap != yes \ && test $ac_cv_func_usleep != yes \ && test $ac_cv_func_poll != yes \ && test $ac_cv_func_select != yes; then AC_MSG_WARN(No way to sleep for less than one second) AC_MSG_WARN(\p in chat scripts will sleep for a full second) fi dnl AC_CHECK_FUNCS(getgrent) dnl case $LIBS in *-lnsl*) ;; *) AC_CHECK_LIB(nsl_s, main) ;; esac case $LIBS in *-lnsl*) ;; *) AC_CHECK_LIB(nsl, main) ;; esac case $LIBS in *-lsocket*) ;; *) AC_CHECK_LIB(socket, socket) ;; esac case $LIBS in *-lxti*) ;; *) AC_CHECK_LIB(xti, t_open);; esac AC_CHECK_FUNCS(socket t_open) dnl AC_CHECK_FUNCS(getcwd getwd) if test $ac_cv_func_getcwd != yes \ && test $ac_cv_func_getwd != yes; then UNIXOBJS="$UNIXOBJS getcwd.o" if test -s /bin/pwd; then AC_DEFINE([PWD_PROGRAM], "/bin/pwd", [Program to print working directory]) fi fi dnl AC_CHECK_FUNCS(mkdir) AM_CONDITIONAL(HAVE_MKDIR, [test $ac_cv_func_mkdir = yes]) if test $ac_cv_func_mkdir != yes; then UNIXOBJS="$UNIXOBJS mkdir.o" if test -s /bin/mkdir; then AC_DEFINE([MKDIR_PROGRAM], "/bin/mkdir", [Program to make a directory]) fi fi dnl AC_CHECK_FUNCS(rmdir) if test $ac_cv_func_rmdir != yes; then UNIXOBJS="$UNIXOBJS rmdir.o" if test -s /bin/rmdir; then AC_DEFINE([RMDIR_PROGRAM], "/bin/rmdir", [Program to remove a directory]) fi fi dnl dnl Figure out which functions we need from lib subdirectory if test $ac_cv_func_bsearch != yes; then LIBOBJS="$LIBOBJS bsrch.o" fi if test $ac_cv_func_bzero != yes \ && test $ac_cv_func_memset != yes; then LIBOBJS="$LIBOBJS bzero.o" fi if test $ac_cv_func_memchr != yes; then LIBOBJS="$LIBOBJS memchr.o" fi if test $ac_cv_func_memcmp != yes \ && test $ac_cv_func_bcmp != yes; then LIBOBJS="$LIBOBJS memcmp.o" fi if test $ac_cv_func_memcpy != yes \ && test $ac_cv_func_bcopy != yes; then LIBOBJS="$LIBOBJS memcpy.o" fi if test $ac_cv_func_strcasecmp != yes \ && test $ac_cv_func_stricmp != yes; then LIBOBJS="$LIBOBJS strcas.o" fi if test $ac_cv_func_strchr != yes \ && test $ac_cv_func_index != yes; then LIBOBJS="$LIBOBJS strchr.o" fi if test $ac_cv_func_strdup != yes; then LIBOBJS="$LIBOBJS strdup.o" fi if test $ac_cv_func_strncasecmp != yes \ && test $ac_cv_func_strnicmp != yes; then LIBOBJS="$LIBOBJS strncs.o" fi if test $ac_cv_func_strrchr != yes \ && test $ac_cv_func_rindex != yes; then LIBOBJS="$LIBOBJS strrch.o" fi if test $ac_cv_func_strstr != yes; then LIBOBJS="$LIBOBJS strstr.o" fi if test $ac_cv_func_strtol != yes; then LIBOBJS="$LIBOBJS strtol.o" fi if test $ac_cv_func_strtoul != yes; then LIBOBJS="$LIBOBJS strtou.o" fi AC_SUBST(LIBOBJS)dnl dnl Figure out which functions we need from unix subdirectory if test $ac_cv_func_opendir != yes; then UNIXOBJS="$UNIXOBJS dirent.o" fi if test $ac_cv_func_dup2 != yes; then UNIXOBJS="$UNIXOBJS dup2.o" fi if test $ac_cv_func_ftw != yes; then UNIXOBJS="$UNIXOBJS ftw.o" fi if test $ac_cv_func_remove != yes; then UNIXOBJS="$UNIXOBJS remove.o" fi if test $ac_cv_func_rename != yes; then UNIXOBJS="$UNIXOBJS rename.o" fi if test $ac_cv_func_strerror != yes; then UNIXOBJS="$UNIXOBJS strerr.o" fi AC_SUBST(UNIXOBJS) dnl if test $ac_cv_func_sigvec = yes; then AC_MSG_CHECKING(for sv_flags) AC_CACHE_VAL(uucp_cv_struct_sv_flags, [AC_TRY_COMPILE([#include ], [struct sigvec s; s.sv_flags = 0;], uucp_cv_struct_sv_flags=yes, uucp_cv_struct_sv_flags=no)]) AC_MSG_RESULT($uucp_cv_struct_sv_flags) if test $uucp_cv_struct_sv_flags = yes; then AC_DEFINE([HAVE_SIGVEC_SV_FLAGS], 1, [Whether struct sigvec has sv_flags member]) fi fi dnl The filesystem info code is from the GNU fileutils 3.12 package. AC_CHECKING(how to get filesystem space usage) space=no # Here we'll compromise a little (and perform only the link test) # since it seems there are no variants of the statvfs function. if test $space = no; then # SVR4 AC_CHECK_FUNCS(statvfs) if test $ac_cv_func_statvfs = yes; then space=yes AC_DEFINE(STAT_STATVFS, 1, [Whether you have statvfs]) fi fi if test $space = no; then # DEC Alpha running OSF/1 AC_MSG_CHECKING([for 3-argument statfs function (DEC OSF/1)]) AC_CACHE_VAL(uucp_cv_sys_stat_statfs3_osf1, [AC_TRY_RUN([ #include #include #include main () { struct statfs fsd; fsd.f_fsize = 0; exit (statfs (".", &fsd, sizeof (struct statfs))); }], uucp_cv_sys_stat_statfs3_osf1=yes, uucp_cv_sys_stat_statfs3_osf1=no, uucp_cv_sys_stat_statfs3_osf1=no)]) AC_MSG_RESULT($uucp_cv_sys_stat_statfs3_osf1) if test $uucp_cv_sys_stat_statfs3_osf1 = yes; then space=yes AC_DEFINE(STAT_STATFS3_OSF1, 1, [Whether you have 3 argument statfs]) fi fi if test $space = no; then # AIX AC_MSG_CHECKING([for two-argument statfs with statfs.bsize dnl member (AIX, 4.3BSD)]) AC_CACHE_VAL(uucp_cv_sys_stat_statfs2_bsize, [AC_TRY_RUN([ #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_VFS_H #include #endif main () { struct statfs fsd; fsd.f_bsize = 0; exit (statfs (".", &fsd)); }], uucp_cv_sys_stat_statfs2_bsize=yes, uucp_cv_sys_stat_statfs2_bsize=no, uucp_cv_sys_stat_statfs2_bsize=no)]) AC_MSG_RESULT($uucp_cv_sys_stat_statfs2_bsize) if test $uucp_cv_sys_stat_statfs2_bsize = yes; then space=yes AC_DEFINE(STAT_STATFS2_BSIZE, 1, [Whether you have two argument statfs with bsize]) fi fi if test $space = no; then # SVR3 AC_MSG_CHECKING([for four-argument statfs (AIX-3.2.5, SVR3)]) AC_CACHE_VAL(uucp_cv_sys_stat_statfs4, [AC_TRY_RUN([#include #include main () { struct statfs fsd; exit (statfs (".", &fsd, sizeof fsd, 0)); }], uucp_cv_sys_stat_statfs4=yes, uucp_cv_sys_stat_statfs4=no, uucp_cv_sys_stat_statfs4=no)]) AC_MSG_RESULT($uucp_cv_sys_stat_statfs4) if test $uucp_cv_sys_stat_statfs4 = yes; then space=yes AC_DEFINE(STAT_STATFS4, 1, [Whether you have four argument statfs]) fi fi if test $space = no; then # 4.4BSD and NetBSD AC_MSG_CHECKING([for two-argument statfs with statfs.fsize dnl member (4.4BSD and NetBSD)]) AC_CACHE_VAL(uucp_cv_sys_stat_statfs2_fsize, [AC_TRY_RUN([#include #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif main () { struct statfs fsd; fsd.f_fsize = 0; exit (statfs (".", &fsd)); }], uucp_cv_sys_stat_statfs2_fsize=yes, uucp_cv_sys_stat_statfs2_fsize=no, uucp_cv_sys_stat_statfs2_fsize=no)]) AC_MSG_RESULT($uucp_cv_sys_stat_statfs2_fsize) if test $uucp_cv_sys_stat_statfs2_fsize = yes; then space=yes AC_DEFINE(STAT_STATFS2_FSIZE, 1, [Whether you have two argument statfs with fsize]) fi fi if test $space = no; then # Ultrix AC_MSG_CHECKING([for two-argument statfs with struct fs_data (Ultrix)]) AC_CACHE_VAL(uucp_cv_sys_stat_fs_data, [AC_TRY_RUN([#include #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_FS_TYPES_H #include #endif main () { struct fs_data fsd; /* Ultrix's statfs returns 1 for success, 0 for not mounted, -1 for failure. */ exit (statfs (".", &fsd) != 1); }], uucp_cv_sys_stat_fs_data=yes, uucp_cv_sys_stat_fs_data=no, uucp_cv_sys_stat_fs_data=no)]) AC_MSG_RESULT($uucp_cv_sys_stat_fs_data) if test $uucp_cv_sys_stat_fs_data = yes; then space=yes AC_DEFINE(STAT_STATFS2_FS_DATA, 1, [Whether you have two argument statfs with fd_req]) fi fi if test $space = no; then AC_CHECK_FUNCS(ustat) if test $ac_cv_func_ustat = yes; then space=yes AC_DEFINE(STAT_USTAT, 1, [Whether you have ustat]) fi fi if test $space = no; then # QNX AC_CHECK_FUNCS(disk_space) if test $ac_cv_func_disk_space = yes; then space=yes AC_DEFINE(STAT_DISK_SPACE, 1, [Whether you have disk_space]) fi fi dnl See whether we can make an extern declaration AC_DEFUN(UUCP_CHECK_DECLARATION, [AC_MSG_CHECKING(for $1 declared as $2) AC_CACHE_VAL(uucp_cv_decl_$1, [AC_TRY_COMPILE([#include #include #include #ifdef HAVE_LIBC_H #include #endif #ifdef HAVE_SYS_TIMES_H #include #endif extern $2 $1 ();], [int i = 0;], eval "uucp_cv_decl_$1=yes", eval "uucp_cv_decl_$1=no")]) if eval "test \"`echo '$uucp_cv_decl_'$1`\" = yes"; then AC_MSG_RESULT(yes) AC_DEFINE_UNQUOTED(translit($1, [a-z], [A-Z])_DECLARATION_OK, 1, [Whether $1 is declared as $2]) else AC_MSG_RESULT(no) fi ]) dnl UUCP_CHECK_DECLARATION(times, long) UUCP_CHECK_DECLARATION(getpwnam, struct passwd *) UUCP_CHECK_DECLARATION(getpwuid, struct passwd *) UUCP_CHECK_DECLARATION(getgrent, struct group *) dnl AC_MSG_CHECKING(for BSD setpgrp) AC_CACHE_VAL(uucp_cv_decl_setpgrp, [AC_TRY_COMPILE([#ifdef HAVE_UNISTD_H #include #endif], [getpgrp (0); setpgrp (0, 0);], uucp_cv_decl_setpgrp=yes, uucp_cv_decl_setpgrp=no)]) AC_MSG_RESULT($uucp_cv_decl_setpgrp) if test $uucp_cv_decl_setpgrp = yes; then AC_DEFINE([HAVE_BSD_PGRP], 1, [Whether you have BSD style setpgrp]) fi dnl AC_MSG_CHECKING(for union wait) AC_CACHE_VAL(uucp_cv_struct_wait, [AC_TRY_COMPILE([#include #ifndef WIFEXITED #define WIFEXITED(u) ((u).w_termsig == 0) #endif], [union wait u; if (WIFEXITED (u)) wait (&u);], uucp_cv_struct_wait=yes, uucp_cv_struct_wait=no)]) AC_MSG_RESULT($uucp_cv_struct_wait) if test $uucp_cv_struct_wait = yes; then AC_DEFINE([HAVE_UNION_WAIT], 1, [Whether you have union wait]) fi dnl AC_MSG_CHECKING(for struct sockaddr_storage) AC_CACHE_VAL(uucp_cv_struct_sockaddrstorage, [AC_TRY_COMPILE([#include ], [struct sockaddr_storage s;], uucp_cv_struct_sockaddrstorage=yes, uucp_cv_struct_sockaddrstorage=no)]) AC_MSG_RESULT($uucp_cv_struct_sockaddrstorage) if test $uucp_cv_struct_sockaddrstorage = yes; then AC_DEFINE([HAVE_STRUCT_SOCKADDR_STORAGE], 1, [Whether you have struct sockaddr_storage]) fi dnl if test "$cross_compiling" = yes; then AC_DEFINE([HAVE_LONG_FILE_NAMES], [0]) AC_DEFINE([HAVE_RESTARTABLE_SYSCALLS], [-1]) else AC_SYS_RESTARTABLE_SYSCALLS AC_SYS_LONG_FILE_NAMES fi dnl AC_OUTPUT(Makefile uuconf/Makefile lib/Makefile unix/Makefile) uucp-1.07/depcomp0000775000076400007640000002752507665321755007520 #! /bin/sh # depcomp - compile a program generating dependencies as side-effects # Copyright 1999, 2000 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. # 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. # Originally written by Alexandre Oliva . if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # `libtool' can also be set to `yes' or `no'. depfile=${depfile-`echo "$object" | sed 's,\([^/]*\)$,.deps/\1,;s/\.\([^.]*\)$/.P\1/'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ## The second -e expression handles DOS-style file names with drive letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the `deleted header file' problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. tr ' ' ' ' < "$tmpdepfile" | ## Some versions of gcc put a space before the `:'. On the theory ## that the space means something, we add a space to the output as ## well. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like `#:fec' to the end of the # dependency line. tr ' ' ' ' < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ tr ' ' ' ' >> $depfile echo >> $depfile # The second pass generates a dummy entry for each header file. tr ' ' ' ' < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> $depfile else # The sourcefile does not contain any dependencies, so just # store a dummy comment line, to avoid errors with the Makefile # "include basename.Plo" scheme. echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. This file always lives in the current directory. # Also, the AIX compiler puts `$object:' at the start of each line; # $object doesn't have directory information. stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'` tmpdepfile="$stripped.u" outname="$stripped.o" if test "$libtool" = yes; then "$@" -Wc,-M else "$@" -M fi stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi if test -f "$tmpdepfile"; then # Each line is of the form `foo.o: dependent.h'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" else # The sourcefile does not contain any dependencies, so just # store a dummy comment line, to avoid errors with the Makefile # "include basename.Plo" scheme. echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" ;; tru64) # The Tru64 AIX compiler uses -MD to generate dependencies as a side # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in `foo.d' instead, so we check for that too. # Subdirectories are respected. tmpdepfile1="$object.d" tmpdepfile2=`echo "$object" | sed -e 's/.o$/.d/'` if test "$libtool" = yes; then "$@" -Wc,-MD else "$@" -MD fi stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi if test -f "$tmpdepfile1"; then tmpdepfile="$tmpdepfile1" else tmpdepfile="$tmpdepfile2" fi if test -f "$tmpdepfile"; then sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" # That's a space and a tab in the []. sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" else echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the proprocessed file to stdout, regardless of -o, # because we must use -o when running libtool. test -z "$dashmflag" && dashmflag=-M ( IFS=" " case " $* " in *" --mode=compile "*) # this is libtool, let us make it quiet for arg do # cycle over the arguments case "$arg" in "--mode=compile") # insert --quiet before "--mode=compile" set fnord "$@" --quiet shift # fnord ;; esac set fnord "$@" "$arg" shift # fnord shift # "$arg" done ;; esac "$@" $dashmflag | sed 's:^[^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" ) & proc=$! "$@" stat=$? wait "$proc" if test "$stat" != 0; then exit $stat; fi rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" tr ' ' ' ' < "$tmpdepfile" | \ ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) # X makedepend ( shift cleared=no for arg in "$@"; do case $cleared in no) set ""; shift cleared=yes esac case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift;; -*) ;; *) set fnord "$@" "$arg"; shift;; esac done obj_suffix="`echo $object | sed 's/^.*\././'`" touch "$tmpdepfile" ${MAKEDEPEND-makedepend} 2>/dev/null -o"$obj_suffix" -f"$tmpdepfile" "$@" ) & proc=$! "$@" stat=$? wait "$proc" if test "$stat" != 0; then exit $stat; fi rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" tail +3 "$tmpdepfile" | tr ' ' ' ' | \ ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the proprocessed file to stdout, regardless of -o, # because we must use -o when running libtool. ( IFS=" " case " $* " in *" --mode=compile "*) for arg do # cycle over the arguments case $arg in "--mode=compile") # insert --quiet before "--mode=compile" set fnord "$@" --quiet shift # fnord ;; esac set fnord "$@" "$arg" shift # fnord shift # "$arg" done ;; esac "$@" -E | sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | sed '$ s: \\$::' > "$tmpdepfile" ) & proc=$! "$@" stat=$? wait "$proc" if test "$stat" != 0; then exit $stat; fi rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the proprocessed file to stdout, regardless of -o, # because we must use -o when running libtool. ( IFS=" " case " $* " in *" --mode=compile "*) for arg do # cycle over the arguments case $arg in "--mode=compile") # insert --quiet before "--mode=compile" set fnord "$@" --quiet shift # fnord ;; esac set fnord "$@" "$arg" shift # fnord shift # "$arg" done ;; esac "$@" -E | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" ) & proc=$! "$@" stat=$? wait "$proc" if test "$stat" != 0; then exit $stat; fi rm -f "$depfile" echo "$object : \\" > "$depfile" . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" echo " " >> "$depfile" . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 uucp-1.07/install-sh0000775000076400007640000001273607665321755010145 #!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 uucp-1.07/missing0000775000076400007640000002123107665321755007526 #! /bin/sh # Common stub for a few missing GNU programs while installing. # Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc. # Originally by Fran,cois Pinard , 1996. # 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. # 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. if test $# -eq 0; then echo 1>&2 "Try \`$0 --help' for more information" exit 1 fi run=: # In the cases where this matters, `missing' is being run in the # srcdir already. if test -f configure.ac; then configure_ac=configure.ac else configure_ac=configure.in fi case "$1" in --run) # Try to run requested program, and just exit if it succeeds. run= shift "$@" && exit 0 ;; esac # If it does not exist, or fails to run (possibly an outdated version), # try to emulate it. case "$1" in -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an error status if there is no known handling for PROGRAM. Options: -h, --help display this help and exit -v, --version output version information and exit --run try to run the given command, and emulate it if it fails Supported PROGRAM values: aclocal touch file \`aclocal.m4' autoconf touch file \`configure' autoheader touch file \`config.h.in' automake touch all \`Makefile.in' files bison create \`y.tab.[ch]', if possible, from existing .[ch] flex create \`lex.yy.c', if possible, from existing .c help2man touch the output file lex create \`lex.yy.c', if possible, from existing .c makeinfo touch the output file tar try tar, gnutar, gtar, then tar without non-portable flags yacc create \`y.tab.[ch]', if possible, from existing .[ch]" ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing 0.3 - GNU automake" ;; -*) echo 1>&2 "$0: Unknown \`$1' option" echo 1>&2 "Try \`$0 --help' for more information" exit 1 ;; aclocal) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`acinclude.m4' or \`${configure_ac}'. You might want to install the \`Automake' and \`Perl' packages. Grab them from any GNU archive site." touch aclocal.m4 ;; autoconf) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." touch configure ;; autoheader) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`acconfig.h' or \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` test -z "$files" && files="config.h" touch_files= for f in $files; do case "$f" in *:*) touch_files="$touch_files "`echo "$f" | sed -e 's/^[^:]*://' -e 's/:.*//'`;; *) touch_files="$touch_files $f.in";; esac done touch $touch_files ;; automake) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. You might want to install the \`Automake' and \`Perl' packages. Grab them from any GNU archive site." find . -type f -name Makefile.am -print | sed 's/\.am$/.in/' | while read f; do touch "$f"; done ;; bison|yacc) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a \`.y' file. You may need the \`Bison' package in order for those modifications to take effect. You can get \`Bison' from any GNU archive site." rm -f y.tab.c y.tab.h if [ $# -ne 1 ]; then eval LASTARG="\${$#}" case "$LASTARG" in *.y) SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` if [ -f "$SRCFILE" ]; then cp "$SRCFILE" y.tab.c fi SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` if [ -f "$SRCFILE" ]; then cp "$SRCFILE" y.tab.h fi ;; esac fi if [ ! -f y.tab.h ]; then echo >y.tab.h fi if [ ! -f y.tab.c ]; then echo 'main() { return 0; }' >y.tab.c fi ;; lex|flex) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a \`.l' file. You may need the \`Flex' package in order for those modifications to take effect. You can get \`Flex' from any GNU archive site." rm -f lex.yy.c if [ $# -ne 1 ]; then eval LASTARG="\${$#}" case "$LASTARG" in *.l) SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` if [ -f "$SRCFILE" ]; then cp "$SRCFILE" lex.yy.c fi ;; esac fi if [ ! -f lex.yy.c ]; then echo 'main() { return 0; }' >lex.yy.c fi ;; help2man) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a dependency of a manual page. You may need the \`Help2man' package in order for those modifications to take effect. You can get \`Help2man' from any GNU archive site." file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` if test -z "$file"; then file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` fi if [ -f "$file" ]; then touch $file else test -z "$file" || exec >$file echo ".ab help2man is required to generate this page" exit 1 fi ;; makeinfo) if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then # We have makeinfo, but it failed. exit 1 fi echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a \`.texi' or \`.texinfo' file, or any other file indirectly affecting the aspect of the manual. The spurious call might also be the consequence of using a buggy \`make' (AIX, DU, IRIX). You might want to install the \`Texinfo' package or the \`GNU make' package. Grab either from any GNU archive site." file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` if test -z "$file"; then file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` fi touch $file ;; tar) shift if test -n "$run"; then echo 1>&2 "ERROR: \`tar' requires --run" exit 1 fi # We have already tried tar in the generic part. # Look for gnutar/gtar before invocation to avoid ugly error # messages. if (gnutar --version > /dev/null 2>&1); then gnutar ${1+"$@"} && exit 0 fi if (gtar --version > /dev/null 2>&1); then gtar ${1+"$@"} && exit 0 fi firstarg="$1" if shift; then case "$firstarg" in *o*) firstarg=`echo "$firstarg" | sed s/o//` tar "$firstarg" ${1+"$@"} && exit 0 ;; esac case "$firstarg" in *h*) firstarg=`echo "$firstarg" | sed s/h//` tar "$firstarg" ${1+"$@"} && exit 0 ;; esac fi echo 1>&2 "\ WARNING: I can't seem to be able to run \`tar' with the given arguments. You may want to install GNU tar or Free paxutils, or check the command line arguments." exit 1 ;; *) echo 1>&2 "\ WARNING: \`$1' is needed, and you do not seem to have it handy on your system. You might have modified some files without having the proper tools for further handling them. Check the \`README' file, it often tells you about the needed prerequirements for installing this package. You may also peek at any GNU archive site, in case some other package would contain this missing \`$1' program." exit 1 ;; esac exit 0 uucp-1.07/mkinstalldirs0000775000076400007640000000132107665321755010733 #! /bin/sh # mkinstalldirs --- make directory hierarchy # Author: Noah Friedman # Created: 1993-05-16 # Public domain # $Id: mkinstalldirs,v 1.1 2002/01/16 10:02:34 ian Rel $ errstatus=0 for file do set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` shift pathcomp= for d do pathcomp="$pathcomp$d" case "$pathcomp" in -* ) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr fi fi pathcomp="$pathcomp/" done done exit $errstatus # mkinstalldirs ends here uucp-1.07/texinfo.tex0000664000076400007640000062214307665321756010337 % 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{2000-12-12.07} % % Copyright (C) 1985, 86, 88, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 2000 % 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., 59 Temple Place - Suite 330, % Boston, MA 02111-1307, USA. % % In other words, you are welcome to use, share and improve this program. % You are forbidden to forbid anyone else to use, share and improve % what you give them. Help stamp out software-hoarding! % % Please try the latest version of texinfo.tex before submitting bug % reports; you can get the latest version from: % ftp://ftp.gnu.org/gnu/texinfo.tex % (and all GNU mirrors, see http://www.gnu.org/order/ftp.html) % ftp://texinfo.org/tex/texinfo.tex % ftp://us.ctan.org/macros/texinfo/texinfo.tex % (and all CTAN mirrors, finger ctan@us.ctan.org for a list). % /home/gd/gnu/doc/texinfo.tex on the GNU machines. % The texinfo.tex in any given Texinfo distribution could well be out % of date, so if that's what you're using, please check. % Texinfo has a small home page at http://texinfo.org/. % % 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, to process the dvi file; this makes foo.ps. % The extra runs of TeX 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. You can get % the existing language-specific files from ftp://ftp.gnu.org/gnu/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} % Save some parts of plain tex 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\ptexi=\i \let\ptexlbrace=\{ \let\ptexrbrace=\} \let\ptexstar=\* \let\ptext=\t % We never want plain's outer \+ definition in Texinfo. % For @tex, we can use \tabalign. \let\+ = \relax \message{Basics,} \chardef\other=12 % If this character appears in an error message or help string, it % starts a new line in the output. \newlinechar = `^^J % 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\putwordDeftypevar\undefined\gdef\putwordDeftypevar{Variable}\fi \ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi \ifx\putwordDeftypefun\undefined\gdef\putwordDeftypefun{Function}\fi % Ignore a token. % \def\gobble#1{} \hyphenation{ap-pen-dix} \hyphenation{mini-buf-fer mini-buf-fers} \hyphenation{eshell} \hyphenation{white-space} % Margin to add to right of even pages, to left of odd pages. \newdimen \bindingoffset \newdimen \normaloffset \newdimen\pagewidth \newdimen\pageheight % 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. % \def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% \ifx\eTeXversion\undefined \def\loggingall{\tracingcommands2 \tracingstats2 \tracingpages1 \tracingoutput1 \tracinglostchars1 \tracingmacros2 \tracingparagraphs1 \tracingrestores1 \showboxbreadth\maxdimen\showboxdepth\maxdimen }% \else \def\loggingall{\tracingcommands3 \tracingstats2 \tracingpages1 \tracingoutput1 \tracinglostchars1 \tracingmacros2 \tracingparagraphs1 \tracingrestores1 \tracingscantokens1 \tracingassigns1 \tracingifs1 \tracinggroups1 \tracingnesting2 \showboxbreadth\maxdimen\showboxdepth\maxdimen }% \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. % \escapechar = `\\ % use backslash in output files. \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. \shipout\vbox{% % Do this early so pdf references go to the beginning of the page. \ifpdfmakepagedest \pdfmkdest{\the\pageno} \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 \oddfootingxxx.) % The \baselineskip=24pt in plain's \makefootline has no effect. \vskip 2\baselineskip \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 \turnoffactive \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#1{% \let\next = #1% \begingroup \obeylines \futurelet\temp\parseargx } % If the next token is an obeyed space (from an @example environment or % the like), remove it and recurse. Otherwise, we're done. \def\parseargx{% % \obeyedspace is defined far below, after the definition of \sepspaces. \ifx\obeyedspace\temp \expandafter\parseargdiscardspace \else \expandafter\parseargline \fi } % Remove a single space (as the delimiter token to the macro call). {\obeyspaces % \gdef\parseargdiscardspace {\futurelet\temp\parseargx}} {\obeylines % \gdef\parseargline#1^^M{% \endgroup % End of the group started in \parsearg. % % First remove any @c comment, then any @comment. % Result of each macro is put in \toks0. \argremovec #1\c\relax % \expandafter\argremovecomment \the\toks0 \comment\relax % % % Call the caller's macro, saved as \next in \parsearg. \expandafter\next\expandafter{\the\toks0}% }% } % Since all \c{,omment} does is throw away the argument, we can let TeX % do that for us. The \relax here is matched by the \relax in the call % in \parseargline; it could be more or less anything, its purpose is % just to delimit the argument to the \c. \def\argremovec#1\c#2\relax{\toks0 = {#1}} \def\argremovecomment#1\comment#2\relax{\toks0 = {#1}} % \argremovec{,omment} might leave us with trailing spaces, though; e.g., % @end itemize @c foo % will have two active spaces as part of the argument with the % `itemize'. Here we remove all active spaces from #1, and assign the % result to \toks0. % % This loses if there are any *other* active characters besides spaces % in the argument -- _ ^ +, for example -- since they get expanded. % Fortunately, Texinfo does not define any such commands. (If it ever % does, the catcode of the characters in questionwill have to be changed % here.) But this means we cannot call \removeactivespaces as part of % \argremovec{,omment}, since @c uses \parsearg, and thus the argument % that \parsearg gets might well have any character at all in it. % \def\removeactivespaces#1{% \begingroup \ignoreactivespaces \edef\temp{#1}% \global\toks0 = \expandafter{\temp}% \endgroup } % Change the active space to expand to nothing. % \begingroup \obeyspaces \gdef\ignoreactivespaces{\obeyspaces\let =\empty} \endgroup \def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} %% These are used to keep @begin/@end levels from running away %% Call \inENV within environments (after a \begingroup) \newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi} \def\ENVcheck{% \ifENV\errmessage{Still within an environment; press RETURN to continue} \endgroup\fi} % This is not perfect, but it should reduce lossage % @begin foo is the same as @foo, for now. \newhelp\EMsimple{Press RETURN to continue.} \outer\def\begin{\parsearg\beginxxx} \def\beginxxx #1{% \expandafter\ifx\csname #1\endcsname\relax {\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else \csname #1\endcsname\fi} % @end foo executes the definition of \Efoo. % \def\end{\parsearg\endxxx} \def\endxxx #1{% \removeactivespaces{#1}% \edef\endthing{\the\toks0}% % \expandafter\ifx\csname E\endthing\endcsname\relax \expandafter\ifx\csname \endthing\endcsname\relax % There's no \foo, i.e., no ``environment'' foo. \errhelp = \EMsimple \errmessage{Undefined command `@end \endthing'}% \else \unmatchedenderror\endthing \fi \else % Everything's ok; the right environment has been started. \csname E\endthing\endcsname \fi } % There is an environment #1, but it hasn't been started. Give an error. % \def\unmatchedenderror#1{% \errhelp = \EMsimple \errmessage{This `@end #1' doesn't have a matching `@#1'}% } % Define the control sequence \E#1 to give an unmatched @end error. % \def\defineunmatchedend#1{% \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}% } % Single-spacing is done by various environments (specifically, in % \nonfillstart and \quotations). \newskip\singlespaceskip \singlespaceskip = 12.5pt \def\singlespace{% % Why was this kern here? It messes up equalizing space above and below % environments. --karl, 6may93 %{\advance \baselineskip by -\singlespaceskip %\kern \baselineskip}% \setleading \singlespaceskip } %% 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 actual \{ & \} command in an index. \catcode`\{ = 12 \catcode`\} = 12 \catcode`\[ = 1 \catcode`\] = 2 \catcode`\@ = 0 \catcode`\\ = 12 @gdef@lbracecmd[\{]% @gdef@rbracecmd[\}]% @endgroup % Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent % Others are defined by plain TeX: @` @' @" @^ @~ @= @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 % Plain TeX defines: @AA @AE @O @OE @L (and lowercase versions) @ss. \def\questiondown{?`} \def\exclamdown{!`} % 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 } % 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} % @. is an end-of-sentence period. \def\.{.\spacefactor=3000 } % @! is an end-of-sentence bang. \def\!{!\spacefactor=3000 } % @? is an end-of-sentence query. \def\?{?\spacefactor=3000 } % @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. % \def\group{\begingroup \ifnum\catcode13=\active \else \errhelp = \groupinvalidhelp \errmessage{@group invalid in context where filling is enabled}% \fi % % The \vtop we start below 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. (See p.82 of % the TeXbook.) Thus, space below is not quite equal to space % above. But it's pretty close. \def\Egroup{% \egroup % End the \vtop. \endgroup % End the \group. }% % \vtop\bgroup % We have to put a strut on the last line in case the @group is in % the midst of an example, rather than completely enclosing it. % Otherwise, the interline space between the last line of the group % and the first line afterwards is too small. But we can't put the % strut in \Egroup, since there it would be on a line by itself. % Hence this just inserts a strut at the beginning of each line. \everypar = {\strut}% % % Since we have a strut on every line, we don't need any of TeX's % normal interline spacing. \offinterlineskip % % OK, but now we have to do something about blank % lines in the input in @example-like environments, which normally % just turn into \lisppar, which will insert no space now that we've % turned off the interline space. Simplest is to make them be an % empty paragraph. \ifx\par\lisppar \edef\par{\leavevmode \par}% % % Reset ^^M's definition to new definition of \par. \obeylines \fi % % 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 } % % 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 \def\need{\parsearg\needx} % Old definition--didn't work. %\def\needx #1{\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 %}} \def\needx#1{% % 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 \let\br = \par % @dots{} output an ellipsis using the current font. % We do .5em per period so that it has the same spacing in a typewriter % font as three actual period characters. % \def\dots{% \leavevmode \hbox to 1.5em{% \hskip 0pt plus 0.25fil minus 0.25fil .\hss.\hss.% \hskip 0pt plus 0.5fil minus 0.5fil }% } % @enddots{} is an end-of-sentence ellipsis. % \def\enddots{% \leavevmode \hbox to 2em{% \hskip 0pt plus 0.25fil minus 0.25fil .\hss.\hss.\hss.% \hskip 0pt plus 0.5fil minus 0.5fil }% \spacefactor=3000 } % @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. \def\exdent{\parsearg\exdentyyy} \def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}} % This defn is used inside nofill environments such as @example. \def\nofillexdent{\parsearg\nofillexdentyyy} \def\nofillexdentyyy #1{{\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. % Allow normal characters that we make active in the argument (a file name). \def\include{\begingroup \catcode`\\=12 \catcode`~=12 \catcode`^=12 \catcode`_=12 \catcode`|=12 \catcode`<=12 \catcode`>=12 \catcode`+=12 \parsearg\includezzz} % Restore active chars for included file. \def\includezzz#1{\endgroup\begingroup % Read the included file in a group so nested @include's work. \def\thisfile{#1}% \input\thisfile \endgroup} \def\thisfile{} % @center line outputs that line, centered \def\center{\parsearg\centerzzz} \def\centerzzz #1{{\advance\hsize by -\leftskip \advance\hsize by -\rightskip \centerline{#1}}} % @sp n outputs n lines of vertical space \def\sp{\parsearg\spxxx} \def\spxxx #1{\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. % We cannot implement @paragraphindent asis, though. % \def\asisword{asis} % no translation, these are keywords \def\noneword{none} % \def\paragraphindent{\parsearg\doparagraphindent} \def\doparagraphindent#1{% \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. \def\exampleindent{\parsearg\doexampleindent} \def\doexampleindent#1{% \def\temp{#1}% \ifx\temp\asisword \else \ifx\temp\noneword \lispnarrowing = 0pt \else \lispnarrowing = #1em \fi \fi } % @asis just yields its argument. Used with @table, for example. % \def\asis#1{#1} % @math means output in math mode. % We don't use $'s directly in the definition of \math because control % sequences like \math are expanded when the toc file is written. Then, % we read the toc file back, the $'s will be normal characters (as they % should be, according to the definition of Texinfo). So we must use a % control sequence to switch into and out of math mode. % % This isn't quite enough for @math to work properly in indices, but it % seems unlikely it will ever be needed there. % \let\implicitmath = $ \def\math#1{\implicitmath #1\implicitmath} % @bullet and @minus need the same treatment as @math, just above. \def\bullet{\implicitmath\ptexbullet\implicitmath} \def\minus{\implicitmath-\implicitmath} % @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{% \iflinks \readauxfile \fi % \openindices needs to do some work in any case. \openindices \fixbackslash % Turn off hack to swallow `\input texinfo'. \global\let\setfilename=\comment % Ignore extra @setfilename cmds. % % If texinfo.cnf is present on the system, read it. % Useful for site-wide @afourpaper, etc. % Just to be on the safe side, close the input stream before the \input. \openin 1 texinfo.cnf \ifeof1 \let\temp=\relax \else \def\temp{\input texinfo.cnf }\fi \closein1 \temp % \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 \ifx\pdfoutput\undefined \pdffalse \let\pdfmkdest = \gobble \let\pdfurl = \gobble \let\endlink = \relax \let\linkcolor = \relax \let\pdfmakeoutlines = \relax \else \pdftrue \pdfoutput = 1 \input pdfcolor \def\dopdfimage#1#2#3{% \def\imagewidth{#2}% \def\imageheight{#3}% \ifnum\pdftexversion < 14 \pdfimage \else \pdfximage \fi \ifx\empty\imagewidth\else width \imagewidth \fi \ifx\empty\imageheight\else height \imageheight \fi {#1.pdf}% \ifnum\pdftexversion < 14 \else \pdfrefximage \pdflastximage \fi} \def\pdfmkdest#1{\pdfdest name{#1@} xyz} \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 by1 \expandafter\xdef\csname#1\endcsname{\the\tempnum}} \def\pdfmakeoutlines{{% \openin 1 \jobname.toc \ifeof 1\else\bgroup \closein 1 \indexnofonts \def\tt{} \let\_ = \normalunderscore % Thanh's hack / proper braces in bookmarks \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace % \def\chapentry ##1##2##3{} \def\unnumbchapentry ##1##2{} \def\secentry ##1##2##3##4{\advancenumber{chap##2}} \def\unnumbsecentry ##1##2{} \def\subsecentry ##1##2##3##4##5{\advancenumber{sec##2.##3}} \def\unnumbsubsecentry ##1##2{} \def\subsubsecentry ##1##2##3##4##5##6{\advancenumber{subsec##2.##3.##4}} \def\unnumbsubsubsecentry ##1##2{} \input \jobname.toc \def\chapentry ##1##2##3{% \pdfoutline goto name{\pdfmkpgn{##3}}count-\expnumber{chap##2}{##1}} \def\unnumbchapentry ##1##2{% \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} \def\secentry ##1##2##3##4{% \pdfoutline goto name{\pdfmkpgn{##4}}count-\expnumber{sec##2.##3}{##1}} \def\unnumbsecentry ##1##2{% \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} \def\subsecentry ##1##2##3##4##5{% \pdfoutline goto name{\pdfmkpgn{##5}}count-\expnumber{subsec##2.##3.##4}{##1}} \def\unnumbsubsecentry ##1##2{% \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} \def\subsubsecentry ##1##2##3##4##5##6{% \pdfoutline goto name{\pdfmkpgn{##6}}{##1}} \def\unnumbsubsubsecentry ##1##2{% \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} \input \jobname.toc \egroup\fi }} \def\makelinks #1,{% \def\params{#1}\def\E{END}% \ifx\params\E \let\nextmakelinks=\relax \else \let\nextmakelinks=\makelinks \ifnum\lnkcount>0,\fi \picknum{#1}% \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{\the\pgn}}% \linkcolor #1% \advance\lnkcount by 1% \endlink \fi \nextmakelinks } \def\picknum#1{\expandafter\pn#1} \def\pn#1{% \def\p{#1}% \ifx\p\lbrace \let\nextpn=\ppn \else \let\nextpn=\ppnn \def\first{#1} \fi \nextpn } \def\ppn#1{\pgn=#1\gobble} \def\ppnn{\pgn=\first} \def\pdfmklnk#1{\lnkcount=0\makelinks #1,END,} \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} \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 \def\pdfurl#1{% \begingroup \normalturnoffactive\def\@{@}% \leavevmode\Red \startlink attr{/Border [0 0 0]}% user{/Subtype /Link /A << /S /URI /URI (#1) >>}% % #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| \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{\mkpgn{#1}} \linkcolor #1\endlink} \def\mkpgn#1{#1@} \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} \fi % \ifx\pdfoutput \message{fonts,} % Font-change commands. % Texinfo sort of supports the sans serif font style, which plain TeX does not. % So we set up a \sf analogous to plain's \rm, etc. \newfam\sffam \def\sf{\fam=\sffam \tensf} \let\li = \sf % Sometimes we call it \li, not \sf. % We don't need math for this one. \def\ttsl{\tenttsl} % Use Computer Modern fonts at \magstephalf (11pt). \newcount\mainmagstep \mainmagstep=\magstephalf % 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} \ifx\bigger\relax \let\mainmagstep=\magstep1 \setfont\textrm\rmshape{12}{1000} \setfont\texttt\ttshape{12}{1000} \else \setfont\textrm\rmshape{10}{\mainmagstep} \setfont\texttt\ttshape{10}{\mainmagstep} \fi % Instead of cmb10, you many want to use cmbx10. % cmbx10 is a prettier font on its own, but cmb10 % looks better when embedded in a line with cmr10. \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, etc. \setfont\defbf\bxshape{10}{\magstep1} %was 1314 \setfont\deftt\ttshape{10}{\magstep1} \def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf} % Fonts for indices, footnotes, small examples (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 title page: \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} % Chapter (and unnumbered) fonts (17.28pt). \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). \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 % \setfont\ssecrm\bxshape{10}{\magstep1} % This size an font looked bad. % \setfont\ssecit\itshape{10}{\magstep1} % The letters were too crowded. % \setfont\ssecsl\slshape{10}{\magstep1} % \setfont\ssectt\ttshape{10}{\magstep1} % \setfont\ssecsf\sfshape{10}{\magstep1} %\setfont\ssecrm\bfshape{10}{1315} % Note the use of cmb rather than cmbx. %\setfont\ssecit\itshape{10}{1315} % Also, the size is a little larger than %\setfont\ssecsl\slshape{10}{1315} % being scaled magstep1. %\setfont\ssectt\ttshape{10}{1315} %\setfont\ssecsf\sfshape{10}{1315} %\let\ssecbf=\ssecrm % Subsection fonts (13.15pt). \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}{\magstep1} \font\sseci=cmmi12 scaled \magstephalf \font\ssecsy=cmsy10 scaled 1315 % The smallcaps and symbol fonts should actually be scaled \magstep1.5, % but that is not a standard magnification. % 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, 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 so that font changes will continue to work % in math mode, where it is the current \fam that is relevant in most % cases, not the current font. Plain TeX does \def\bf{\fam=\bffam % \tenbf}, for example. By redefining \tenbf, we obviate the need to % redefine \bf itself. \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 \resetmathfonts} \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 \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 \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 \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 \resetmathfonts \setleading{15pt}} \let\subsubsecfonts = \subsecfonts % Maybe make sssec fonts scaled magstephalf? \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 \resetmathfonts \setleading{11pt}} % Set up the default fonts, so we can use them for creating boxes. % \textfonts % 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\bxshape{12}{1000} \setfont\shortcontsl\slshape{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\/\fi\fi\fi} \def\smartslanted#1{{\sl #1}\futurelet\next\smartitalicx} \def\smartitalic#1{{\it #1}\futurelet\next\smartitalicx} \let\i=\smartitalic \let\var=\smartslanted \let\dfn=\smartslanted \let\emph=\smartitalic \let\cite=\smartslanted \def\b#1{{\bf #1}} \let\strong=\b % 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 = `- } \def\t#1{% {\tt \rawbackslash \frenchspacing #1}% \null } \let\ttfont=\t \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}}}} % 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 \frenchspacing #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 % \global\def\code{\begingroup \catcode`\-=\active \let-\codedash \catcode`\_=\active \let_\codeunder \codex } % % If we end up with any active - characters when handling the index, % just treat them as a normal -. \global\def\indexbreaks{\catcode`\-=\active \let-\realdash} } \def\realdash{-} \def\codedash{-\discretionary{}{}{}} \def\codeunder{\ifusingtt{\normalunderscore\discretionary{}{}{}}{\_}} \def\codex #1{\tclose{#1}\endgroup} %\let\exp=\tclose %Was temporary % @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). \def\kbdinputstyle{\parsearg\kbdinputstylexxx} \def\kbdinputstylexxx#1{% \def\arg{#1}% \ifx\arg\worddistinct \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% \else\ifx\arg\wordexample \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% \else\ifx\arg\wordcode \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% \fi\fi\fi } \def\worddistinct{distinct} \def\wordexample{example} \def\wordcode{code} % Default is kbdinputdistinct. (Too much of a hassle to call the macro, % the catcodes are wrong for parsearg to work.) \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl} \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 @url, @env, @command quotes seem unnecessary, so use \code. \let\url=\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} % 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 downcases the argument and prints in smallcaps. \def\acronym#1{{\smallcaps \lowercase{#1}}} % @pounds{} is a sterling sign. \def\pounds{{\it\$}} \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 \def\shorttitlepage{\parsearg\shorttitlepagezzz} \def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% \endgroup\page\hbox{}\page} \def\titlepage{\begingroup \parindent=0pt \textfonts \let\subtitlerm=\tenrm \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}% % \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}% % % Leave some space at the very top of the page. \vglue\titlepagetopglue % % Now you can print the title using @title. \def\title{\parsearg\titlezzz}% \def\titlezzz##1{\leftline{\titlefonts\rm ##1} % print a rule at the page bottom also. \finishedtitlepagefalse \vskip4pt \hrule height 4pt width \hsize \vskip4pt}% % No rule at page bottom unless we print one at the top with @title. \finishedtitlepagetrue % % Now you can put text using @subtitle. \def\subtitle{\parsearg\subtitlezzz}% \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}% % % @author should come last, but may come many times. \def\author{\parsearg\authorzzz}% \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi {\authorfont \leftline{##1}}}% % % 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 \oldpage \let\page = \oldpage \hbox{}}% % \def\page{\oldpage \hbox{}} } \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 % % 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 % \ifpdf \pdfmakepagedesttrue \fi % \HEADINGSon } \def\finishtitlepage{% \vskip4pt \hrule height 2pt width \hsize \vskip\titlepagebottomglue \finishedtitlepagetrue } %%% 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\oddheading{\parsearg\oddheadingxxx} \def\everyheading{\parsearg\everyheadingxxx} \def\evenfooting{\parsearg\evenfootingxxx} \def\oddfooting{\parsearg\oddfootingxxx} \def\everyfooting{\parsearg\everyfootingxxx} {\catcode`\@=0 % \gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish} \gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{% \global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish} \gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{% \global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \gdef\everyheadingxxx#1{\oddheadingxxx{#1}\evenheadingxxx{#1}}% \gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish} \gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{% \global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish} \gdef\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 -\baselineskip \global\advance\vsize by -\baselineskip } \gdef\everyfootingxxx#1{\oddfootingxxx{#1}\evenfootingxxx{#1}} % }% unbind the catcode of @. % @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\settitlezzz} \def\settitlezzz #1{\gdef\thistitle{#1}} \message{tables,} % Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(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, @vtable, 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\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz} \def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \itemxpar \parsearg\xitemzzz} \def\internalBkitem{\smallbreak \parsearg\kitemzzz} \def\internalBkitemx{\itemxpar \parsearg\kitemzzz} \def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}% \itemzzz {#1}} \def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}% \itemzzz {#1}} \def\itemzzz #1{\begingroup % \advance\hsize by -\rightskip \advance\hsize by -\tableindent \setbox0=\hbox{\itemfont{#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. Unfortunately % we can't prevent a possible page break at the following % \baselineskip glue. \nobreak \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 table}} \def\itemx{\errmessage{@itemx while not in a table}} \def\kitem{\errmessage{@kitem while not in a table}} \def\kitemx{\errmessage{@kitemx while not in a table}} \def\xitem{\errmessage{@xitem while not in a table}} \def\xitemx{\errmessage{@xitemx while not in a table}} % Contains a kludge to get @end[description] to work. \def\description{\tablez{\dontindex}{1}{}{}{}{}} % @table, @ftable, @vtable. \def\table{\begingroup\inENV\obeylines\obeyspaces\tablex} {\obeylines\obeyspaces% \gdef\tablex #1^^M{% \tabley\dontindex#1 \endtabley}} \def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex} {\obeylines\obeyspaces% \gdef\ftablex #1^^M{% \tabley\fnitemindex#1 \endtabley \def\Eftable{\endgraf\afterenvbreak\endgroup}% \let\Etable=\relax}} \def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex} {\obeylines\obeyspaces% \gdef\vtablex #1^^M{% \tabley\vritemindex#1 \endtabley \def\Evtable{\endgraf\afterenvbreak\endgroup}% \let\Etable=\relax}} \def\dontindex #1{} \def\fnitemindex #1{\doind {fn}{\code{#1}}}% \def\vritemindex #1{\doind {vr}{\code{#1}}}% {\obeyspaces % \gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup% \tablez{#1}{#2}{#3}{#4}{#5}{#6}}} \def\tablez #1#2#3#4#5#6{% \aboveenvbreak % \begingroup % \def\Edescription{\Etable}% Necessary kludge. \let\itemindex=#1% \ifnum 0#3>0 \advance \leftskip by #3\mil \fi % \ifnum 0#4>0 \tableindent=#4\mil \fi % \ifnum 0#5>0 \advance \rightskip by #5\mil \fi % \def\itemfont{#2}% \itemmax=\tableindent % \advance \itemmax by -\itemmargin % \advance \leftskip by \tableindent % \exdentamount=\tableindent \parindent = 0pt \parskip = \smallskipamount \ifdim \parskip=0pt \parskip=2pt \fi% \def\Etable{\endgraf\afterenvbreak\endgroup}% \let\item = \internalBitem % \let\itemx = \internalBitemx % \let\kitem = \internalBkitem % \let\kitemx = \internalBkitemx % \let\xitem = \internalBxitem % \let\xitemx = \internalBxitemx % } % This is the counter used by @enumerate, which is really @itemize \newcount \itemno \def\itemize{\parsearg\itemizezzz} \def\itemizezzz #1{% \begingroup % ended by the @end itemize \itemizey {#1}{\Eitemize} } \def\itemizey #1#2{% \aboveenvbreak % \itemmax=\itemindent % \advance \itemmax by -\itemmargin % \advance \leftskip by \itemindent % \exdentamount=\itemindent \parindent = 0pt % \parskip = \smallskipamount % \ifdim \parskip=0pt \parskip=2pt \fi% \def#2{\endgraf\afterenvbreak\endgroup}% \def\itemcontents{#1}% \let\item=\itemizeitem} % Set sfcode to normal for the chars that usually have another value. % These are `.?!:;,' \def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000 \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 } % \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'. % \def\enumerate{\parsearg\enumeratezzz} \def\enumeratezzz #1{\enumeratey #1 \endenumeratey} \def\enumeratey #1 #2\endenumeratey{% \begingroup % ended by the @end enumerate % % 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 itemizey, 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 \itemizey{#1.}\Eenumerate\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} % Definition of @item while inside @itemize. \def\itemizeitem{% \advance\itemno by 1 {\let\par=\endgraf \smallbreak}% \ifhmode \errmessage{In hmode at itemizeitem}\fi {\parskip=0in \hskip 0pt \hbox to 0pt{\hss \itemcontents\hskip \itemmargin}% \vadjust{\penalty 1200}}% \flushcr} % @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. % % For those who want to use more than one line's worth of words in % the preamble, break the line within one argument and it % will parse correctly, i.e., % % @multitable {Column 1 template} {Column 2 template} {Column 3 % template} % Not: % @multitable {Column 1 template} {Column 2 template} % {Column 3 template} % 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, @multitable or @end multitable 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 part of the @columnfraction before the decimal point, which % is presumably either 0 or the empty string (but we don't check, we % just throw it away). #2 is the decimal part, which we use as the % percent of \hsize for this column. \def\pickupwholefraction#1.#2 {% \global\advance\colcount by 1 \expandafter\xdef\csname col\the\colcount\endcsname{.#2\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 }% 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 } % This used to have \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{&} % @multitable ... @end multitable definitions: % \def\multitable{\parsearg\dotable} \def\dotable#1{\bgroup \vskip\parskip \let\item\crcr \tolerance=9500 \hbadness=9500 \setmultitablespacing \parskip=\multitableparskip \parindent=\multitableparindent \overfullrule=0pt \global\colcount=0 \def\Emultitable{\global\setpercentfalse\cr\egroup\egroup}% % % To parse everything between @multitable and @item: \setuptable#1 \endsetuptable % % \everycr will reset column counter, \colcount, at the end of % each line. Every column entry will cause \colcount to advance by one. % The table preamble % looks at the current \colcount to find the correct column width. \everycr{\noalign{% % % \filbreak%% keeps underfull box messages off when table breaks over pages. % 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. \global\colcount=0\relax}}% % % 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\relax \multistrut\vtop{\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\setmultitablespacing{% test to see if user has set \multitablelinespace. % If so, do nothing. If not, give it an appropriate dimension based on % current baselineskip. \ifdim\multitablelinespace=0pt \setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip \global\advance\multitablelinespace by-\ht0 %% strut to put in table in case some entry doesn't have descenders, %% to keep lines equally spaced \let\multistrut = \strut \else %% FIXME: what is \box0 supposed to be? \gdef\multistrut{\vrule height\multitablelinespace depth\dp0 width0pt\relax} \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,} % Prevent errors for section commands. % Used in @ignore and in failing conditionals. \def\ignoresections{% \let\chapter=\relax \let\unnumbered=\relax \let\top=\relax \let\unnumberedsec=\relax \let\unnumberedsection=\relax \let\unnumberedsubsec=\relax \let\unnumberedsubsection=\relax \let\unnumberedsubsubsec=\relax \let\unnumberedsubsubsection=\relax \let\section=\relax \let\subsec=\relax \let\subsubsec=\relax \let\subsection=\relax \let\subsubsection=\relax \let\appendix=\relax \let\appendixsec=\relax \let\appendixsection=\relax \let\appendixsubsec=\relax \let\appendixsubsection=\relax \let\appendixsubsubsec=\relax \let\appendixsubsubsection=\relax \let\contents=\relax \let\smallbook=\relax \let\titlepage=\relax } % Used in nested conditionals, where we have to parse the Texinfo source % and so want to turn off most commands, in case they are used % incorrectly. % \def\ignoremorecommands{% \let\defcodeindex = \relax \let\defcv = \relax \let\deffn = \relax \let\deffnx = \relax \let\defindex = \relax \let\defivar = \relax \let\defmac = \relax \let\defmethod = \relax \let\defop = \relax \let\defopt = \relax \let\defspec = \relax \let\deftp = \relax \let\deftypefn = \relax \let\deftypefun = \relax \let\deftypeivar = \relax \let\deftypeop = \relax \let\deftypevar = \relax \let\deftypevr = \relax \let\defun = \relax \let\defvar = \relax \let\defvr = \relax \let\ref = \relax \let\xref = \relax \let\printindex = \relax \let\pxref = \relax \let\settitle = \relax \let\setchapternewpage = \relax \let\setchapterstyle = \relax \let\everyheading = \relax \let\evenheading = \relax \let\oddheading = \relax \let\everyfooting = \relax \let\evenfooting = \relax \let\oddfooting = \relax \let\headings = \relax \let\include = \relax \let\lowersections = \relax \let\down = \relax \let\raisesections = \relax \let\up = \relax \let\set = \relax \let\clear = \relax \let\item = \relax } % Ignore @ignore ... @end ignore. % \def\ignore{\doignore{ignore}} % Ignore @ifinfo, @ifhtml, @ifnottex, @html, @menu, and @direntry text. % \def\ifinfo{\doignore{ifinfo}} \def\ifhtml{\doignore{ifhtml}} \def\ifnottex{\doignore{ifnottex}} \def\html{\doignore{html}} \def\menu{\doignore{menu}} \def\direntry{\doignore{direntry}} % @dircategory CATEGORY -- specify a category of the dir file % which this file should belong to. Ignore this in TeX. \let\dircategory = \comment % Ignore text until a line `@end #1'. % \def\doignore#1{\begingroup % Don't complain about control sequences we have declared \outer. \ignoresections % % Define a command to swallow text until we reach `@end #1'. % This @ is a catcode 12 token (that is the normal catcode of @ in % this texinfo.tex file). We change the catcode of @ below to match. \long\def\doignoretext##1@end #1{\enddoignore}% % % Make sure that spaces turn into tokens that match what \doignoretext wants. \catcode32 = 10 % % Ignore braces, too, so mismatched braces don't cause trouble. \catcode`\{ = 9 \catcode`\} = 9 % % We must not have @c interpreted as a control sequence. \catcode`\@ = 12 % % Make the letter c a comment character so that the rest of the line % will be ignored. This way, the document can have (for example) % @c @end ifinfo % and the @end ifinfo will be properly ignored. % (We've just changed @ to catcode 12.) \catcode`\c = 14 % % And now expand that command. \doignoretext } % What we do to finish off ignored text. % \def\enddoignore{\endgroup\ignorespaces}% \newif\ifwarnedobs\warnedobsfalse \def\obstexwarn{% \ifwarnedobs\relax\else % We need to warn folks that they may have trouble with TeX 3.0. % This uses \immediate\write16 rather than \message to get newlines. \immediate\write16{} \immediate\write16{WARNING: for users of Unix TeX 3.0!} \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).} \immediate\write16{If you are running another version of TeX, relax.} \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.} \immediate\write16{ Then upgrade your TeX installation if you can.} \immediate\write16{ (See ftp://ftp.gnu.org/pub/gnu/TeX.README.)} \immediate\write16{If you are stuck with version 3.0, run the} \immediate\write16{ script ``tex3patch'' from the Texinfo distribution} \immediate\write16{ to use a workaround.} \immediate\write16{} \global\warnedobstrue \fi } % **In TeX 3.0, setting text in \nullfont hangs tex. For a % workaround (which requires the file ``dummy.tfm'' to be installed), % uncomment the following line: %%%%%\font\nullfont=dummy\let\obstexwarn=\relax % Ignore text, except that we keep track of conditional commands for % purposes of nesting, up to an `@end #1' command. % \def\nestedignore#1{% \obstexwarn % We must actually expand the ignored text to look for the @end % command, so that nested ignore constructs work. Thus, we put the % text into a \vbox and then do nothing with the result. To minimize % the change of memory overflow, we follow the approach outlined on % page 401 of the TeXbook: make the current font be a dummy font. % \setbox0 = \vbox\bgroup % Don't complain about control sequences we have declared \outer. \ignoresections % % Define `@end #1' to end the box, which will in turn undefine the % @end command again. \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}% % % We are going to be parsing Texinfo commands. Most cause no % trouble when they are used incorrectly, but some commands do % complicated argument parsing or otherwise get confused, so we % undefine them. % % We can't do anything about stray @-signs, unfortunately; % they'll produce `undefined control sequence' errors. \ignoremorecommands % % Set the current font to be \nullfont, a TeX primitive, and define % all the font commands to also use \nullfont. We don't use % dummy.tfm, as suggested in the TeXbook, because not all sites % might have that installed. Therefore, math mode will still % produce output, but that should be an extremely small amount of % stuff compared to the main input. % \nullfont \let\tenrm=\nullfont \let\tenit=\nullfont \let\tensl=\nullfont \let\tenbf=\nullfont \let\tentt=\nullfont \let\smallcaps=\nullfont \let\tensf=\nullfont % Similarly for index fonts (mostly for their use in smallexample). \let\smallrm=\nullfont \let\smallit=\nullfont \let\smallsl=\nullfont \let\smallbf=\nullfont \let\smalltt=\nullfont \let\smallsc=\nullfont \let\smallsf=\nullfont % % Don't complain when characters are missing from the fonts. \tracinglostchars = 0 % % Don't bother to do space factor calculations. \frenchspacing % % Don't report underfull hboxes. \hbadness = 10000 % % Do minimal line-breaking. \pretolerance = 10000 % % Do not execute instructions in @tex \def\tex{\doignore{tex}}% % Do not execute macro definitions. % `c' is a comment character, so the word `macro' will get cut off. \def\macro{\doignore{ma}}% } % @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. Make sure the catcode of space is correct to avoid % losing inside @example, for instance. % \def\set{\begingroup\catcode` =10 \catcode`\-=12 \catcode`\_=12 % Allow - and _ in VAR. \parsearg\setxxx} \def\setxxx#1{\setyyy#1 \endsetyyy} \def\setyyy#1 #2\endsetyyy{% \def\temp{#2}% \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted. \fi \endgroup } % Can't use \xdef to pre-expand #2 and save some time, since \temp or % \next or other control sequences that we've defined might get us into % an infinite loop. Consider `@set foo @cite{bar}'. \def\setzzz#1#2 \endsetzzz{\expandafter\gdef\csname SET#1\endcsname{#2}} % @clear VAR clears (i.e., unsets) the variable VAR. % \def\clear{\parsearg\clearxxx} \def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax} % @value{foo} gets the text saved in variable foo. { \catcode`\_ = \active % % We might end up with active _ or - characters in the argument if % we're called from @code, as @code{@value{foo-bar_}}. So \let any % such active characters to their normal equivalents. \gdef\value{\begingroup \catcode`\-=12 \catcode`\_=12 \indexbreaks \let_\normalunderscore \valuexxx} } \def\valuexxx#1{\expandablevalue{#1}\endgroup} % We have this subroutine so that we can handle at least some @value's % properly in indexes (we \let\value to this in \indexdummies). Ones % whose names contain - or _ still won't work, but we can't do anything % about that. The command has to be fully expandable, 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'']}% \else \csname SET#1\endcsname \fi } % @ifset VAR ... @end ifset reads the `...' iff VAR has been defined % with @set. % \def\ifset{\parsearg\ifsetxxx} \def\ifsetxxx #1{% \expandafter\ifx\csname SET#1\endcsname\relax \expandafter\ifsetfail \else \expandafter\ifsetsucceed \fi } \def\ifsetsucceed{\conditionalsucceed{ifset}} \def\ifsetfail{\nestedignore{ifset}} \defineunmatchedend{ifset} % @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been % defined with @set, or has been undefined with @clear. % \def\ifclear{\parsearg\ifclearxxx} \def\ifclearxxx #1{% \expandafter\ifx\csname SET#1\endcsname\relax \expandafter\ifclearsucceed \else \expandafter\ifclearfail \fi } \def\ifclearsucceed{\conditionalsucceed{ifclear}} \def\ifclearfail{\nestedignore{ifclear}} \defineunmatchedend{ifclear} % @iftex, @ifnothtml, @ifnotinfo always succeed; we read the text % following, through the first @end iftex (etc.). Make `@end iftex' % (etc.) valid only after an @iftex. % \def\iftex{\conditionalsucceed{iftex}} \def\ifnothtml{\conditionalsucceed{ifnothtml}} \def\ifnotinfo{\conditionalsucceed{ifnotinfo}} \defineunmatchedend{iftex} \defineunmatchedend{ifnothtml} \defineunmatchedend{ifnotinfo} % We can't just want to start a group at @iftex (for example) and end it % at @end iftex, since then @set commands inside the conditional have no % effect (they'd get reverted at the end of the group). So we must % define \Eiftex to redefine itself to be its previous value. (We can't % just define it to fail again with an ``unmatched end'' error, since % the @ifset might be nested.) % \def\conditionalsucceed#1{% \edef\temp{% % Remember the current value of \E#1. \let\nece{prevE#1} = \nece{E#1}% % % At the `@end #1', redefine \E#1 to be its previous value. \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}% }% \temp } % We need to expand lots of \csname's, but we don't want to expand the % control sequences after we've constructed them. % \def\nece#1{\expandafter\noexpand\csname#1\endcsname} % @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 \newindex. {\catcode`\@=11 \gdef\newwrite{\alloc@7\write\chardef\sixt@@n}} % \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}}} \def\indexdummies{% \def\ { }% % Take care of the plain tex accent commands. \def\"{\realbackslash "}% \def\`{\realbackslash `}% \def\'{\realbackslash '}% \def\^{\realbackslash ^}% \def\~{\realbackslash ~}% \def\={\realbackslash =}% \def\b{\realbackslash b}% \def\c{\realbackslash c}% \def\d{\realbackslash d}% \def\u{\realbackslash u}% \def\v{\realbackslash v}% \def\H{\realbackslash H}% % Take care of the plain tex special European modified letters. \def\oe{\realbackslash oe}% \def\ae{\realbackslash ae}% \def\aa{\realbackslash aa}% \def\OE{\realbackslash OE}% \def\AE{\realbackslash AE}% \def\AA{\realbackslash AA}% \def\o{\realbackslash o}% \def\O{\realbackslash O}% \def\l{\realbackslash l}% \def\L{\realbackslash L}% \def\ss{\realbackslash ss}% % Take care of texinfo commands likely to appear in an index entry. % (Must be a way to avoid doing expansion at all, and thus not have to % laboriously list every single command here.) \def\@{@}% will be @@ when we switch to @ as escape char. % 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 \def\_{{\realbackslash _}}% \def\w{\realbackslash w }% \def\bf{\realbackslash bf }% %\def\rm{\realbackslash rm }% \def\sl{\realbackslash sl }% \def\sf{\realbackslash sf}% \def\tt{\realbackslash tt}% \def\gtr{\realbackslash gtr}% \def\less{\realbackslash less}% \def\hat{\realbackslash hat}% \def\TeX{\realbackslash TeX}% \def\dots{\realbackslash dots }% \def\result{\realbackslash result}% \def\equiv{\realbackslash equiv}% \def\expansion{\realbackslash expansion}% \def\print{\realbackslash print}% \def\error{\realbackslash error}% \def\point{\realbackslash point}% \def\copyright{\realbackslash copyright}% \def\tclose##1{\realbackslash tclose {##1}}% \def\code##1{\realbackslash code {##1}}% \def\uref##1{\realbackslash uref {##1}}% \def\url##1{\realbackslash url {##1}}% \def\env##1{\realbackslash env {##1}}% \def\command##1{\realbackslash command {##1}}% \def\option##1{\realbackslash option {##1}}% \def\dotless##1{\realbackslash dotless {##1}}% \def\samp##1{\realbackslash samp {##1}}% \def\,##1{\realbackslash ,{##1}}% \def\t##1{\realbackslash t {##1}}% \def\r##1{\realbackslash r {##1}}% \def\i##1{\realbackslash i {##1}}% \def\b##1{\realbackslash b {##1}}% \def\sc##1{\realbackslash sc {##1}}% \def\cite##1{\realbackslash cite {##1}}% \def\key##1{\realbackslash key {##1}}% \def\file##1{\realbackslash file {##1}}% \def\var##1{\realbackslash var {##1}}% \def\kbd##1{\realbackslash kbd {##1}}% \def\dfn##1{\realbackslash dfn {##1}}% \def\emph##1{\realbackslash emph {##1}}% \def\acronym##1{\realbackslash acronym {##1}}% % % Handle some cases of @value -- where the variable name does not % contain - or _, and the value does not contain any % (non-fully-expandable) commands. \let\value = \expandablevalue % \unsepspaces % Turn off macro expansion \turnoffmacros } % 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 \ ). {\obeyspaces \gdef\unsepspaces{\obeyspaces\let =\space}} % \indexnofonts no-ops all font-change commands. % This is used when outputting the strings to sort the index by. \def\indexdummyfont#1{#1} \def\indexdummytex{TeX} \def\indexdummydots{...} \def\indexnofonts{% % Just ignore accents. \let\,=\indexdummyfont \let\"=\indexdummyfont \let\`=\indexdummyfont \let\'=\indexdummyfont \let\^=\indexdummyfont \let\~=\indexdummyfont \let\==\indexdummyfont \let\b=\indexdummyfont \let\c=\indexdummyfont \let\d=\indexdummyfont \let\u=\indexdummyfont \let\v=\indexdummyfont \let\H=\indexdummyfont \let\dotless=\indexdummyfont % Take care of the plain tex special European modified letters. \def\oe{oe}% \def\ae{ae}% \def\aa{aa}% \def\OE{OE}% \def\AE{AE}% \def\AA{AA}% \def\o{o}% \def\O{O}% \def\l{l}% \def\L{L}% \def\ss{ss}% \let\w=\indexdummyfont \let\t=\indexdummyfont \let\r=\indexdummyfont \let\i=\indexdummyfont \let\b=\indexdummyfont \let\emph=\indexdummyfont \let\strong=\indexdummyfont \let\cite=\indexdummyfont \let\sc=\indexdummyfont %Don't no-op \tt, since it isn't a user-level command % and is used in the definitions of the active chars like <, >, |... %\let\tt=\indexdummyfont \let\tclose=\indexdummyfont \let\code=\indexdummyfont \let\url=\indexdummyfont \let\uref=\indexdummyfont \let\env=\indexdummyfont \let\acronym=\indexdummyfont \let\command=\indexdummyfont \let\option=\indexdummyfont \let\file=\indexdummyfont \let\samp=\indexdummyfont \let\kbd=\indexdummyfont \let\key=\indexdummyfont \let\var=\indexdummyfont \let\TeX=\indexdummytex \let\dots=\indexdummydots \def\@{@}% } % To define \realbackslash, we must make \ not be an escape. % We must first make another character (@) an escape % so we do not become unable to do a definition. {\catcode`\@=0 \catcode`\\=\other @gdef@realbackslash{\}} \let\indexbackslash=0 %overridden during \printindex. \let\SETmarginindex=\relax % put index entries in margin (undocumented)? % For \ifx comparisons. \def\emptymacro{\empty} % Most index entries go through here, but \dosubind is the general case. % \def\doind#1#2{\dosubind{#1}{#2}\empty} % 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 defuns, which call us directly. % \def\dosubind#1#2#3{% % Put the index entry in the margin if desired. \ifx\SETmarginindex\relax\else \insert\margin{\hbox{\vrule height8pt depth3pt width0pt #2}}% \fi {% \count255=\lastpenalty {% \indexdummies % Must do this here, since \bf, etc expand at this stage \escapechar=`\\ {% \let\folio = 0% We will expand all macros now EXCEPT \folio. \def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now % so it will be output as is; and it will print as backslash. % \def\thirdarg{#3}% % % If third arg is present, precede it with space in sort key. \ifx\thirdarg\emptymacro \let\subentry = \empty \else \def\subentry{ #3}% \fi % % First process the index entry with all font commands turned % off to get the string to sort by. {\indexnofonts \xdef\indexsorttmp{#2\subentry}}% % % Now the real index entry with the fonts. \toks0 = {#2}% % % If third (subentry) arg is present, add it to the index % string. And include a space. \ifx\thirdarg\emptymacro \else \toks0 = \expandafter{\the\toks0 \space #3}% \fi % % 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, texindex reduces to % two when writing the .??s sorted result. \edef\temp{% \write\csname#1indfile\endcsname{% \realbackslash entry{\indexsorttmp}{\folio}{\the\toks0}}% }% % % 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. % \iflinks \ifvmode \skip0 = \lastskip \ifdim\lastskip = 0pt \else \nobreak\vskip-\lastskip \fi \fi % \temp % do the write % % \ifvmode \ifdim\skip0 = 0pt \else \nobreak\vskip\skip0 \fi \fi \fi }% }% \penalty\count255 }% } % 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). % \def\printindex{\parsearg\doprintindex} \def\doprintindex#1{\begingroup \dobreak \chapheadingskip{10000}% % \smallfonts \rm \tolerance = 9500 \indexbreaks % % 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{\rawbackslashxx}% \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. \penalty -300 % % 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}% \vskip .33\baselineskip plus .1\baselineskip % % Do our best not to break after the initial. \nobreak }} % This typesets a paragraph consisting of #1, dot leaders, and then #2 % flush to the right margin. It is used for index and table of contents % entries. The paragraph is indented by \leftskip. % \def\entry#1#2{\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 % % Start a ``paragraph'' for the index entry so the line breaking % parameters we've set above will have an effect. \noindent % % Insert the text of the index entry. TeX will do line-breaking on it. #1% % 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{#2}% \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#2.\ \the\toksA % The page number ends the paragraph. \else \ #2% The page number ends the paragraph. \fi \fi% \par \endgroup} % Like \dotfill except takes at least 1 em. \def\indexdotfill{\cleaders \hbox{$\mathsurround=0pt \mkern1.5mu ${\it .}$ \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 #2\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. \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 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. \def\thischapter{} \def\thissection{} \newcount\absseclevel % used to calculate proper heading level \newcount\secbase\secbase=0 % @raise/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 % Choose a numbered-heading macro % #1 is heading level if unmodified by @raisesections or @lowersections % #2 is text for heading \def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 \ifcase\absseclevel \chapterzzz{#2} \or \seczzz{#2} \or \numberedsubseczzz{#2} \or \numberedsubsubseczzz{#2} \else \ifnum \absseclevel<0 \chapterzzz{#2} \else \numberedsubsubseczzz{#2} \fi \fi } % like \numhead, but chooses appendix heading levels \def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 \ifcase\absseclevel \appendixzzz{#2} \or \appendixsectionzzz{#2} \or \appendixsubseczzz{#2} \or \appendixsubsubseczzz{#2} \else \ifnum \absseclevel<0 \appendixzzz{#2} \else \appendixsubsubseczzz{#2} \fi \fi } % like \numhead, but chooses numberless heading levels \def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 \ifcase\absseclevel \unnumberedzzz{#2} \or \unnumberedseczzz{#2} \or \unnumberedsubseczzz{#2} \or \unnumberedsubsubseczzz{#2} \else \ifnum \absseclevel<0 \unnumberedzzz{#2} \else \unnumberedsubsubseczzz{#2} \fi \fi } % @chapter, @appendix, @unnumbered. \def\thischaptername{No Chapter Title} \outer\def\chapter{\parsearg\chapteryyy} \def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz \def\chapterzzz #1{% \secno=0 \subsecno=0 \subsubsecno=0 \global\advance \chapno by 1 \message{\putwordChapter\space \the\chapno}% \chapmacro {#1}{\the\chapno}% \gdef\thissection{#1}% \gdef\thischaptername{#1}% % We don't substitute the actual chapter name into \thischapter % because we don't want its macros evaluated now. \xdef\thischapter{\putwordChapter{} \the\chapno: \noexpand\thischaptername}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash chapentry{\the\toks0}% {\the\chapno}}}% \temp \donoderef \global\let\section = \numberedsec \global\let\subsection = \numberedsubsec \global\let\subsubsection = \numberedsubsubsec } \outer\def\appendix{\parsearg\appendixyyy} \def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz \def\appendixzzz #1{% \secno=0 \subsecno=0 \subsubsecno=0 \global\advance \appendixno by 1 \message{\putwordAppendix\space \appendixletter}% \chapmacro {#1}{\putwordAppendix{} \appendixletter}% \gdef\thissection{#1}% \gdef\thischaptername{#1}% \xdef\thischapter{\putwordAppendix{} \appendixletter: \noexpand\thischaptername}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash chapentry{\the\toks0}% {\putwordAppendix{} \appendixletter}}}% \temp \appendixnoderef \global\let\section = \appendixsec \global\let\subsection = \appendixsubsec \global\let\subsubsection = \appendixsubsubsec } % @centerchap is like @unnumbered, but the heading is centered. \outer\def\centerchap{\parsearg\centerchapyyy} \def\centerchapyyy #1{{\let\unnumbchapmacro=\centerchapmacro \unnumberedyyy{#1}}} % @top is like @unnumbered. \outer\def\top{\parsearg\unnumberedyyy} \outer\def\unnumbered{\parsearg\unnumberedyyy} \def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz \def\unnumberedzzz #1{% \secno=0 \subsecno=0 \subsubsecno=0 % % 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)}% % \unnumbchapmacro {#1}% \gdef\thischapter{#1}\gdef\thissection{#1}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash unnumbchapentry{\the\toks0}}}% \temp \unnumbnoderef \global\let\section = \unnumberedsec \global\let\subsection = \unnumberedsubsec \global\let\subsubsection = \unnumberedsubsubsec } % Sections. \outer\def\numberedsec{\parsearg\secyyy} \def\secyyy #1{\numhead1{#1}} % normally calls seczzz \def\seczzz #1{% \subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % \gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash secentry{\the\toks0}% {\the\chapno}{\the\secno}}}% \temp \donoderef \nobreak } \outer\def\appendixsection{\parsearg\appendixsecyyy} \outer\def\appendixsec{\parsearg\appendixsecyyy} \def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz \def\appendixsectionzzz #1{% \subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % \gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash secentry{\the\toks0}% {\appendixletter}{\the\secno}}}% \temp \appendixnoderef \nobreak } \outer\def\unnumberedsec{\parsearg\unnumberedsecyyy} \def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz \def\unnumberedseczzz #1{% \plainsecheading {#1}\gdef\thissection{#1}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash unnumbsecentry{\the\toks0}}}% \temp \unnumbnoderef \nobreak } % Subsections. \outer\def\numberedsubsec{\parsearg\numberedsubsecyyy} \def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz \def\numberedsubseczzz #1{% \gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % \subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash subsecentry{\the\toks0}% {\the\chapno}{\the\secno}{\the\subsecno}}}% \temp \donoderef \nobreak } \outer\def\appendixsubsec{\parsearg\appendixsubsecyyy} \def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz \def\appendixsubseczzz #1{% \gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % \subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash subsecentry{\the\toks0}% {\appendixletter}{\the\secno}{\the\subsecno}}}% \temp \appendixnoderef \nobreak } \outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy} \def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz \def\unnumberedsubseczzz #1{% \plainsubsecheading {#1}\gdef\thissection{#1}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash unnumbsubsecentry% {\the\toks0}}}% \temp \unnumbnoderef \nobreak } % Subsubsections. \outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy} \def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz \def\numberedsubsubseczzz #1{% \gdef\thissection{#1}\global\advance \subsubsecno by 1 % \subsubsecheading {#1} {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash subsubsecentry{\the\toks0}% {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}}}% \temp \donoderef \nobreak } \outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy} \def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz \def\appendixsubsubseczzz #1{% \gdef\thissection{#1}\global\advance \subsubsecno by 1 % \subsubsecheading {#1} {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash subsubsecentry{\the\toks0}% {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}}}% \temp \appendixnoderef \nobreak } \outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy} \def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz \def\unnumberedsubsubseczzz #1{% \plainsubsubsecheading {#1}\gdef\thissection{#1}% \toks0 = {#1}% \edef\temp{\noexpand\writetocentry{\realbackslash unnumbsubsubsecentry% {\the\toks0}}}% \temp \unnumbnoderef \nobreak } % These are variants which are not "outer", so they can appear in @ifinfo. % Actually, they should now be obsolete; ordinary section commands should work. \def\infotop{\parsearg\unnumberedzzz} \def\infounnumbered{\parsearg\unnumberedzzz} \def\infounnumberedsec{\parsearg\unnumberedseczzz} \def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz} \def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz} \def\infoappendix{\parsearg\appendixzzz} \def\infoappendixsec{\parsearg\appendixseczzz} \def\infoappendixsubsec{\parsearg\appendixsubseczzz} \def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz} \def\infochapter{\parsearg\chapterzzz} \def\infosection{\parsearg\sectionzzz} \def\infosubsection{\parsearg\subsectionzzz} \def\infosubsubsection{\parsearg\subsubsectionzzz} % 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. \global\let\section = \numberedsec \global\let\subsection = \numberedsubsec \global\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{\parsearg\majorheadingzzz} \def\majorheadingzzz #1{% {\advance\chapheadingskip by 10pt \chapbreak }% {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 \parindent=0pt\raggedright \rm #1\hfill}}\bigskip \par\penalty 200} \def\chapheading{\parsearg\chapheadingzzz} \def\chapheadingzzz #1{\chapbreak % {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 \parindent=0pt\raggedright \rm #1\hfill}}\bigskip \par\penalty 200} % @heading, @subheading, @subsubheading. \def\heading{\parsearg\plainsecheading} \def\subheading{\parsearg\plainsubsecheading} \def\subsubheading{\parsearg\plainsubsubsecheading} % 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} \def\setchapterstyle #1 {\csname CHAPF#1\endcsname} %%% 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 \def\CHAPFplain{ \global\let\chapmacro=\chfplain \global\let\unnumbchapmacro=\unnchfplain \global\let\centerchapmacro=\centerchfplain} % Plain chapter opening. % #1 is the text, #2 the chapter number or empty if unnumbered. \def\chfplain#1#2{% \pchapsepmacro {% \chapfonts \rm \def\chapnum{#2}% \setbox0 = \hbox{#2\ifx\chapnum\empty\else\enspace\fi}% \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright \hangindent = \wd0 \centerparametersmaybe \unhbox0 #1\par}% }% \nobreak\bigskip % no page break after a chapter title \nobreak } % Plain opening for unnumbered. \def\unnchfplain#1{\chfplain{#1}{}} % @centerchap -- centered and unnumbered. \let\centerparametersmaybe = \relax \def\centerchfplain#1{{% \def\centerparametersmaybe{% \advance\rightskip by 3\rightskip \leftskip = \rightskip \parfillskip = 0pt }% \chfplain{#1}{}% }} \CHAPFplain % The default \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\unnumbchapmacro=\unnchfopen \global\let\centerchapmacro=\centerchfopen} % Section titles. \newskip\secheadingskip \def\secheadingbreak{\dobreak \secheadingskip {-1000}} \def\secheading#1#2#3{\sectionheading{sec}{#2.#3}{#1}} \def\plainsecheading#1{\sectionheading{sec}{}{#1}} % Subsection titles. \newskip \subsecheadingskip \def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}} \def\subsecheading#1#2#3#4{\sectionheading{subsec}{#2.#3.#4}{#1}} \def\plainsubsecheading#1{\sectionheading{subsec}{}{#1}} % Subsubsection titles. \let\subsubsecheadingskip = \subsecheadingskip \let\subsubsecheadingbreak = \subsecheadingbreak \def\subsubsecheading#1#2#3#4#5{\sectionheading{subsubsec}{#2.#3.#4.#5}{#1}} \def\plainsubsubsecheading#1{\sectionheading{subsubsec}{}{#1}} % Print any size section title. % % #1 is the section type (sec/subsec/subsubsec), #2 is the section % number (maybe empty), #3 the text. \def\sectionheading#1#2#3{% {% \expandafter\advance\csname #1headingskip\endcsname by \parskip \csname #1headingbreak\endcsname }% {% % Switch to the right set of fonts. \csname #1fonts\endcsname \rm % % Only insert the separating space if we have a section number. \def\secnum{#2}% \setbox0 = \hbox{#2\ifx\secnum\empty\else\enspace\fi}% % \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright \hangindent = \wd0 % zero if no section number \unhbox0 #3}% }% \ifdim\parskip<10pt \nobreak\kern10pt\nobreak\kern-\parskip\fi \nobreak } \message{toc,} % Table of contents. \newwrite\tocfile % Write an entry to the toc file, opening it if necessary. % Called from @chapter, etc. We supply {\folio} at the end of the % argument, which will end up as the last argument to the \...entry macro. % % We open the .toc file here instead of at @setfilename or any other % given time so that @contents can be put in the document anywhere. % \newif\iftocfileopened \def\writetocentry#1{% \iftocfileopened\else \immediate\openout\tocfile = \jobname.toc \global\tocfileopenedtrue \fi \iflinks \write\tocfile{#1{\folio}}\fi } \newskip\contentsrightmargin \contentsrightmargin=1in \newcount\savepageno \newcount\lastnegativepageno \lastnegativepageno = -1 % Finish up the main text and 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. \unnumbchapmacro{#1}\def\thischapter{}% \savepageno = \pageno \begingroup % Set up to handle contents files properly. \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11 % We can't do this, because then an actual ^ in a section % title fails, e.g., @chapter ^ -- exponentiation. --karl, 9jul97. %\catcode`\^=7 % to see ^^e4 as \"a etc. juha@piuha.ydi.vtt.fi \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 \pageno = \lastnegativepageno \fi } % Normal (long) toc. \def\contents{% \startcontents{\putwordTOC}% \openin 1 \jobname.toc \ifeof 1 \else \closein 1 \input \jobname.toc \fi \vfill \eject \contentsalignmacro % in case @setchapternewpage odd is in effect \pdfmakeoutlines \endgroup \lastnegativepageno = \pageno \pageno = \savepageno } % And just the chapters. \def\summarycontents{% \startcontents{\putwordShortTOC}% % \let\chapentry = \shortchapentry \let\unnumbchapentry = \shortunnumberedentry % We want a true roman here for the page numbers. \secfonts \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl \rm \hyphenpenalty = 10000 \advance\baselineskip by 1pt % Open it up a little. \def\secentry ##1##2##3##4{} \def\unnumbsecentry ##1##2{} \def\subsecentry ##1##2##3##4##5{} \def\unnumbsubsecentry ##1##2{} \def\subsubsecentry ##1##2##3##4##5##6{} \def\unnumbsubsubsecentry ##1##2{} \openin 1 \jobname.toc \ifeof 1 \else \closein 1 \input \jobname.toc \fi \vfill \eject \contentsalignmacro % in case @setchapternewpage odd is in effect \endgroup \lastnegativepageno = \pageno \pageno = \savepageno } \let\shortcontents = \summarycontents \ifpdf \pdfcatalog{/PageMode /UseOutlines}% \fi % 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, ... % Chapter-level things, for both the long and short contents. \def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}} % See comments in \dochapentry re vbox and related settings \def\shortchapentry#1#2#3{% \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#3\egroup}% } % Typeset the label for a chapter or appendix for the short contents. % The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter. % We could simplify the code here by writing out an \appendixentry % command in the toc file for appendices, instead of using \chapentry % for both, but it doesn't seem worth it. % \newdimen\shortappendixwidth % \def\shortchaplabel#1{% % Compute width of word "Appendix", may change with language. \setbox0 = \hbox{\shortcontrm \putwordAppendix}% \shortappendixwidth = \wd0 % % We typeset #1 in a box of constant width, regardless of the text of % #1, so the chapter titles will come out aligned. \setbox0 = \hbox{#1}% \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi % % This space should be plenty, since a single number is .5em, and the % widest letter (M) is 1em, at least in the Computer Modern fonts. % (This space doesn't include the extra space that gets added after % the label; that gets put in by \shortchapentry above.) \advance\dimen0 by 1.1em \hbox to \dimen0{#1\hfil}% } \def\unnumbchapentry#1#2{\dochapentry{#1}{#2}} \def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno\bgroup#2\egroup}} % Sections. \def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}} \def\unnumbsecentry#1#2{\dosecentry{#1}{#2}} % Subsections. \def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}} \def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}} % And subsubsections. \def\subsubsecentry#1#2#3#4#5#6{% \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}} \def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}} % This parameter controls the indentation of the various levels. \newdimen\tocindent \tocindent = 3pc % 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} % Final typesetting of a toc entry; we use the same \entry macro as for % the index entries, but we want to suppress hyphenation here. (We % can't do that in the \entry macro, since index entries might consist % of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.) \def\tocentry#1#2{\begingroup \vskip 0pt plus1pt % allow a little stretch for the sake of nice page breaks % Do not use \turnoffactive in these arguments. Since the toc is % typeset in cmr, so characters such as _ would come out wrong; we % have to do the usual translation tricks. \entry{#1}{#2}% \endgroup} % 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} \let\subsecentryfonts = \textfonts \let\subsubsecentryfonts = \textfonts \message{environments,} % @foo ... @end foo. % 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. % Furthermore, these definitions must come after we define our fonts. \newbox\dblarrowbox \newbox\longdblarrowbox \newbox\pushcharbox \newbox\bullbox \newbox\equivbox \newbox\errorbox %{\tentt %\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil} %\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil} %\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil} %\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil} % Adapted from the manmac format (p.420 of TeXbook) %\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex % depth .1ex\hfil} %} % @point{}, @result{}, @expansion{}, @print{}, @equiv{}. \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}} % Adapted from the TeXbook's \boxit. {\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 \tensf error\kern-1.5pt} \global\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} % The @error{} command. \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. \def\tex{\begingroup \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 \catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie \catcode `\%=14 \catcode 43=12 % plus \catcode`\"=12 \catcode`\==12 \catcode`\|=12 \catcode`\<=12 \catcode`\>=12 \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\{=\ptexlbrace \let\+=\tabalign \let\}=\ptexrbrace \let\*=\ptexstar \let\t=\ptext % \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% \def\@{@}% \let\Etex=\endgroup} % Define @lisp ... @endlisp. % @lisp does a \begingroup so it can rebind things, % including the definition of @endlisp (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} % 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. % {\obeyspaces % \gdef\sepspaces{\obeyspaces\let =\tie}} % Define \obeyedspace to be our active space, whatever it is. This is % for use in \parsearg. {\sepspaces% \global\let\obeyedspace= } % 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{{\advance\envskipamount by \parskip \endgraf \ifdim\lastskip<\envskipamount \removelastskip \penalty-50 \vskip\envskipamount \fi}} \let\afterenvbreak = \aboveenvbreak % \nonarrowing is a flag. If "set", @lisp etc don't narrow margins. \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 \long\def\cartouche{% \begingroup \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=\comment \vbox\bgroup \baselineskip=0pt\parskip=0pt\lineskip=0pt \carttop \hbox\bgroup \hskip\lskip \vrule\kern3pt \vbox\bgroup \hsize=\cartinner \kern3pt \begingroup \baselineskip=\normbskip \lineskip=\normlskip \parskip=\normpskip \vskip -\parskip \def\Ecartouche{% \endgroup \kern3pt \egroup \kern3pt\vrule \hskip\rskip \egroup \cartbot \egroup \endgroup }} % This macro is called at the beginning of all the @example variants, % inside a group. \def\nonfillstart{% \aboveenvbreak \inENV % This group ends at the end of the body \hfuzz = 12pt % Don't be fussy \sepspaces % Make spaces be word-separators rather than space tokens. \singlespace \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 % @cartouche defines \nonarrowing to inhibit narrowing % at next level down. \ifx\nonarrowing\relax \advance \leftskip by \lispnarrowing \exdentamount=\lispnarrowing \let\exdent=\nofillexdent \let\nonarrowing=\relax \fi } % Define the \E... control sequence only if we are inside the particular % environment, so the error checking in \end will work. % % To end an @example-like environment, we first end the paragraph (via % \afterenvbreak's vertical glue), and then the group. That way we keep % the zero \parskip that the environments set -- \parskip glue will be % inserted at the beginning of the next paragraph in the document, after % the environment. % \def\nonfillfinish{\afterenvbreak\endgroup} % @lisp: indented, narrowed, typewriter font. \def\lisp{\begingroup \nonfillstart \let\Elisp = \nonfillfinish \tt \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. \gobble % eat return } % @example: Same as @lisp. \def\example{\begingroup \def\Eexample{\nonfillfinish\endgroup}\lisp} % @small... is usually equivalent to the non-small (@smallbook % redefines). We must call \example (or whatever) last in the % definition, since it reads the return following the @example (or % whatever) command. % % This actually allows (for example) @end display inside an % @smalldisplay. Too bad, but makeinfo will catch the error anyway. % \def\smalldisplay{\begingroup\def\Esmalldisplay{\nonfillfinish\endgroup}\display} \def\smallexample{\begingroup\def\Esmallexample{\nonfillfinish\endgroup}\lisp} \def\smallformat{\begingroup\def\Esmallformat{\nonfillfinish\endgroup}\format} \def\smalllisp{\begingroup\def\Esmalllisp{\nonfillfinish\endgroup}\lisp} % Real @smallexample and @smalllisp (when @smallbook): use smaller fonts. % Originally contributed by Pavel@xerox. \def\smalllispx{\begingroup \def\Esmalllisp{\nonfillfinish\endgroup}% \def\Esmallexample{\nonfillfinish\endgroup}% \smallfonts \lisp } % @display: same as @lisp except keep current font. % \def\display{\begingroup \nonfillstart \let\Edisplay = \nonfillfinish \gobble } % @smalldisplay (when @smallbook): @display plus smaller fonts. % \def\smalldisplayx{\begingroup \def\Esmalldisplay{\nonfillfinish\endgroup}% \smallfonts \rm \display } % @format: same as @display except don't narrow margins. % \def\format{\begingroup \let\nonarrowing = t \nonfillstart \let\Eformat = \nonfillfinish \gobble } % @smallformat (when @smallbook): @format plus smaller fonts. % \def\smallformatx{\begingroup \def\Esmallformat{\nonfillfinish\endgroup}% \smallfonts \rm \format } % @flushleft (same as @format). % \def\flushleft{\begingroup \def\Eflushleft{\nonfillfinish\endgroup}\format} % @flushright. % \def\flushright{\begingroup \let\nonarrowing = t \nonfillstart \let\Eflushright = \nonfillfinish \advance\leftskip by 0pt plus 1fill \gobble } % @quotation does normal linebreaking (hence we can't use \nonfillstart) % and narrows the margins. % \def\quotation{% \begingroup\inENV %This group ends at the end of the @quotation body {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip \singlespace \parindent=0pt % We have retained a nonzero parskip for the environment, since we're % doing normal filling. So to avoid extra space below the environment... \def\Equotation{\parskip = 0pt \nonfillfinish}% % % @cartouche defines \nonarrowing to inhibit narrowing at next level down. \ifx\nonarrowing\relax \advance\leftskip by \lispnarrowing \advance\rightskip by \lispnarrowing \exdentamount = \lispnarrowing \let\nonarrowing = \relax \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 '@' too \def\dospecials{% \do\ \do\\\do\@\do\{\do\}\do\$\do\&% \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~} % % [Knuth] p. 380 \def\uncatcodespecials{% \def\do##1{\catcode`##1=12}\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} \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 }% } \endgroup \def\setupverbatim{% % Easiest (and conventionally used) font for verbatim \tt \def\par{\leavevmode\egroup\box0\endgraf}% \catcode`\`=\active \tabexpand % 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`\{=12\catcode`\}=12 \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] %% Include LaTeX hack for completeness -- never know %% \begingroup %% \catcode`|=0 \catcode`[=1 %% \catcode`]=2\catcode`\{=12\catcode`\}=12\catcode`\ =\active %% \catcode`\\=12|gdef|doverbatim#1@end verbatim[ %% #1|endgroup|def|Everbatim[]|end[verbatim]] %% |endgroup \begingroup \catcode`\ =\active \gdef\doverbatim#1@end verbatim{#1\end{verbatim}} \endgroup % \def\verbatim{% \def\Everbatim{\nonfillfinish\endgroup}% \begingroup \nonfillstart \advance\leftskip by -\defbodyindent \begingroup\setupverbatim\doverbatim } % @verbatiminclude FILE - insert text of file in verbatim environment. % % Allow normal characters that we make active in the argument (a file name). \def\verbatiminclude{% \begingroup \catcode`\\=12 \catcode`~=12 \catcode`^=12 \catcode`_=12 \catcode`|=12 \catcode`<=12 \catcode`>=12 \catcode`+=12 \parsearg\doverbatiminclude } \def\setupverbatiminclude{% \begingroup \nonfillstart \advance\leftskip by -\defbodyindent \begingroup\setupverbatim } % \def\doverbatiminclude#1{% % Restore active chars for included file. \endgroup \begingroup \def\thisfile{#1}% \expandafter\expandafter\setupverbatiminclude\input\thisfile \endgroup\nonfillfinish\endgroup } \message{defuns,} % @defun etc. % Allow user to change definition object font (\df) internally \def\setdeffont #1 {\csname DEF#1\endcsname} \newskip\defbodyindent \defbodyindent=.4in \newskip\defargsindent \defargsindent=50pt \newskip\deftypemargin \deftypemargin=12pt \newskip\deflastargmargin \deflastargmargin=18pt \newcount\parencount % define \functionparens, which makes ( and ) and & do special things. % \functionparens affects the group it is contained in. \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 = ) {\activeparens % Now, smart parens don't turn on until &foo (see \amprm) % 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. \global\let(=\lparen \global\let)=\rparen \global\let[=\lbrack \global\let]=\rbrack \gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 } \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} % This is used to turn on special parens % but make & act ordinary (given that it's active). \gdef\boldbraxnoamp{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb\let&=\ampnr} % Definitions of (, ) and & used in args for functions. % This is the definition of ( outside of all parentheses. \gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested \global\advance\parencount by 1 } % % This is the definition of ( when already inside a level of parens. \gdef\opnested{\char`\(\global\advance\parencount by 1 } % \gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0. % also in that case restore the outer-level definition of (. \ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi \global\advance \parencount by -1 } % If we encounter &foo, then turn on ()-hacking afterwards \gdef\amprm#1 {{\rm\}\let(=\oprm \let)=\clrm\ } % \gdef\normalparens{\boldbrax\let&=\ampnr} } % End of definition inside \activeparens %% These parens (in \boldbrax) actually are a little bolder than the %% contained text. This is especially needed for [ and ] \def\opnr{{\sf\char`\(}\global\advance\parencount by 1 } \def\clnr{{\sf\char`\)}\global\advance\parencount by -1 } \let\ampnr = \& \def\lbrb{{\bf\char`\[}} \def\rbrb{{\bf\char`\]}} % Active &'s sneak into the index arguments, so make sure it's defined. { \catcode`& = 13 \global\let& = \ampnr } % First, defname, which formats the header line itself. % #1 should be the function name. % #2 should be the type of definition, such as "Function". \def\defname #1#2{% % Get the values of \leftskip and \rightskip as they were % outside the @def... \dimen2=\leftskip \advance\dimen2 by -\defbodyindent \noindent \setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}% \dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line \dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations \parshape 2 0in \dimen0 \defargsindent \dimen1 % Now output arg 2 ("Function" or some such) % ending at \deftypemargin from the right margin, % but stuck inside a box of width 0 so it does not interfere with linebreaking {% Adjust \hsize to exclude the ambient margins, % so that \rightline will obey them. \advance \hsize by -\dimen2 \rlap{\rightline{{\rm #2}\hskip -1.25pc }}}% % Make all lines underfull and no complaints: \tolerance=10000 \hbadness=10000 \advance\leftskip by -\defbodyindent \exdentamount=\defbodyindent {\df #1}\enskip % Generate function name } % Actually process the body of a definition % #1 should be the terminating control sequence, such as \Edefun. % #2 should be the "another name" control sequence, such as \defunx. % #3 should be the control sequence that actually processes the header, % such as \defunheader. \def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody \medbreak % % Define the end token that this defining construct specifies % so that it will exit this group. \def#1{\endgraf\endgroup\medbreak}% \def#2{\begingroup\obeylines\activeparens\spacesplit#3}% \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent \begingroup % \catcode 61=\active % 61 is `=' \obeylines\activeparens\spacesplit#3} % #1 is the \E... control sequence to end the definition (which we define). % #2 is the \...x control sequence for consecutive fns (which we define). % #3 is the control sequence to call to resume processing. % #4, delimited by the space, is the class name. % \def\defmethparsebody#1#2#3#4 {\begingroup\inENV % \medbreak % % Define the end token that this defining construct specifies % so that it will exit this group. \def#1{\endgraf\endgroup\medbreak}% \def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}% \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent \begingroup\obeylines\activeparens\spacesplit{#3{#4}}} % Used for @deftypemethod and @deftypeivar. % #1 is the \E... control sequence to end the definition (which we define). % #2 is the \...x control sequence for consecutive fns (which we define). % #3 is the control sequence to call to resume processing. % #4, delimited by a space, is the class name. % #5 is the method's return type. % \def\deftypemethparsebody#1#2#3#4 #5 {\begingroup\inENV \medbreak \def#1{\endgraf\endgroup\medbreak}% \def#2##1 ##2 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}{##2}}}% \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent \begingroup\obeylines\activeparens\spacesplit{#3{#4}{#5}}} % Used for @deftypeop. The change from \deftypemethparsebody is an % extra argument at the beginning which is the `category', instead of it % being the hardwired string `Method' or `Instance Variable'. We have % to account for this both in the \...x definition and in parsing the % input at hand. Thus also need a control sequence (passed as #5) for % the \E... definition to assign the category name to. % \def\deftypeopparsebody#1#2#3#4#5 #6 {\begingroup\inENV \medbreak \def#1{\endgraf\endgroup\medbreak}% \def#2##1 ##2 ##3 {% \def#4{##1}% \begingroup\obeylines\activeparens\spacesplit{#3{##2}{##3}}}% \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent \begingroup\obeylines\activeparens\spacesplit{#3{#5}{#6}}} \def\defopparsebody #1#2#3#4#5 {\begingroup\inENV % \medbreak % % Define the end token that this defining construct specifies % so that it will exit this group. \def#1{\endgraf\endgroup\medbreak}% \def#2##1 ##2 {\def#4{##1}% \begingroup\obeylines\activeparens\spacesplit{#3{##2}}}% \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent \begingroup\obeylines\activeparens\spacesplit{#3{#5}}} % These parsing functions are similar to the preceding ones % except that they do not make parens into active characters. % These are used for "variables" since they have no arguments. \def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody \medbreak % % Define the end token that this defining construct specifies % so that it will exit this group. \def#1{\endgraf\endgroup\medbreak}% \def#2{\begingroup\obeylines\spacesplit#3}% \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent \begingroup % \catcode 61=\active % \obeylines\spacesplit#3} % This is used for \def{tp,vr}parsebody. It could probably be used for % some of the others, too, with some judicious conditionals. % \def\parsebodycommon#1#2#3{% \begingroup\inENV % \medbreak % % Define the end token that this defining construct specifies % so that it will exit this group. \def#1{\endgraf\endgroup\medbreak}% \def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}% \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent \begingroup\obeylines } \def\defvrparsebody#1#2#3#4 {% \parsebodycommon{#1}{#2}{#3}% \spacesplit{#3{#4}}% } % This loses on `@deftp {Data Type} {struct termios}' -- it thinks the % type is just `struct', because we lose the braces in `{struct % termios}' when \spacesplit reads its undelimited argument. Sigh. % \let\deftpparsebody=\defvrparsebody % % So, to get around this, we put \empty in with the type name. That % way, TeX won't find exactly `{...}' as an undelimited argument, and % won't strip off the braces. % \def\deftpparsebody #1#2#3#4 {% \parsebodycommon{#1}{#2}{#3}% \spacesplit{\parsetpheaderline{#3{#4}}}\empty } % Fine, but then we have to eventually remove the \empty *and* the % braces (if any). That's what this does. % \def\removeemptybraces\empty#1\relax{#1} % After \spacesplit has done its work, this is called -- #1 is the final % thing to call, #2 the type name (which starts with \empty), and #3 % (which might be empty) the arguments. % \def\parsetpheaderline#1#2#3{% #1{\removeemptybraces#2\relax}{#3}% }% \def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV % \medbreak % % Define the end token that this defining construct specifies % so that it will exit this group. \def#1{\endgraf\endgroup\medbreak}% \def#2##1 ##2 {\def#4{##1}% \begingroup\obeylines\spacesplit{#3{##2}}}% \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent \begingroup\obeylines\spacesplit{#3{#5}}} % Split up #2 at the first space token. % call #1 with two arguments: % the first is all of #2 before the space token, % the second is all of #2 after that space token. % If #2 contains no space token, all of it is passed as the first arg % and the second is passed as empty. {\obeylines \gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}% \long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{% \ifx\relax #3% #1{#2}{}\else #1{#2}{#3#4}\fi}} % So much for the things common to all kinds of definitions. % Define @defun. % First, define the processing that is wanted for arguments of \defun % Use this to expand the args and terminate the paragraph they make up \def\defunargs#1{\functionparens \sl % Expand, preventing hyphenation at `-' chars. % Note that groups don't affect changes in \hyphenchar. % Set the font temporarily and use \font in case \setfont made \tensl a macro. {\tensl\hyphenchar\font=0}% #1% {\tensl\hyphenchar\font=45}% \ifnum\parencount=0 \else \errmessage{Unbalanced parentheses in @def}\fi% \interlinepenalty=10000 \advance\rightskip by 0pt plus 1fil \endgraf\nobreak\vskip -\parskip\nobreak } \def\deftypefunargs #1{% % Expand, preventing hyphenation at `-' chars. % Note that groups don't affect changes in \hyphenchar. % Use \boldbraxnoamp, not \functionparens, so that & is not special. \boldbraxnoamp \tclose{#1}% avoid \code because of side effects on active chars \interlinepenalty=10000 \advance\rightskip by 0pt plus 1fil \endgraf\nobreak\vskip -\parskip\nobreak } % Do complete processing of one @defun or @defunx line already parsed. % @deffn Command forward-char nchars \def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader} \def\deffnheader #1#2#3{\doind {fn}{\code{#2}}% \begingroup\defname {#2}{#1}\defunargs{#3}\endgroup % \catcode 61=\other % Turn off change made in \defparsebody } % @defun == @deffn Function \def\defun{\defparsebody\Edefun\defunx\defunheader} \def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index \begingroup\defname {#1}{\putwordDeffunc}% \defunargs {#2}\endgroup % \catcode 61=\other % Turn off change made in \defparsebody } % @deftypefun int foobar (int @var{foo}, float @var{bar}) \def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader} % #1 is the data type. #2 is the name and args. \def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax} % #1 is the data type, #2 the name, #3 the args. \def\deftypefunheaderx #1#2 #3\relax{% \doind {fn}{\code{#2}}% Make entry in function index \begingroup\defname {\defheaderxcond#1\relax$$$#2}{\putwordDeftypefun}% \deftypefunargs {#3}\endgroup % \catcode 61=\other % Turn off change made in \defparsebody } % @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar}) \def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader} % \defheaderxcond#1\relax$$$ % puts #1 in @code, followed by a space, but does nothing if #1 is null. \def\defheaderxcond#1#2$$${\ifx#1\relax\else\code{#1#2} \fi} % #1 is the classification. #2 is the data type. #3 is the name and args. \def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax} % #1 is the classification, #2 the data type, #3 the name, #4 the args. \def\deftypefnheaderx #1#2#3 #4\relax{% \doind {fn}{\code{#3}}% Make entry in function index \begingroup \normalparens % notably, turn off `&' magic, which prevents % at least some C++ text from working \defname {\defheaderxcond#2\relax$$$#3}{#1}% \deftypefunargs {#4}\endgroup % \catcode 61=\other % Turn off change made in \defparsebody } % @defmac == @deffn Macro \def\defmac{\defparsebody\Edefmac\defmacx\defmacheader} \def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index \begingroup\defname {#1}{\putwordDefmac}% \defunargs {#2}\endgroup % \catcode 61=\other % Turn off change made in \defparsebody } % @defspec == @deffn Special Form \def\defspec{\defparsebody\Edefspec\defspecx\defspecheader} \def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index \begingroup\defname {#1}{\putwordDefspec}% \defunargs {#2}\endgroup % \catcode 61=\other % Turn off change made in \defparsebody } % @defop CATEGORY CLASS OPERATION ARG... % \def\defop #1 {\def\defoptype{#1}% \defopparsebody\Edefop\defopx\defopheader\defoptype} % \def\defopheader#1#2#3{% \dosubind {fn}{\code{#2}}{\putwordon\ #1}% Make entry in function index \begingroup\defname {#2}{\defoptype\ \putwordon\ #1}% \defunargs {#3}\endgroup % } % @deftypeop CATEGORY CLASS TYPE OPERATION ARG... % \def\deftypeop #1 {\def\deftypeopcategory{#1}% \deftypeopparsebody\Edeftypeop\deftypeopx\deftypeopheader \deftypeopcategory} % % #1 is the class name, #2 the data type, #3 the operation name, #4 the args. \def\deftypeopheader#1#2#3#4{% \dosubind{fn}{\code{#3}}{\putwordon\ \code{#1}}% entry in function index \begingroup \defname{\defheaderxcond#2\relax$$$#3} {\deftypeopcategory\ \putwordon\ \code{#1}}% \deftypefunargs{#4}% \endgroup } % @deftypemethod CLASS TYPE METHOD ARG... % \def\deftypemethod{% \deftypemethparsebody\Edeftypemethod\deftypemethodx\deftypemethodheader} % % #1 is the class name, #2 the data type, #3 the method name, #4 the args. \def\deftypemethodheader#1#2#3#4{% \dosubind{fn}{\code{#3}}{\putwordon\ \code{#1}}% entry in function index \begingroup \defname{\defheaderxcond#2\relax$$$#3}{\putwordMethodon\ \code{#1}}% \deftypefunargs{#4}% \endgroup } % @deftypeivar CLASS TYPE VARNAME % \def\deftypeivar{% \deftypemethparsebody\Edeftypeivar\deftypeivarx\deftypeivarheader} % % #1 is the class name, #2 the data type, #3 the variable name. \def\deftypeivarheader#1#2#3{% \dosubind{vr}{\code{#3}}{\putwordof\ \code{#1}}% entry in variable index \begingroup \defname{\defheaderxcond#2\relax$$$#3} {\putwordInstanceVariableof\ \code{#1}}% \defvarargs{#3}% \endgroup } % @defmethod == @defop Method % \def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader} % % #1 is the class name, #2 the method name, #3 the args. \def\defmethodheader#1#2#3{% \dosubind{fn}{\code{#2}}{\putwordon\ \code{#1}}% entry in function index \begingroup \defname{#2}{\putwordMethodon\ \code{#1}}% \defunargs{#3}% \endgroup } % @defcv {Class Option} foo-class foo-flag \def\defcv #1 {\def\defcvtype{#1}% \defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype} \def\defcvarheader #1#2#3{% \dosubind {vr}{\code{#2}}{\putwordof\ #1}% Make entry in var index \begingroup\defname {#2}{\defcvtype\ \putwordof\ #1}% \defvarargs {#3}\endgroup % } % @defivar CLASS VARNAME == @defcv {Instance Variable} CLASS VARNAME % \def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader} % \def\defivarheader#1#2#3{% \dosubind {vr}{\code{#2}}{\putwordof\ #1}% entry in var index \begingroup \defname{#2}{\putwordInstanceVariableof\ #1}% \defvarargs{#3}% \endgroup } % @defvar % First, define the processing that is wanted for arguments of @defvar. % This is actually simple: just print them in roman. % This must expand the args and terminate the paragraph they make up \def\defvarargs #1{\normalparens #1% \interlinepenalty=10000 \endgraf\nobreak\vskip -\parskip\nobreak} % @defvr Counter foo-count \def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader} \def\defvrheader #1#2#3{\doind {vr}{\code{#2}}% \begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup} % @defvar == @defvr Variable \def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader} \def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index \begingroup\defname {#1}{\putwordDefvar}% \defvarargs {#2}\endgroup % } % @defopt == @defvr {User Option} \def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader} \def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index \begingroup\defname {#1}{\putwordDefopt}% \defvarargs {#2}\endgroup % } % @deftypevar int foobar \def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader} % #1 is the data type. #2 is the name, perhaps followed by text that % is actually part of the data type, which should not be put into the index. \def\deftypevarheader #1#2{% \dovarind#2 \relax% Make entry in variables index \begingroup\defname {\defheaderxcond#1\relax$$$#2}{\putwordDeftypevar}% \interlinepenalty=10000 \endgraf\nobreak\vskip -\parskip\nobreak \endgroup} \def\dovarind#1 #2\relax{\doind{vr}{\code{#1}}} % @deftypevr {Global Flag} int enable \def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader} \def\deftypevrheader #1#2#3{\dovarind#3 \relax% \begingroup\defname {\defheaderxcond#2\relax$$$#3}{#1} \interlinepenalty=10000 \endgraf\nobreak\vskip -\parskip\nobreak \endgroup} % Now define @deftp % Args are printed in bold, a slight difference from @defvar. \def\deftpargs #1{\bf \defvarargs{#1}} % @deftp Class window height width ... \def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader} \def\deftpheader #1#2#3{\doind {tp}{\code{#2}}% \begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup} % These definitions are used if you use @defunx (etc.) % anywhere other than immediately after a @defun or @defunx. % \def\defcvx#1 {\errmessage{@defcvx in invalid context}} \def\deffnx#1 {\errmessage{@deffnx in invalid context}} \def\defivarx#1 {\errmessage{@defivarx in invalid context}} \def\defmacx#1 {\errmessage{@defmacx in invalid context}} \def\defmethodx#1 {\errmessage{@defmethodx in invalid context}} \def\defoptx #1 {\errmessage{@defoptx in invalid context}} \def\defopx#1 {\errmessage{@defopx in invalid context}} \def\defspecx#1 {\errmessage{@defspecx in invalid context}} \def\deftpx#1 {\errmessage{@deftpx in invalid context}} \def\deftypefnx#1 {\errmessage{@deftypefnx in invalid context}} \def\deftypefunx#1 {\errmessage{@deftypefunx in invalid context}} \def\deftypeivarx#1 {\errmessage{@deftypeivarx in invalid context}} \def\deftypemethodx#1 {\errmessage{@deftypemethodx in invalid context}} \def\deftypeopx#1 {\errmessage{@deftypeopx in invalid context}} \def\deftypevarx#1 {\errmessage{@deftypevarx in invalid context}} \def\deftypevrx#1 {\errmessage{@deftypevrx in invalid context}} \def\defunx#1 {\errmessage{@defunx in invalid context}} \def\defvarx#1 {\errmessage{@defvarx in invalid context}} \def\defvrx#1 {\errmessage{@defvrx in invalid context}} \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\scanmacro#1{% \begingroup \newlinechar`\^^M % Undo catcode changes of \startcontents and \doprintindex \catcode`\@=0 \catcode`\\=12 \escapechar=`\@ % Append \endinput to make sure that TeX does not see the ending newline. \toks0={#1\endinput}% \immediate\openout\macscribble=\jobname.tmp \immediate\write\macscribble{\the\toks0}% \immediate\closeout\macscribble \let\xeatspaces\eatspaces \input \jobname.tmp \endgroup } \else \def\scanmacro#1{% \begingroup \newlinechar`\^^M % Undo catcode changes of \startcontents and \doprintindex \catcode`\@=0 \catcode`\\=12 \escapechar=`\@ \let\xeatspaces\eatspaces\scantokens{#1\endinput}\endgroup} \fi \newcount\paramno % Count of parameters \newtoks\macname % Macro name \newif\ifrecursive % Is it recursive? \def\macrolist{} % List of all defined macros in the form % \do\macro1\do\macro2... % Utility routines. % Thisdoes \let #1 = #2, except with \csnames. \def\cslet#1#2{% \expandafter\expandafter \expandafter\let \expandafter\expandafter \csname#1\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=12\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\macrobodyctxt{% \catcode`\~=12 \catcode`\^=12 \catcode`\_=12 \catcode`\|=12 \catcode`\<=12 \catcode`\>=12 \catcode`\+=12 \catcode`\{=12 \catcode`\}=12 \catcode`\@=12 \catcode`\^^M=12 \usembodybackslash} \def\macroargctxt{% \catcode`\~=12 \catcode`\^=12 \catcode`\_=12 \catcode`\|=12 \catcode`\<=12 \catcode`\>=12 \catcode`\+=12 \catcode`\@=12 \catcode`\\=12} % \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{The name \the\macname\space is reserved}\fi \global\cslet{macsave.\the\macname}{\the\macname}% \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% % Add the macroname to \macrolist \toks0 = \expandafter{\macrolist\do}% \xdef\macrolist{\the\toks0 \expandafter\noexpand\csname\the\macname\endcsname}% \fi \begingroup \macrobodyctxt \ifrecursive \expandafter\parsermacbody \else \expandafter\parsemacbody \fi} \def\unmacro{\parsearg\unmacroxxx} \def\unmacroxxx#1{% \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 \edef\tempa{\expandafter\noexpand\csname#1\endcsname}% \def\do##1{% \def\tempb{##1}% \ifx\tempa\tempb % remove this \else \toks0 = \expandafter{\newmacrolist\do}% \edef\newmacrolist{\the\toks0\expandafter\noexpand\tempa}% \fi}% \def\newmacrolist{}% % Execute macro list to define \newmacrolist \macrolist \global\let\macrolist\newmacrolist \endgroup \else \errmessage{Macro #1 not defined}% \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\next=#1\futurelet\nchar\braceorlinexxx} \def\braceorlinexxx{% \ifx\nchar\bgroup\else \expandafter\parsearg \fi \next} % We mant to disable all macros during \shipout so that they are not % expanded by \write. \def\turnoffmacros{\begingroup \def\do##1{\let\noexpand##1=\relax}% \edef\next{\macrolist}\expandafter\endgroup\next} % @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{\begingroup\obeyspaces\parsearg\aliasxxx} \def\aliasxxx #1{\aliasyyy#1\relax} \def\aliasyyy #1=#2\relax{\ignoreactivespaces \edef\next{\global\let\expandafter\noexpand\csname#1\endcsname=% \expandafter\noexpand\csname#2\endcsname}% \expandafter\endgroup\next} \message{cross references,} % @xref etc. \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 job is to define \lastnode. \def\node{\ENVcheck\parsearg\nodezzz} \def\nodezzz#1{\nodexxx [#1,]} \def\nodexxx[#1,#2]{\gdef\lastnode{#1}} \let\nwnode=\node \let\lastnode=\relax % The sectioning commands (@chapter, etc.) call these. \def\donoderef{% \ifx\lastnode\relax\else \expandafter\expandafter\expandafter\setref{\lastnode}% {Ysectionnumberandtype}% \global\let\lastnode=\relax \fi } \def\unnumbnoderef{% \ifx\lastnode\relax\else \expandafter\expandafter\expandafter\setref{\lastnode}{Ynothing}% \global\let\lastnode=\relax \fi } \def\appendixnoderef{% \ifx\lastnode\relax\else \expandafter\expandafter\expandafter\setref{\lastnode}% {Yappendixletterandtype}% \global\let\lastnode=\relax \fi } % @anchor{NAME} -- define xref target at arbitrary point. % \newcount\savesfregister \gdef\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} \gdef\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} \gdef\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} % \setref{NAME}{SNT} defines a cross-reference point NAME, namely % NAME-title, NAME-pg, and NAME-SNT. Called from \foonoderef. We have % to set \indexdummies so commands such as @code in a section title % aren't expanded. It would be nicer not to expand the titles in the % first place, but there's so many layers that that is hard to do. % \def\setref#1#2{{% \indexdummies \pdfmkdest{#1}% \dosetq{#1-title}{Ytitle}% \dosetq{#1-pg}{Ypagenumber}% \dosetq{#1-snt}{#2}% }} % @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\printednodename{\ignorespaces #3}% \setbox1=\hbox{\printedmanual}% \setbox0=\hbox{\printednodename}% \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\printednodename{\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\printednodename{\ignorespaces #1}% \else \ifhavexrefs % We know the real title if we have the xref values. \def\printednodename{\refx{#1-title}{}}% \else % Otherwise just copy the Info node name. \def\printednodename{\ignorespaces #1}% \fi% \fi \fi \fi % % 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. \ifpdf \leavevmode \getfilename{#4}% \ifnum\filenamelength>0 \startlink attr{/Border [0 0 0]}% goto file{\the\filename.pdf} name{#1@}% \else \startlink attr{/Border [0 0 0]}% goto name{#1@}% \fi \linkcolor \fi % \ifdim \wd1 > 0pt \putwordsection{} ``\printednodename'' \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. {\normalturnoffactive % 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 }% % [mynode], [\printednodename],\space % page 3 \turnoffactive \putwordpage\tie\refx{#1-pg}{}% \fi \endlink \endgroup} % \dosetq is the interface for calls from other macros % Use \normalturnoffactive so that punctuation chars such as underscore % and backslash work in node names. (\turnoffactive doesn't do \.) \def\dosetq#1#2{% {\let\folio=0% \normalturnoffactive \edef\next{\write\auxfile{\internalsetq{#1}{#2}}}% \iflinks \next \fi }% } % \internalsetq {foo}{page} expands into % CHARACTERS 'xrdef {foo}{...expansion of \Ypage...} % When the aux file is read, ' is the escape character \def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}} % Things to be expanded by \internalsetq \def\Ypagenumber{\folio} \def\Ytitle{\thissection} \def\Ynothing{} \def\Ysectionnumberandtype{% \ifnum\secno=0 \putwordChapter\xreftie\the\chapno % \else \ifnum \subsecno=0 \putwordSection\xreftie\the\chapno.\the\secno % \else \ifnum \subsubsecno=0 % \putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno % \else % \putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno % \fi \fi \fi } \def\Yappendixletterandtype{% \ifnum\secno=0 \putwordAppendix\xreftie'char\the\appendixno{}% \else \ifnum \subsecno=0 \putwordSection\xreftie'char\the\appendixno.\the\secno % \else \ifnum \subsubsecno=0 % \putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno % \else % \putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno % \fi \fi \fi } \gdef\xreftie{'tie} % 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 % Non-3.0. \else \def\linenumber{\the\inputlineno:\space} \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{% \expandafter\ifx\csname X#1\endcsname\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. \csname X#1\endcsname \fi #2% Output the suffix in any case. } % This is the macro invoked by entries in the aux file. % \def\xrdef#1{\begingroup % Reenable \ as an escape while reading the second argument. \catcode`\\ = 0 \afterassignment\endgroup \expandafter\gdef\csname X#1\endcsname } % Read the last existing aux file, if any. No error if none exists. \def\readauxfile{\begingroup \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 \catcode`\@=\other \catcode`\^=\other % It was suggested to define this as 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 \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 % Make the characters 128-255 be printing characters {% \count 1=128 \def\loop{% \catcode\count 1=\other \advance\count 1 by 1 \ifnum \count 1<256 \loop \fi }% }% % The aux file uses ' as the escape (for now). % Turn off \ as an escape so we do not lose on % entries which were dumped with control sequences in their names. % For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^ % Reference to such entries still does not work the way one would wish, % but at least they do not bomb out when the aux file is read in. \catcode`\{=1 \catcode`\}=2 \catcode`\%=\other \catcode`\'=0 \catcode`\\=\other % \openin 1 \jobname.aux \ifeof 1 \else \closein 1 \input \jobname.aux \global\havexrefstrue \global\warnedobstrue \fi % Open the new aux file. TeX will close it automatically at exit. \openout\auxfile=\jobname.aux \endgroup} % 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 \let\ptexfootnote=\footnote {\catcode `\@=11 % % Auto-number footnotes. Otherwise like plain. \gdef\footnote{% \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}\/\fi % % Remove inadvertent blank space before typesetting the footnote number. \unskip \thisfootno\@sf \footnotezzz }% % 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 fail inside footnotes because the tokens are fixed when % the footnote is read. --karl, 16nov96. % \long\gdef\footnotezzz{\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. \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 % % Hang the footnote text off the number. \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 } \def\fo@t{\ifcat\bgroup\noexpand\next \let\next\f@@t \else\let\next\f@t\fi \next} \def\f@@t{\bgroup\aftergroup\@foot\let\next} \def\f@t#1{#1\@foot} \def\@foot{\strut\par\egroup} }%end \catcode `\@=11 % 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 }% } % @| 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 }% }% } % 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} % @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 \closein 1 % Do not bother showing banner with post-v2.7 epsf.tex (available in % doc/epsf.tex until it shows up on ctan). \def\epsfannounce{\toks0 = }% \input epsf.tex \fi % % 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 just the usual extra ignored arg for parsing this stuff. \def\imagexxx#1,#2,#3,#4\finish{% \ifpdf \centerline{\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 \begingroup \catcode`\^^M = 5 % in case we're inside an example % If the image is by itself, center it. \ifvmode \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 \centerline{\epsfbox{#1.eps}}% \bigbreak \else % In the middle of a paragraph, no extra space. \epsfbox{#1.eps}% \fi \endgroup \fi } \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. % \def\documentlanguage{\parsearg\dodocumentlanguage} \def\dodocumentlanguage#1{% \tex % read txi-??.tex file in plain TeX. % Read the file if it exists. \openin 1 txi-#1.tex \ifeof1 \errhelp = \nolanghelp \errmessage{Cannot read language file txi-#1.tex}% \let\temp = \relax \else \def\temp{\input txi-#1.tex }% \fi \temp \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. Then whoever calls us can % set \parskip and call \setleading for \baselineskip. % \def\internalpagesizes#1#2#3#4#5#6{% \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 % \parindent = \defaultparindent \setemergencystretch } % @letterpaper (the default). \def\letterpaper{{\globaldefs = 1 \parskip = 3pt plus 2pt minus 1pt \setleading{13.2pt}% % % If page is nothing but text, make it come out even. \internalpagesizes{46\baselineskip}{6in}{\voffset}{.25in}{\bindingoffset}{36pt}% }} % Use @smallbook to reset parameters for 7x9.5 (or so) format. \def\smallbook{{\globaldefs = 1 \parskip = 2pt plus 1pt \setleading{12pt}% % \internalpagesizes{7.5in}{5.in}{\voffset}{.25in}{\bindingoffset}{16pt}% % \lispnarrowing = 0.3in \tolerance = 700 \hfuzz = 1pt \contentsrightmargin = 0pt \deftypemargin = 0pt \defbodyindent = .5cm % \let\smalldisplay = \smalldisplayx \let\smallexample = \smalllispx \let\smallformat = \smallformatx \let\smalllisp = \smalllispx }} % Use @afourpaper to print on European A4 paper. \def\afourpaper{{\globaldefs = 1 \setleading{12pt}% \parskip = 3pt plus 2pt minus 1pt % \internalpagesizes{53\baselineskip}{160mm}{\voffset}{4mm}{\bindingoffset}{44pt}% % \tolerance = 700 \hfuzz = 1pt }} % A specific text layout, 24x15cm overall, intended for A4 paper. Top margin % 29mm, hence bottom margin 28mm, nominal side margin 3cm. \def\afourlatex{{\globaldefs = 1 \setleading{13.6pt}% % \afourpaper \internalpagesizes{237mm}{150mm}{3.6mm}{3.6mm}{3mm}{7mm}% % \globaldefs = 0 }} % Use @afourwide to print on European A4 paper in wide format. \def\afourwide{% \afourpaper \internalpagesizes{6.5in}{9.5in}{\hoffset}{\normaloffset}{\bindingoffset}{7mm}% % \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. % \def\pagesizes{\parsearg\pagesizesxxx} \def\pagesizesxxx#1{\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{13.2pt}% % \internalpagesizes{#1}{\hsize}{\voffset}{\normaloffset}{\bindingoffset}{44pt}% }} % 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{$} % This macro is used to make a character print one way in ttfont % where it can probably just be output, 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\_} % Subroutine for the previous macro. \def\_{\leavevmode \kern.06em \vbox{\hrule width.3em height.1ex}} \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} %\catcode 27=\active %\def^^[{$\diamondsuit$} % Set up an active definition for =, but don't enable it most of the time. {\catcode`\==\active \global\def={{\tt \char 61}}} \catcode`+=\active \catcode`\_=\active % 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} \catcode`\@=0 % \rawbackslashxx output one backslash character in current font \global\chardef\rawbackslashxx=`\\ %{\catcode`\\=\other %@gdef@rawbackslashxx{\}} % \rawbackslash redefines \ as input to do \rawbackslashxx. {\catcode`\\=\active @gdef@rawbackslash{@let\=@rawbackslashxx }} % \normalbackslash outputs one backslash in fixed width font. \def\normalbackslash{{\tt\rawbackslashxx}} % \catcode 17=0 % Define control-q \catcode`\\=\active % Used sometimes to turn off (effectively) the active characters % even after parsing them. @def@turnoffactive{@let"=@normaldoublequote @let\=@realbackslash @let~=@normaltilde @let^=@normalcaret @let_=@normalunderscore @let|=@normalverticalbar @let<=@normalless @let>=@normalgreater @let+=@normalplus @let$=@normaldollar} @def@normalturnoffactive{@let"=@normaldoublequote @let\=@normalbackslash @let~=@normaltilde @let^=@normalcaret @let_=@normalunderscore @let|=@normalverticalbar @let<=@normalless @let>=@normalgreater @let+=@normalplus @let$=@normaldollar} % 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 back turn 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 Set initial fonts. @textfonts @rm @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: uucp-1.07/cu.h0000664000076400007640000000566007665321755006717 /* cu.h Header file for cu. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ /* The user settable variables supported by cu. */ /* The escape character used to introduce a special command. The escape character is the first character of this string. */ extern const char *zCuvar_escape; /* Whether to delay for a second before printing the host name after seeing an escape character. */ extern boolean fCuvar_delay; /* The input characters which finish a line. The escape character is only recognized following one of these characters. */ extern const char *zCuvar_eol; /* Whether to transfer binary data (nonprintable characters other than newline and tab) when sending a file. If this is FALSE, then newline is changed to carriage return. */ extern boolean fCuvar_binary; /* A prefix string to use before sending a binary character from a file; this is only used if fCuvar_binary is TRUE. */ extern const char *zCuvar_binary_prefix; /* Whether to check for echoes of characters sent when sending a file. This is ignored if fCuvar_binary is TRUE. */ extern boolean fCuvar_echocheck; /* A character to look for after each newline is sent when sending a file. The character is the first character in this string, except that a '\0' means that no echo check is done. */ extern const char *zCuvar_echonl; /* The timeout to use when looking for an character. */ extern int cCuvar_timeout; /* The character to use to kill a line if an echo check fails. The first character in this string is sent. */ extern const char *zCuvar_kill; /* The number of times to try resending a line if the echo check keeps failing. */ extern int cCuvar_resend; /* The string to send at the end of a file sent with ~>. */ extern const char *zCuvar_eofwrite; /* The string to look for to finish a file received with ~<. For tip this is a collection of single characters, but I don't want to do that because it means that there are characters which cannot be received. */ extern const char *zCuvar_eofread; /* Whether to provide verbose information when sending or receiving a file. */ extern boolean fCuvar_verbose; uucp-1.07/cu.c0000664000076400007640000014676207665321755006723 /* cu.c Call up a remote system. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char cu_rcsid[] = "$Id: cu.c,v 1.47 2002/03/05 19:10:41 ian Rel $"; #endif #include "cu.h" #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "prot.h" #include "system.h" #include "sysdep.h" #include "getopt.h" #include #include #include /* Here are the user settable variables. The user is permitted to change these while running the program, using ~s. */ /* The escape character used to introduce a special command. The escape character is the first character of this string. */ const char *zCuvar_escape = "~"; /* Whether to delay for a second before printing the host name after seeing an escape character. */ boolean fCuvar_delay = TRUE; /* The input characters which finish a line. The escape character is only recognized following one of these characters. The default is carriage return, ^U, ^C, ^O, ^D, ^S, ^Q, ^R, which I got from the Ultrix /etc/remote file. */ const char *zCuvar_eol = "\r\025\003\017\004\023\021\022"; /* Whether to transfer binary data (nonprintable characters other than newline and tab) when sending a file. If this is FALSE, then newline is changed to carriage return. */ boolean fCuvar_binary = FALSE; /* A prefix string to use before sending a binary character from a file; this is only used if fCuvar_binary is TRUE. The default is ^V. */ const char *zCuvar_binary_prefix = "\026"; /* Whether to check for echoes of characters sent when sending a file. This is ignored if fCuvar_binary is TRUE. */ boolean fCuvar_echocheck = FALSE; /* A character to look for after each newline is sent when sending a file. The character is the first character in this string, except that a '\0' means that no echo check is done. */ const char *zCuvar_echonl = "\r"; /* The timeout to use when looking for an character. */ int cCuvar_timeout = 30; /* The character to use to kill a line if an echo check fails. The first character in this string is sent. The default is ^U. */ const char *zCuvar_kill = "\025"; /* The number of times to try resending a line if the echo check keeps failing. */ int cCuvar_resend = 10; /* The string to send at the end of a file sent with ~>. The default is ^D. */ const char *zCuvar_eofwrite = "\004"; /* The string to look for to finish a file received with ~<. For tip this is a collection of single characters, but I don't want to do that because it means that there are characters which cannot be received. The default is a guess at a typical shell prompt. */ const char *zCuvar_eofread = "$"; /* Whether to provide verbose information when sending or receiving a file. */ boolean fCuvar_verbose = TRUE; /* The table used to give a value to a variable, and to print all the variable values. */ static const struct uuconf_cmdtab asCuvars[] = { { "escape", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_escape, NULL }, { "delay", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_delay, NULL }, { "eol", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eol, NULL }, { "binary", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_binary, NULL }, { "binary-prefix", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_binary_prefix, NULL }, { "echocheck", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_echocheck, NULL }, { "echonl", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_echonl, NULL }, { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cCuvar_timeout, NULL }, { "kill", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_kill, NULL }, { "resend", UUCONF_CMDTABTYPE_INT, (pointer) &cCuvar_resend, NULL }, { "eofwrite", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eofwrite, NULL }, { "eofread", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eofread, NULL }, { "verbose", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_verbose, NULL }, { NULL, 0, NULL, NULL} }; /* The string printed at the initial connect. */ #if ANSI_C #define ZCONNMSG "\aConnected." #else #define ZCONNMSG "Connected." #endif /* The string printed when disconnecting. */ #if ANSI_C #define ZDISMSG "\aDisconnected." #else #define ZDISMSG "Disconnected." #endif /* Local variables. */ /* The string we print when the user is once again connected to the port after transferring a file or taking some other action. */ static const char abCuconnected[] #if ANSI_C = "\a[connected]"; #else = "[connected]"; #endif /* Global uuconf pointer. */ static pointer pCuuuconf; /* Connection. */ static struct sconnection *qCuconn; /* Whether to close the connection. */ static boolean fCuclose_conn; /* Dialer used to dial out. */ static struct uuconf_dialer *qCudialer; /* Whether we need to restore the terminal. */ static boolean fCurestore_terminal; /* Whether we are doing local echoing. */ static boolean fCulocalecho; /* Whether we need to call fsysdep_cu_finish. */ static boolean fCustarted; /* Whether ZCONNMSG has been printed yet. */ static boolean fCuconnprinted = FALSE; /* A structure used to pass information to icuport_lock. */ struct sconninfo { boolean fmatched; boolean flocked; boolean fdirect; struct sconnection *qconn; const char *zline; }; /* Local functions. */ static void ucuusage P((void)); static void ucuhelp P((void)); static void ucuabort P((void)); static void uculog_start P((void)); static void uculog_end P((void)); static int icuport_lock P((struct uuconf_port *qport, pointer pinfo)); static boolean fcudo_cmd P((pointer puuconf, struct sconnection *qconn, int bcmd)); static boolean fcuset_var P((pointer puuconf, char *zline)); static int icuunrecogvar P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int icuunrecogfn P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static void uculist_vars P((void)); static void uculist_fns P((const char *zescape)); static boolean fcudo_subcmd P((pointer puuconf, struct sconnection *qconn, char *zline)); static boolean fcusend_buf P((struct sconnection *qconn, const char *zbuf, size_t cbuf)); #define ucuputs(zline) \ do { if (! fsysdep_terminal_puts (zline)) ucuabort (); } while (0) /* Long getopt options. */ static const struct option asCulongopts[] = { { "phone", required_argument, NULL, 'c' }, { "escape", required_argument, NULL, 'E' }, { "parity", required_argument, NULL, 2 }, { "halfduplex", no_argument, NULL, 'h' }, { "prompt", no_argument, NULL, 'n' }, { "line", required_argument, NULL, 'l' }, { "port", required_argument, NULL, 'p' }, { "speed", required_argument, NULL, 's' }, { "baud", required_argument, NULL, 's' }, { "mapcr", no_argument, NULL, 't' }, { "nostop", no_argument, NULL, 3 }, { "system", required_argument, NULL, 'z' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; int main (argc, argv) int argc; char **argv; { /* -c: phone number. */ char *zphone = NULL; /* -e: even parity. */ boolean feven = FALSE; /* -l: line. */ char *zline = NULL; /* -n: prompt for phone number. */ boolean fprompt = FALSE; /* -o: odd parity. */ boolean fodd = FALSE; /* -p: port name. */ const char *zport = NULL; /* -s: speed. */ long ibaud = 0L; /* -t: map cr to crlf. */ boolean fmapcr = FALSE; /* -z: system. */ const char *zsystem = NULL; /* --nostop: turn off XON/XOFF. */ enum txonxoffsetting txonxoff = XONXOFF_ON; /* -I: configuration file name. */ const char *zconfig = NULL; int iopt; pointer puuconf; int iuuconf; const char *zlocalname; int i; struct uuconf_system ssys; const struct uuconf_system *qsys = NULL; boolean flooped; struct uuconf_port sport; struct sconnection sconn; struct sconninfo sinfo; long ihighbaud; struct uuconf_dialer sdialer; struct uuconf_dialer *qdialer; char bcmd; zProgram = argv[0]; /* We want to accept -# as a speed. It's easiest to look through the arguments, replace -# with -s#, and let getopt handle it. */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-' && isdigit (BUCHAR (argv[i][1]))) { size_t clen; char *z; clen = strlen (argv[i]); z = zbufalc (clen + 2); z[0] = '-'; z[1] = 's'; memcpy (z + 2, argv[i] + 1, clen); argv[i] = z; } } while ((iopt = getopt_long (argc, argv, "a:c:deE:hnI:l:op:s:tvx:z:", asCulongopts, (int *) NULL)) != EOF) { switch (iopt) { case 'c': /* Phone number. */ zphone = optarg; break; case 'd': /* Set debugging level to maximum. */ #if DEBUG > 1 iDebug = DEBUG_MAX; #endif break; case 'e': /* Even parity. */ feven = TRUE; break; case 'E': /* Escape character. */ zCuvar_escape = optarg; break; case 'h': /* Local echo. */ fCulocalecho = TRUE; break; case 'n': /* Prompt for phone number. */ fprompt = TRUE; break; case 'l': /* Line name. */ zline = optarg; break; case 'o': /* Odd parity. */ fodd = TRUE; break; case 'p': case 'a': /* Port name (-a is for compatibility). */ zport = optarg; break; case 's': /* Speed. */ ibaud = strtol (optarg, (char **) NULL, 10); break; case 't': /* Map cr to crlf. */ fmapcr = TRUE; break; case 'z': /* System name. */ zsystem = optarg; break; case 'I': /* Configuration file name. */ if (fsysdep_other_config (optarg)) zconfig = optarg; break; case 'x': #if DEBUG > 1 /* Set debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'v': /* Print version and exit. */ printf ("cu (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 2: /* --parity. */ if (strncmp (optarg, "even", strlen (optarg)) == 0) feven = TRUE; else if (strncmp (optarg, "odd", strlen (optarg)) == 0) fodd = TRUE; else if (strncmp (optarg, "none", strlen (optarg)) == 0) { feven = TRUE; fodd = TRUE; } else { fprintf (stderr, "%s: --parity requires even, odd or none\n", zProgram); ucuusage (); } break; case 3: /* --nostop. */ txonxoff = XONXOFF_OFF; break; case 1: /* --help. */ ucuhelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found and flag set. */ break; default: ucuusage (); /*NOTREACHED*/ } } /* There can be one more argument, which is either a system name, a phone number, or "dir". We decide which it is based on the first character. To call a UUCP system whose name begins with a digit, or one which is named "dir", you must use -z. */ if (optind != argc) { if (optind != argc - 1 || zsystem != NULL || zphone != NULL) { fprintf (stderr, "%s: too many arguments\n", zProgram); ucuusage (); } if (strcmp (argv[optind], "dir") != 0) { if (isdigit (BUCHAR (argv[optind][0]))) zphone = argv[optind]; else zsystem = argv[optind]; } } /* If the user doesn't give a system, port, line or speed, then there's no basis on which to select a port. */ if (zsystem == NULL && zport == NULL && zline == NULL && ibaud == 0L) { fprintf (stderr, "%s: must specify system, line, port or speed\n", zProgram); ucuusage (); } if (fprompt) { size_t cphone; printf ("Phone number: "); (void) fflush (stdout); zphone = NULL; cphone = 0; if (getline (&zphone, &cphone, stdin) <= 0 || *zphone == '\0') { fprintf (stderr, "%s: no phone number entered\n", zProgram); exit (EXIT_FAILURE); } } iuuconf = uuconf_init (&puuconf, "cu", zconfig); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); pCuuuconf = puuconf; #if DEBUG > 1 { const char *zdebug; iuuconf = uuconf_debuglevel (puuconf, &zdebug); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (zdebug != NULL) iDebug |= idebug_parse (zdebug); } #endif usysdep_initialize (puuconf, INIT_NOCHDIR | INIT_SUID); iuuconf = uuconf_localname (puuconf, &zlocalname); if (iuuconf == UUCONF_NOT_FOUND) { zlocalname = zsysdep_localname (); if (zlocalname == NULL) exit (EXIT_FAILURE); } else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); ulog_fatal_fn (ucuabort); pfLstart = uculog_start; pfLend = uculog_end; #ifdef SIGINT usysdep_signal (SIGINT); #endif #ifdef SIGHUP usysdep_signal (SIGHUP); #endif #ifdef SIGQUIT usysdep_signal (SIGQUIT); #endif #ifdef SIGTERM usysdep_signal (SIGTERM); #endif #ifdef SIGPIPE usysdep_signal (SIGPIPE); #endif if (zsystem != NULL) { iuuconf = uuconf_system_info (puuconf, zsystem, &ssys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); ulog (LOG_FATAL, "%s: System not found", zsystem); } qsys = &ssys; } /* This loop is used if a system is specified. It loops over the various alternates until it finds one for which the dial succeeds. This is an ugly spaghetti construction, and it should be broken up into different functions someday. */ flooped = FALSE; while (TRUE) { enum tparitysetting tparity; enum tstripsetting tstrip; long iusebaud; /* The uuconf_find_port function only selects directly on a port name and a speed. To select based on the line name, we use a function. If we can't find any defined port, and the user specified a line name but did not specify a port name or a system or a phone number, then we fake a direct port with that line name (we don't fake a port if a system or phone number were given because if we fake a port we have no way to place a call; perhaps we should automatically look up a particular dialer). This permits users to say cu -lttyd0 without having to put ttyd0 in the ports file, provided they have read and write access to the port. */ sinfo.fmatched = FALSE; sinfo.flocked = FALSE; sinfo.fdirect = qsys == NULL && zphone == NULL; sinfo.qconn = &sconn; sinfo.zline = zline; if (zport != NULL || zline != NULL || ibaud != 0L) { iuuconf = uuconf_find_port (puuconf, zport, ibaud, 0L, icuport_lock, (pointer) &sinfo, &sport); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) { if (sinfo.flocked) { (void) fconn_unlock (&sconn); uconn_free (&sconn); } ulog_uuconf (LOG_FATAL, puuconf, iuuconf); } if (zline == NULL || zport != NULL || zphone != NULL || qsys != NULL) { if (sinfo.fmatched) ulog (LOG_FATAL, "All matching ports in use"); else ulog (LOG_FATAL, "No matching ports"); } sport.uuconf_zname = zline; sport.uuconf_ttype = UUCONF_PORTTYPE_DIRECT; sport.uuconf_zprotocols = NULL; sport.uuconf_qproto_params = NULL; sport.uuconf_ireliable = 0; sport.uuconf_zlockname = NULL; sport.uuconf_palloc = NULL; sport.uuconf_u.uuconf_sdirect.uuconf_zdevice = NULL; sport.uuconf_u.uuconf_sdirect.uuconf_ibaud = ibaud; if (! fconn_init (&sport, &sconn, UUCONF_PORTTYPE_UNKNOWN)) ucuabort (); if (! fconn_lock (&sconn, FALSE, TRUE)) ulog (LOG_FATAL, "%s: Line in use", zline); qCuconn = &sconn; /* Check user access after locking the port, because on some systems shared lines affect the ownership and permissions. In such a case ``Line in use'' is more clear than ``Permission denied.'' */ if (! fsysdep_port_access (&sport)) ulog (LOG_FATAL, "%s: Permission denied", zline); } iusebaud = ibaud; ihighbaud = 0L; } else { for (; qsys != NULL; qsys = qsys->uuconf_qalternate) { if (! qsys->uuconf_fcall) continue; if (qsys->uuconf_qport != NULL) { if (fconn_init (qsys->uuconf_qport, &sconn, UUCONF_PORTTYPE_UNKNOWN)) { if (fconn_lock (&sconn, FALSE, FALSE)) { qCuconn = &sconn; break; } uconn_free (&sconn); } } else { sinfo.fmatched = FALSE; sinfo.flocked = FALSE; sinfo.qconn = &sconn; iuuconf = uuconf_find_port (puuconf, qsys->uuconf_zport, qsys->uuconf_ibaud, qsys->uuconf_ihighbaud, icuport_lock, (pointer) &sinfo, &sport); if (iuuconf == UUCONF_SUCCESS) break; if (iuuconf != UUCONF_NOT_FOUND) { if (sinfo.flocked) { (void) fconn_unlock (&sconn); uconn_free (&sconn); } ulog_uuconf (LOG_FATAL, puuconf, iuuconf); } } } if (qsys == NULL) { const char *zrem; if (flooped) zrem = "remaining "; else zrem = ""; if (sinfo.fmatched) ulog (LOG_FATAL, "%s: All %smatching ports in use", zsystem, zrem); else ulog (LOG_FATAL, "%s: No %smatching ports", zsystem, zrem); } iusebaud = qsys->uuconf_ibaud; ihighbaud = qsys->uuconf_ihighbaud; } /* Here we have locked a connection to use. */ if (! fconn_open (&sconn, iusebaud, ihighbaud, FALSE, sinfo.fdirect)) ucuabort (); fCuclose_conn = TRUE; if (FGOT_SIGNAL ()) ucuabort (); /* Set up the connection. */ if (fodd && feven) { tparity = PARITYSETTING_NONE; tstrip = STRIPSETTING_SEVENBITS; } else if (fodd) { tparity = PARITYSETTING_ODD; tstrip = STRIPSETTING_SEVENBITS; } else if (feven) { tparity = PARITYSETTING_EVEN; tstrip = STRIPSETTING_SEVENBITS; } else { tparity = PARITYSETTING_DEFAULT; tstrip = STRIPSETTING_DEFAULT; } if (! fconn_set (&sconn, tparity, tstrip, txonxoff)) ucuabort (); if (qsys != NULL) zphone = qsys->uuconf_zphone; if (qsys != NULL || zphone != NULL) { enum tdialerfound tdialer; if (! fconn_dial (&sconn, puuconf, qsys, zphone, &sdialer, &tdialer)) { if (zport != NULL || zline != NULL || ibaud != 0L || qsys == NULL) ucuabort (); qsys = qsys->uuconf_qalternate; if (qsys == NULL) ulog (LOG_FATAL, "%s: No remaining alternates", zsystem); fCuclose_conn = FALSE; (void) fconn_close (&sconn, pCuuuconf, qCudialer, FALSE); qCuconn = NULL; (void) fconn_unlock (&sconn); uconn_free (&sconn); /* Loop around and try another alternate. */ flooped = TRUE; continue; } if (tdialer == DIALERFOUND_FALSE) qdialer = NULL; else qdialer = &sdialer; } else { /* If no system or phone number was specified, we connect directly to the modem. We only permit this if the user has access to the port, since it permits various shenanigans such as reprogramming the automatic callbacks. */ if (! fsysdep_port_access (sconn.qport)) ulog (LOG_FATAL, "Access to port denied"); qdialer = NULL; if (! fconn_carrier (&sconn, FALSE)) ulog (LOG_FATAL, "Can't turn off carrier"); } break; } qCudialer = qdialer; if (FGOT_SIGNAL ()) ucuabort (); /* Here we have connected, and can start the main cu protocol. The program spends most of its time in system dependent code, and only comes out when a special command is received from the terminal. */ printf ("%s\n", ZCONNMSG); fCuconnprinted = TRUE; if (! fsysdep_terminal_raw (fCulocalecho)) ucuabort (); fCurestore_terminal = TRUE; if (! fsysdep_cu_init (&sconn)) ucuabort (); fCustarted = TRUE; while (fsysdep_cu (&sconn, &bcmd, zlocalname)) if (! fcudo_cmd (puuconf, &sconn, bcmd)) break; fCustarted = FALSE; if (! fsysdep_cu_finish ()) ucuabort (); fCurestore_terminal = FALSE; (void) fsysdep_terminal_restore (); (void) fconn_close (&sconn, puuconf, qdialer, TRUE); (void) fconn_unlock (&sconn); uconn_free (&sconn); if (fCuconnprinted) printf ("\n%s\n", ZDISMSG); ulog_close (); usysdep_exit (TRUE); /* Avoid errors about not returning a value. */ return 0; } /* Print a usage message and die. */ static void ucuusage () { fprintf (stderr, "Usage: %s [options] [system or phone-number]\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EXIT_FAILURE); } /* Print a help message. */ static void ucuhelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); printf ("Usage: %s [options] [system or phone-number]\n", zProgram); printf (" -a,-p,--port port: Use named port\n"); printf (" -l,--line line: Use named device (e.g. tty0)\n"); printf (" -s,--speed,--baud speed, -#: Use given speed\n"); printf (" -c,--phone phone: Phone number to call\n"); printf (" -z,--system system: System to call\n"); printf (" -e: Set even parity\n"); printf (" -o: Set odd parity\n"); printf (" --parity={odd,even}: Set parity\n"); printf (" -E,--escape char: Set escape character\n"); printf (" -h,--halfduplex: Echo locally\n"); printf (" --nostop: Turn off XON/XOFF handling\n"); printf (" -t,--mapcr: Map carriage return to carriage return/linefeed\n"); printf (" -n,--prompt: Prompt for phone number\n"); printf (" -d: Set maximum debugging level\n"); printf (" -x,--debug debug: Set debugging type\n"); #if HAVE_TAYLOR_CONFIG printf (" -I,--config file: Set configuration file to use\n"); #endif /* HAVE_TAYLOR_CONFIG */ printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } /* This function is called when a fatal error occurs. */ static void ucuabort () { if (fCustarted) { fCustarted = FALSE; (void) fsysdep_cu_finish (); } if (fCurestore_terminal) { fCurestore_terminal = FALSE; (void) fsysdep_terminal_restore (); } if (qCuconn != NULL) { struct sconnection *qconn; if (fCuclose_conn) { fCuclose_conn = FALSE; (void) fconn_close (qCuconn, pCuuuconf, qCudialer, FALSE); } qconn = qCuconn; qCuconn = NULL; (void) fconn_unlock (qconn); uconn_free (qconn); } ulog_close (); if (fCuconnprinted) printf ("\n%s\n", ZDISMSG); usysdep_exit (FALSE); } /* This variable is just used to communicate between uculog_start and uculog_end. */ static boolean fCulog_restore; /* This function is called by ulog before it output anything. We use it to restore the terminal, if necessary. ulog is only called for errors or debugging in cu, so it's not too costly to do this. If we didn't do it, then at least on Unix each line would leave the cursor in the same column rather than wrapping back to the start, since CRMOD will not be on. */ static void uculog_start () { if (! fCurestore_terminal) fCulog_restore = FALSE; else { fCulog_restore = TRUE; fCurestore_terminal = FALSE; if (! fsysdep_terminal_restore ()) ucuabort (); } } /* This function is called by ulog after everything is output. It sets the terminal back, if necessary. */ static void uculog_end () { if (fCulog_restore) { if (! fsysdep_terminal_raw (fCulocalecho)) ucuabort (); fCurestore_terminal = TRUE; } } /* Check to see if this port has the desired line, to handle the -l option. If it does, or if no line was specified, set up a connection and lock it. */ static int icuport_lock (qport, pinfo) struct uuconf_port *qport; pointer pinfo; { struct sconninfo *q = (struct sconninfo *) pinfo; if (q->zline != NULL && ! fsysdep_port_is_line (qport, q->zline)) return UUCONF_NOT_FOUND; q->fmatched = TRUE; if (! fconn_init (qport, q->qconn, UUCONF_PORTTYPE_UNKNOWN)) return UUCONF_NOT_FOUND; else if (! fconn_lock (q->qconn, FALSE, q->fdirect)) { uconn_free (q->qconn); return UUCONF_NOT_FOUND; } else { qCuconn = q->qconn; q->flocked = TRUE; return UUCONF_SUCCESS; } } /* Execute a cu escape command. Return TRUE if the connection should continue, or FALSE if the connection should be terminated. */ static boolean fcudo_cmd (puuconf, qconn, bcmd) pointer puuconf; struct sconnection *qconn; int bcmd; { char *zline; char *z; char abescape[5]; boolean fret; size_t clen; char abbuf[100]; /* Some commands take a string up to the next newline character. */ switch (bcmd) { default: zline = NULL; break; case '!': case '$': case '|': case '+': case '%': case 'c': case '>': case '<': case 'p': case 't': case 's': { zline = zsysdep_terminal_line ((const char *) NULL); if (zline == NULL) ucuabort (); zline[strcspn (zline, "\n")] = '\0'; } break; } switch (bcmd) { default: if (! isprint (*zCuvar_escape)) sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape)); else { abescape[0] = *zCuvar_escape; abescape[1] = '\0'; } sprintf (abbuf, "[Unrecognized. Use %s%s to send %s]", abescape, abescape, abescape); ucuputs (abbuf); return TRUE; case '.': /* Hangup. */ return FALSE; case '!': case '$': case '|': case '+': /* Shell out. */ if (! fsysdep_cu_copy (FALSE) || ! fsysdep_terminal_restore ()) ucuabort (); fCurestore_terminal = FALSE; { enum tshell_cmd t; switch (bcmd) { default: case '!': t = SHELL_NORMAL; break; case '$': t = SHELL_STDOUT_TO_PORT; break; case '|': t = SHELL_STDIN_FROM_PORT; break; case '+': t = SHELL_STDIO_ON_PORT; break; } (void) fsysdep_shell (qconn, zline, t); } if (! fsysdep_cu_copy (TRUE) || ! fsysdep_terminal_raw (fCulocalecho)) ucuabort (); fCurestore_terminal = TRUE; ubuffree (zline); return TRUE; case '%': fret = fcudo_subcmd (puuconf, qconn, zline); ubuffree (zline); return fret; case '#': if (! fconn_break (qconn)) ucuabort (); return TRUE; case 'c': (void) fsysdep_chdir (zline); ubuffree (zline); return TRUE; case '>': case '<': case 'p': case 't': clen = strlen (zline); z = zbufalc (clen + 3); z[0] = bcmd; z[1] = ' '; memcpy (z + 2, zline, clen + 1); ubuffree (zline); fret = fcudo_subcmd (puuconf, qconn, z); ubuffree (z); return fret; case 'z': if (! fsysdep_cu_copy (FALSE) || ! fsysdep_terminal_restore ()) ucuabort (); fCurestore_terminal = FALSE; if (! fsysdep_suspend ()) ucuabort (); if (! fsysdep_cu_copy (TRUE) || ! fsysdep_terminal_raw (fCulocalecho)) ucuabort (); fCurestore_terminal = TRUE; return TRUE; case 's': fret = fcuset_var (puuconf, zline); ubuffree (zline); return fret; case 'v': uculist_vars (); return TRUE; case '?': if (! isprint (*zCuvar_escape)) sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape)); else { abescape[0] = *zCuvar_escape; abescape[1] = '\0'; } ucuputs (""); ucuputs ("[Escape sequences]"); sprintf (abbuf, "[%s. hangup] [%s!CMD run shell]", abescape, abescape); ucuputs (abbuf); sprintf (abbuf, "[%s$CMD stdout to remote] [%s|CMD stdin from remote]", abescape, abescape); ucuputs (abbuf); sprintf (abbuf, "[%s+CMD stdin and stdout to remote]", abescape); ucuputs (abbuf); sprintf (abbuf, "[%s# send break] [%scDIR change directory]", abescape, abescape); ucuputs (abbuf); sprintf (abbuf, "[%s> send file] [%s< receive file]", abescape, abescape); ucuputs (abbuf); sprintf (abbuf, "[%spFROM TO send to Unix] [%stFROM TO receive from Unix]", abescape, abescape); ucuputs (abbuf); sprintf (abbuf, "[%ssVAR VAL set variable] [%ssVAR set boolean]", abescape, abescape); ucuputs (abbuf); sprintf (abbuf, "[%ss!VAR unset boolean] [%sv list variables]", abescape, abescape); ucuputs (abbuf); #ifdef SIGTSTP sprintf (abbuf, "[%sz suspend]", abescape); ucuputs (abbuf); #endif uculist_fns (abescape); return TRUE; } } /* List ~% functions. */ static void uculist_fns (zescape) const char *zescape; { char abbuf[100]; sprintf (abbuf, "[%s%%break send break] [%s%%cd DIR change directory]", zescape, zescape); ucuputs (abbuf); sprintf (abbuf, "[%s%%put FROM TO send file] [%s%%take FROM TO receive file]", zescape, zescape); ucuputs (abbuf); sprintf (abbuf, "[%s%%nostop no XON/XOFF] [%s%%stop use XON/XOFF]", zescape, zescape); ucuputs (abbuf); } /* Set a variable. */ static boolean fcuset_var (puuconf, zline) pointer puuconf; char *zline; { char *zvar, *zval; char *azargs[2]; int iuuconf; zvar = strtok (zline, "= \t"); if (zvar == NULL) { ucuputs (abCuconnected); return TRUE; } zval = strtok ((char *) NULL, " \t"); if (zval == NULL) { azargs[0] = zvar; if (azargs[0][0] != '!') azargs[1] = zbufcpy ("t"); else { ++azargs[0]; azargs[1] = zbufcpy ("f"); } } else { azargs[0] = zvar; azargs[1] = zbufcpy (zval); } iuuconf = uuconf_cmd_args (puuconf, 2, azargs, asCuvars, (pointer) NULL, icuunrecogvar, 0, (pointer) NULL); if ((iuuconf & UUCONF_CMDTABRET_KEEP) == 0) ubuffree (azargs[1]); if ((iuuconf &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return TRUE; } /* Warn about an unknown variable. */ /*ARGSUSED*/ static int icuunrecogvar (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { char abescape[5]; if (! isprint (*zCuvar_escape)) sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape)); else { abescape[0] = *zCuvar_escape; abescape[1] = '\0'; } ulog (LOG_ERROR, "%s: unknown variable (%sv lists variables)", argv[0], abescape); return UUCONF_CMDTABRET_CONTINUE; } /* List all the variables with their values. */ static void uculist_vars () { const struct uuconf_cmdtab *q; char abbuf[100]; ucuputs (""); for (q = asCuvars; q->uuconf_zcmd != NULL; q++) { switch (UUCONF_TTYPE_CMDTABTYPE (q->uuconf_itype)) { case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_BOOLEAN): if (*(boolean *) q->uuconf_pvar) sprintf (abbuf, "%s true", q->uuconf_zcmd); else sprintf (abbuf, "%s false", q->uuconf_zcmd); break; case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_INT): sprintf (abbuf, "%s %d", q->uuconf_zcmd, *(int *) q->uuconf_pvar); break; case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_LONG): sprintf (abbuf, "%s %ld", q->uuconf_zcmd, *(long *) q->uuconf_pvar); break; case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_STRING): case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_FULLSTRING): { const char *z; char abchar[5]; size_t clen; sprintf (abbuf, "%s ", q->uuconf_zcmd); clen = strlen (abbuf); for (z = *(const char **) q->uuconf_pvar; *z != '\0'; z++) { int cchar; if (! isprint (*z)) { sprintf (abchar, "\\%03o", BUCHAR (*z)); cchar = 4; } else { abchar[0] = *z; abchar[1] = '\0'; cchar = 1; } if (clen + cchar < sizeof (abbuf)) strcat (abbuf, abchar); clen += cchar; } } break; default: sprintf (abbuf, "%s [unprintable type]", q->uuconf_zcmd); break; } ucuputs (abbuf); } } /* Subcommands. These are commands that begin with ~%. */ /* This variable is only used so that we can pass a non-NULL address in pvar. It is never assigned to or examined. */ static char bCutype; /* The command table for the subcommands. */ static int icubreak P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int icudebug P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int icuchdir P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int icuput P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int icutake P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int icunostop P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static const struct uuconf_cmdtab asCucmds[] = { { "break", UUCONF_CMDTABTYPE_FN | 1, NULL, icubreak }, { "b", UUCONF_CMDTABTYPE_FN | 1, NULL, icubreak }, { "cd", UUCONF_CMDTABTYPE_FN | 0, NULL, icuchdir }, { "d", UUCONF_CMDTABTYPE_FN | 1, NULL, icudebug }, { "put", UUCONF_CMDTABTYPE_FN | 0, NULL, icuput }, { "take", UUCONF_CMDTABTYPE_FN | 0, NULL, icutake }, { "nostop", UUCONF_CMDTABTYPE_FN | 1, NULL, icunostop }, { "stop", UUCONF_CMDTABTYPE_FN | 1, &bCutype, icunostop }, { ">", UUCONF_CMDTABTYPE_FN | 0, &bCutype, icuput }, { "<", UUCONF_CMDTABTYPE_FN | 0, &bCutype, icutake }, { "p", UUCONF_CMDTABTYPE_FN | 0, NULL, icuput }, { "t", UUCONF_CMDTABTYPE_FN | 0, NULL, icutake }, { NULL, 0, NULL, NULL } }; /* Do a subcommand. This is called by commands beginning with ~%. */ static boolean fcudo_subcmd (puuconf, qconn, zline) pointer puuconf; struct sconnection *qconn; char *zline; { char *azargs[3]; int iarg; int iuuconf; for (iarg = 0; iarg < 3; iarg++) { azargs[iarg] = strtok (iarg == 0 ? zline : (char *) NULL, " \t\n"); if (azargs[iarg] == NULL) break; } if (iarg == 0) { ucuputs (abCuconnected); return TRUE; } iuuconf = uuconf_cmd_args (puuconf, iarg, azargs, asCucmds, (pointer) qconn, icuunrecogfn, 0, (pointer) NULL); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return TRUE; } /* Warn about an unknown function. */ /*ARGSUSED*/ static int icuunrecogfn (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { char abescape[5]; if (! isprint (*zCuvar_escape)) sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape)); else { abescape[0] = *zCuvar_escape; abescape[1] = '\0'; } if (argv[0][0] == '?') uculist_fns (abescape); else ulog (LOG_ERROR, "%s: unknown (%s%%? lists choices)", argv[0], abescape); return UUCONF_CMDTABRET_CONTINUE; } /* Send a break. */ /*ARGSUSED*/ static int icubreak (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sconnection *qconn = (struct sconnection *) pinfo; if (! fconn_break (qconn)) ucuabort (); return UUCONF_CMDTABRET_CONTINUE; } /* Change directories. */ /*ARGSUSED*/ static int icuchdir (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { const char *zarg; if (argc <= 1) zarg = NULL; else zarg = argv[1]; (void) fsysdep_chdir (zarg); return UUCONF_CMDTABRET_CONTINUE; } /* Toggle debugging. */ /*ARGSUSED*/ static int icudebug (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { #if DEBUG > 1 if (iDebug != 0) iDebug = 0; else iDebug = DEBUG_MAX; #else ucuputs ("[compiled without debugging]"); #endif return UUCONF_CMDTABRET_CONTINUE; } /* Control whether the port does xon/xoff handshaking. If pvar is not NULL, this is "stop"; otherwise it is "nostop". */ /*ARGSUSED*/ static int icunostop (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar; pointer pinfo; { struct sconnection *qconn = (struct sconnection *) pinfo; if (! fconn_set (qconn, PARITYSETTING_DEFAULT, STRIPSETTING_DEFAULT, pvar == NULL ? XONXOFF_OFF : XONXOFF_ON)) ucuabort (); return UUCONF_CMDTABRET_CONTINUE; } /* Send a file to the remote system. The first argument is the file to send. If that argument is not present, it is prompted for. The second argument is to file name to use on the remote system. If that argument is not present, the basename of the local filename is used. If pvar is not NULL, then this is ~>, which is used to send a command to a non-Unix system. We treat is the same as ~%put, except that we assume the user has already entered the appropriate command (for ~%put, we force ``cat >to'' to the other side). */ /*ARGSUSED*/ static int icuput (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar; pointer pinfo; { struct sconnection *qconn = (struct sconnection *) pinfo; char *zfrom; char *zto = NULL; char *zalc; openfile_t e; int cline; char *zbuf; size_t cbuf; if (argc > 1) zfrom = zbufcpy (argv[1]); else { zfrom = zsysdep_terminal_line ("File to send: "); if (zfrom == NULL) ucuabort (); zfrom[strcspn (zfrom, " \t\n")] = '\0'; if (*zfrom == '\0') { ubuffree (zfrom); ucuputs (abCuconnected); return UUCONF_CMDTABRET_CONTINUE; } } if (pvar == NULL) { if (argc > 2) zto = zbufcpy (argv[2]); else { char *zbase; char *zprompt; zbase = zsysdep_base_name (zfrom); if (zbase == NULL) ucuabort (); zprompt = zbufalc (sizeof "Remote file name []: " + strlen (zbase)); sprintf (zprompt, "Remote file name [%s]: ", zbase); zto = zsysdep_terminal_line (zprompt); ubuffree (zprompt); if (zto == NULL) ucuabort (); zto[strcspn (zto, " \t\n")] = '\0'; if (*zto != '\0') ubuffree (zbase); else { ubuffree (zto); zto = zbase; } } } e = esysdep_user_fopen (zfrom, TRUE, fCuvar_binary); if (! ffileisopen (e)) { const char *zerrstr; if (pvar == NULL) ubuffree (zto); zerrstr = strerror (errno); zalc = zbufalc (strlen (zfrom) + sizeof ": " + strlen (zerrstr)); sprintf (zalc, "%s: %s", zfrom, zerrstr); ubuffree (zfrom); ucuputs (zalc); ubuffree (zalc); ucuputs (abCuconnected); return UUCONF_CMDTABRET_CONTINUE; } ubuffree (zfrom); /* Tell the system dependent layer to stop copying data from the port to the terminal. We want to read the echoes ourself. Also permit the local user to generate signals. */ if (! fsysdep_cu_copy (FALSE) || ! fsysdep_terminal_signals (TRUE)) ucuabort (); /* If pvar is NULL, then we are sending a file to a Unix system. We send over the command "cat > TO" to prepare it to receive. If pvar is not NULL, the user is assumed to have set up whatever action was needed to receive the file. */ if (pvar == NULL) { boolean fret; zalc = zbufalc (sizeof "cat > \n" + strlen (zto)); sprintf (zalc, "cat > %s\n", zto); ubuffree (zto); fret = fcusend_buf (qconn, zalc, strlen (zalc)); ubuffree (zalc); if (! fret) { (void) ffileclose (e); if (! fsysdep_cu_copy (TRUE) || ! fsysdep_terminal_signals (FALSE)) ucuabort (); ucuputs (abCuconnected); return UUCONF_CMDTABRET_CONTINUE; } } cline = 0; zbuf = NULL; cbuf = 0; while (TRUE) { char abbuf[512]; size_t c; #if USE_STDIO if (fCuvar_binary) #endif { if (ffileeof (e)) break; c = cfileread (e, abbuf, sizeof abbuf); if (ffileioerror (e, c)) { ucuputs ("[file read error]"); break; } if (c == 0) break; zbuf = abbuf; } #if USE_STDIO else { if (getline (&zbuf, &cbuf, e) <= 0) { xfree ((pointer) zbuf); break; } c = strlen (zbuf); } #endif if (fCuvar_verbose) { ++cline; printf ("%d ", cline); (void) fflush (stdout); } if (! fcusend_buf (qconn, zbuf, c)) { if (! fCuvar_binary) xfree ((pointer) zbuf); (void) fclose (e); if (! fsysdep_cu_copy (TRUE) || ! fsysdep_terminal_signals (FALSE)) ucuabort (); ucuputs (abCuconnected); return UUCONF_CMDTABRET_CONTINUE; } } (void) ffileclose (e); if (pvar == NULL) { char beof; beof = '\004'; if (! fconn_write (qconn, &beof, 1)) ucuabort (); } else { if (*zCuvar_eofwrite != '\0') { if (! fconn_write (qconn, zCuvar_eofwrite, strlen (zCuvar_eofwrite))) ucuabort (); } } if (fCuvar_verbose) ucuputs (""); ucuputs ("[file transfer complete]"); if (! fsysdep_cu_copy (TRUE) || ! fsysdep_terminal_signals (FALSE)) ucuabort (); ucuputs (abCuconnected); return UUCONF_CMDTABRET_CONTINUE; } /* Get a file from the remote side. This is ~%take, or ~t, or ~<. The first two are assumed to be taking the file from a Unix system, so we force the command "cat FROM; echo */ /*ARGSUSED*/ static int icutake (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar; pointer pinfo; { struct sconnection *qconn = (struct sconnection *) pinfo; const char *zeof; char *zfrom, *zto, *zcmd; char *zalc; openfile_t e; char bcr; size_t ceoflen; char *zlook = NULL; size_t ceofhave; boolean ferr; if (argc > 1) zfrom = zbufcpy (argv[1]); else { zfrom = zsysdep_terminal_line ("Remote file to retreive: "); if (zfrom == NULL) ucuabort (); zfrom[strcspn (zfrom, " \t\n")] = '\0'; if (*zfrom == '\0') { ubuffree (zfrom); ucuputs (abCuconnected); return UUCONF_CMDTABRET_CONTINUE; } } if (argc > 2) zto = zbufcpy (argv[2]); else { char *zbase; char *zprompt; zbase = zsysdep_base_name (zfrom); if (zbase == NULL) ucuabort (); zprompt = zbufalc (sizeof "Local file name []: " + strlen (zbase)); sprintf (zprompt, "Local file name [%s]: ", zbase); zto = zsysdep_terminal_line (zprompt); ubuffree (zprompt); if (zto == NULL) ucuabort (); zto[strcspn (zto, " \t\n")] = '\0'; if (*zto != '\0') ubuffree (zbase); else { ubuffree (zto); zto = zbase; } } if (pvar != NULL) { zcmd = zsysdep_terminal_line ("Remote command to execute: "); if (zcmd == NULL) ucuabort (); zcmd[strcspn (zcmd, "\n")] = '\0'; zeof = zCuvar_eofread; } else { zcmd = zbufalc (sizeof "cat ; echo; echo ////cuend////" + strlen (zfrom)); sprintf (zcmd, "cat %s; echo; echo ////cuend////", zfrom); zeof = "\n////cuend////\n"; } ubuffree (zfrom); e = esysdep_user_fopen (zto, FALSE, fCuvar_binary); if (! ffileisopen (e)) { const char *zerrstr; ubuffree (zcmd); zerrstr = strerror (errno); zalc = zbufalc (strlen (zto) + sizeof ": " + strlen (zerrstr)); sprintf (zalc, "%s: %s\n", zto, zerrstr); ucuputs (zalc); ubuffree (zalc); ucuputs (abCuconnected); ubuffree (zto); return UUCONF_CMDTABRET_CONTINUE; } if (! fsysdep_cu_copy (FALSE) || ! fsysdep_terminal_signals (TRUE)) ucuabort (); if (! fconn_write (qconn, zcmd, strlen (zcmd))) ucuabort (); bcr = '\r'; if (! fconn_write (qconn, &bcr, 1)) ucuabort (); ubuffree (zcmd); /* Eliminated any previously echoed data to avoid confusion. */ iPrecstart = 0; iPrecend = 0; /* If we're dealing with a Unix system, we can reliably discard the command. Otherwise, the command will probably wind up in the file; too bad. */ if (pvar == NULL) { int b; while ((b = breceive_char (qconn, cCuvar_timeout, TRUE)) != '\n') { if (b == -2) ucuabort (); if (b < 0) { ucuputs ("[timed out waiting for newline]"); ucuputs (abCuconnected); ubuffree (zto); return UUCONF_CMDTABRET_CONTINUE; } } } ceoflen = strlen (zeof); zlook = zbufalc (ceoflen); ceofhave = 0; ferr = FALSE; while (TRUE) { int b; if (FGOT_SIGNAL ()) { /* Make sure the signal is logged. */ ulog (LOG_ERROR, (const char *) NULL); ucuputs ("[file receive aborted]"); /* Reset the SIGINT flag so that it does not confuse us in the future. */ afSignal[INDEXSIG_SIGINT] = FALSE; break; } b = breceive_char (qconn, cCuvar_timeout, TRUE); if (b == -2) ucuabort (); if (b < 0) { if (ceofhave > 0) (void) fwrite (zlook, sizeof (char), ceofhave, e); ucuputs ("[timed out]"); break; } if (b == '\r' && ! fCuvar_binary) continue; if (ceoflen == 0) { if (cfilewrite (e, &b, 1) != 1) { ferr = TRUE; break; } } else { zlook[ceofhave] = b; ++ceofhave; if (ceofhave == ceoflen) { size_t cmove; char *zmove; if (memcmp (zeof, zlook, ceoflen) == 0) { ucuputs ("[file transfer complete]"); break; } if (cfilewrite (e, zlook, 1) != 1) { ferr = TRUE; break; } zmove = zlook; for (cmove = ceoflen - 1, zmove = zlook; cmove > 0; cmove--, zmove++) zmove[0] = zmove[1]; --ceofhave; } } } ubuffree (zlook); if (! fsysdep_sync (e, zto)) { (void) ffileclose (e); ferr = TRUE; } else { if (! ffileclose (e)) ferr = TRUE; } if (ferr) ucuputs ("[file write error]"); if (! fsysdep_cu_copy (TRUE) || ! fsysdep_terminal_signals (FALSE)) ucuabort (); ucuputs (abCuconnected); ubuffree (zto); return UUCONF_CMDTABRET_CONTINUE; } /* Send a buffer to the remote system. If fCuvar_binary is FALSE, each buffer passed in will be a single line; in this case we can check the echoed characters and kill the line if they do not match. This returns FALSE if an echo check fails. If a port error occurrs, it calls ucuabort. */ static boolean fcusend_buf (qconn, zbufarg, cbufarg) struct sconnection *qconn; const char *zbufarg; size_t cbufarg; { const char *zbuf; size_t cbuf; int ctries; size_t cbplen; char *zsendbuf; zbuf = zbufarg; cbuf = cbufarg; ctries = 0; if (fCuvar_binary) cbplen = strlen (zCuvar_binary_prefix); else cbplen = 1; zsendbuf = zbufalc (64 * (cbplen + 1)); /* Loop while we still have characters to send. The value of cbuf will be reset to cbufarg if an echo failure occurs while sending a line in non-binary mode. */ while (cbuf > 0) { int csend; char *zput; const char *zget; boolean fnl; int i; if (FGOT_SIGNAL ()) { /* Make sure the signal is logged. */ ubuffree (zsendbuf); ulog (LOG_ERROR, (const char *) NULL); ucuputs ("[file send aborted]"); /* Reset the SIGINT flag so that it does not confuse us in the future. */ afSignal[INDEXSIG_SIGINT] = FALSE; return FALSE; } /* Discard anything we've read from the port up to now, to avoid confusing the echo checking. */ iPrecstart = 0; iPrecend = 0; /* Send all characters up to a newline before actually sending the newline. This makes it easier to handle the special newline echo checking. Send up to 64 characters at a time before doing echo checking. */ if (*zbuf == '\n') csend = 1; else { const char *znl; znl = memchr (zbuf, '\n', cbuf); if (znl == NULL) csend = cbuf; else csend = znl - zbuf; if (csend > 64) csend = 64; } /* Translate this part of the buffer. If we are not in binary mode, we translate \n to \r, and ignore any nonprintable characters. */ zput = zsendbuf; fnl = FALSE; for (i = 0, zget = zbuf; i < csend; i++, zget++) { if (isprint (*zget) || *zget == '\t') *zput++ = *zget; else if (*zget == '\n') { if (fCuvar_binary) *zput++ = '\n'; else *zput++ = '\r'; fnl = TRUE; } else if (fCuvar_binary) { strcpy (zput, zCuvar_binary_prefix); zput += cbplen; *zput++ = *zget; } } zbuf += csend; cbuf -= csend; if (zput == zsendbuf) continue; /* Send the data over the port. */ if (! fsend_data (qconn, zsendbuf, (size_t) (zput - zsendbuf), TRUE)) ucuabort (); /* We do echo checking if requested, unless we are in binary mode. Echo checking of a newline is different from checking of normal characters; when we send a newline we look for *zCuvar_echonl. */ if ((fCuvar_echocheck && ! fCuvar_binary) || (fnl && *zCuvar_echonl != '\0')) { long iend; iend = ixsysdep_time ((long *) NULL) + (long) cCuvar_timeout; for (zget = zsendbuf; zget < zput; zget++) { int bread; int bwant; if (fCuvar_binary ? *zget == '\n' : *zget == '\r') { bwant = *zCuvar_echonl; if (bwant == '\0') continue; } else { if (! fCuvar_echocheck || ! isprint (*zget)) continue; bwant = *zget; } do { if (FGOT_SIGNAL ()) { /* Make sure the signal is logged. */ ubuffree (zsendbuf); ulog (LOG_ERROR, (const char *) NULL); ucuputs ("[file send aborted]"); /* Reset the SIGINT flag so that it does not confuse us in the future. */ afSignal[INDEXSIG_SIGINT] = FALSE; return FALSE; } bread = breceive_char (qconn, iend - ixsysdep_time ((long *) NULL), TRUE); if (bread < 0) { if (bread == -2) ucuabort (); /* If we timed out, and we're not in binary mode, we kill the line and try sending it again from the beginning. */ if (! fCuvar_binary && *zCuvar_kill != '\0') { ++ctries; if (ctries < cCuvar_resend) { if (fCuvar_verbose) { printf ("R "); (void) fflush (stdout); } if (! fsend_data (qconn, zCuvar_kill, 1, TRUE)) ucuabort (); zbuf = zbufarg; cbuf = cbufarg; break; } } ubuffree (zsendbuf); ucuputs ("[timed out looking for echo]"); return FALSE; } } while (bread != *zget); if (bread < 0) break; } } } ubuffree (zsendbuf); return TRUE; } uucp-1.07/prot.c0000664000076400007640000001457407665321755007273 /* prot.c Protocol support routines to move commands and data around. Copyright (C) 1991, 1992, 1994 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char prot_rcsid[] = "$Id: prot.c,v 1.33 2002/03/05 19:10:41 ian Rel $"; #endif #include #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "conn.h" #include "prot.h" /* Variables visible to the protocol-specific routines. */ /* Buffer to hold received data. */ char abPrecbuf[CRECBUFLEN]; /* Index of start of data in abPrecbuf. */ int iPrecstart; /* Index of end of data (first byte not included in data) in abPrecbuf. */ int iPrecend; /* We want to output and input at the same time, if supported on this machine. If we have something to send, we send it all while accepting a large amount of data. Once we have sent everything we look at whatever we have received. If data comes in faster than we can send it, we may run out of buffer space. */ boolean fsend_data (qconn, zsend, csend, fdoread) struct sconnection *qconn; const char *zsend; size_t csend; boolean fdoread; { if (! fdoread) return fconn_write (qconn, zsend, csend); while (csend > 0) { size_t crec, csent; if (iPrecend < iPrecstart) crec = iPrecstart - iPrecend - 1; else { crec = CRECBUFLEN - iPrecend; if (iPrecstart == 0) --crec; } if (crec == 0) return fconn_write (qconn, zsend, csend); csent = csend; if (! fconn_io (qconn, zsend, &csent, abPrecbuf + iPrecend, &crec)) return FALSE; csend -= csent; zsend += csent; iPrecend = (iPrecend + crec) % CRECBUFLEN; } return TRUE; } /* Read data from the other system when we have nothing to send. The argument cneed is the amount of data the caller wants, and ctimeout is the timeout in seconds. The function sets *pcrec to the amount of data which was actually received, which may be less than cneed if there isn't enough room in the receive buffer. If no data is received before the timeout expires, *pcrec will be returned as 0. If an error occurs, the function returns FALSE. If the freport argument is FALSE, no error should be reported. */ boolean freceive_data (qconn, cneed, pcrec, ctimeout, freport) struct sconnection *qconn; size_t cneed; size_t *pcrec; int ctimeout; boolean freport; { /* Set *pcrec to the maximum amount of data we can read. fconn_read expects *pcrec to be the buffer size, and sets it to the amount actually received. */ if (iPrecend < iPrecstart) *pcrec = iPrecstart - iPrecend - 1; else { *pcrec = CRECBUFLEN - iPrecend; if (iPrecstart == 0) --(*pcrec); } #if DEBUG > 0 /* If we have no room in the buffer, we're in trouble. The protocols must be written to ensure that this can't happen. */ if (*pcrec == 0) ulog (LOG_FATAL, "freceive_data: No room in buffer"); #endif /* If we don't have room for all the data the caller wants, we simply have to expect less. We'll get the rest later. */ if (*pcrec < cneed) cneed = *pcrec; if (! fconn_read (qconn, abPrecbuf + iPrecend, pcrec, cneed, ctimeout, freport)) return FALSE; iPrecend = (iPrecend + *pcrec) % CRECBUFLEN; return TRUE; } /* Read a single character. Get it out of the receive buffer if it's there, otherwise ask freceive_data for at least one character. This is used because as a protocol is shutting down freceive_data may read ahead and eat characters that should be read outside the protocol routines. We call freceive_data rather than fconn_read with an argument of 1 so that we can get all the available data in a single system call. The ctimeout argument is the timeout in seconds; the freport argument is FALSE if no error should be reported. This returns a character, or -1 on timeout or -2 on error. */ int breceive_char (qconn, ctimeout, freport) struct sconnection *qconn; int ctimeout; boolean freport; { char b; if (iPrecstart == iPrecend) { size_t crec; if (! freceive_data (qconn, sizeof (char), &crec, ctimeout, freport)) return -2; if (crec == 0) return -1; } b = abPrecbuf[iPrecstart]; iPrecstart = (iPrecstart + 1) % CRECBUFLEN; return BUCHAR (b); } /* Send mail about a file transfer. We send to the given mailing address if there is one, otherwise to the user. */ boolean fmail_transfer (fsuccess, zuser, zmail, zwhy, zfromfile, zfromsys, ztofile, ztosys, zsaved) boolean fsuccess; const char *zuser; const char *zmail; const char *zwhy; const char *zfromfile; const char *zfromsys; const char *ztofile; const char *ztosys; const char *zsaved; { const char *zsendto; const char *az[20]; int i; if (zmail != NULL && *zmail != '\0') zsendto = zmail; else zsendto = zuser; i = 0; az[i++] = "The file\n\t"; if (zfromsys != NULL) { az[i++] = zfromsys; az[i++] = "!"; } az[i++] = zfromfile; if (fsuccess) az[i++] = "\nwas successfully transferred to\n\t"; else az[i++] = "\ncould not be transferred to\n\t"; if (ztosys != NULL) { az[i++] = ztosys; az[i++] = "!"; } az[i++] = ztofile; az[i++] = "\nas requested by\n\t"; az[i++] = zuser; if (! fsuccess) { az[i++] = "\nfor the following reason:\n\t"; az[i++] = zwhy; az[i++] = "\n"; } if (zsaved != NULL) { az[i++] = zsaved; az[i++] = "\n"; } return fsysdep_mail (zsendto, fsuccess ? "UUCP succeeded" : "UUCP failed", i, az); } uucp-1.07/log.c0000664000076400007640000004515407665321755007066 /* log.c Routines to add entries to the log files. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char log_rcsid[] = "$Id: log.c,v 1.65 2002/03/05 19:10:41 ian Rel $"; #endif #include #include #if HAVE_STDARG_H #include #endif #if TM_IN_SYS_TIME #include #else #include #endif #include "uudefs.h" #include "uuconf.h" #include "system.h" /* Local functions. */ __inline__ static char *zstpcpy P((char *zto, const char *zfrom)); static const char *zldate_and_time P((void)); /* Program name. Set by main function. */ const char *zProgram; /* Log file name. */ static const char *zLogfile; /* The function to call when a LOG_FATAL error occurs. */ static void (*pfLfatal) P((void)); /* Whether to go to a file. */ static boolean fLfile; /* ID number. */ static int iLid; /* The current user name. */ static char *zLuser; /* The current system name. */ static char *zLsystem; /* The current device name. */ char *zLdevice; /* The open log file. */ static FILE *eLlog; /* Whether we have tried to open the log file. We need this because we don't want to keep trying to open the log file if we failed the first time. It can't be static because under HAVE_HDB_LOGGING we may have to write to various different log files. */ static boolean fLlog_tried; #if DEBUG > 1 /* Debugging file name. */ static const char *zLdebugfile; /* The open debugging file. */ static FILE *eLdebug; /* Whether we've tried to open the debugging file. */ static boolean fLdebug_tried; #endif /* Statistics file name. */ static const char *zLstatsfile; /* The open statistics file. */ static FILE *eLstats; /* Whether we've tried to open the statistics file. */ static boolean fLstats_tried; /* The array of signals. The elements are only set to TRUE by the default signal handler. They are only set to FALSE if we don't care whether we got the signal or not. */ volatile sig_atomic_t afSignal[INDEXSIG_COUNT]; /* The array of signals to log. The elements are only set to TRUE by the default signal handler. They are set to FALSE when the signal is logged in ulog. This means that if a signal comes in at just the right time we won't log it (or, rather, we'll log it once instead of twice), but that is not a catatrophe. */ volatile sig_atomic_t afLog_signal[INDEXSIG_COUNT]; /* Flag that indicates SIGHUP is worth logging. */ boolean fLog_sighup = TRUE; /* Signal names to use when logging signals. */ static const char * const azSignal_names[INDEXSIG_COUNT] = INDEXSIG_NAMES; /* If not NULL, ulog calls this function before outputting anything. This is used to support cu. */ void (*pfLstart) P((void)); /* If not NULL, ulog calls this function after outputting everything. This is used to support cu. */ void (*pfLend) P((void)); /* Set the function to call on a LOG_FATAL error. */ void ulog_fatal_fn (pfn) void (*pfn) P((void)); { pfLfatal = pfn; } /* Decide whether to send log message to the file or not. */ void ulog_to_file (puuconf, ffile) pointer puuconf; boolean ffile; { int iuuconf; iuuconf = uuconf_logfile (puuconf, &zLogfile); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); #if DEBUG > 1 iuuconf = uuconf_debugfile (puuconf, &zLdebugfile); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); #endif iuuconf = uuconf_statsfile (puuconf, &zLstatsfile); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); fLfile = ffile; } /* Set the ID number. This will be called by the usysdep_initialize if there is something sensible to set it to. */ void ulog_id (i) int i; { iLid = i; } /* Set the user we are making log entries for. The arguments will be copied into memory. */ void ulog_user (zuser) const char *zuser; { ubuffree (zLuser); zLuser = zbufcpy (zuser); } /* Set the system name we are making log entries for. The name is copied into memory. */ void ulog_system (zsystem) const char *zsystem; { if (zsystem == NULL || zLsystem == NULL || strcmp (zsystem, zLsystem) != 0) { ubuffree (zLsystem); zLsystem = zbufcpy (zsystem); #if HAVE_HDB_LOGGING /* Under HDB logging we now must write to a different log file. */ ulog_close (); #endif /* HAVE_HDB_LOGGING */ } } /* Set the device name. This is copied into memory. */ void ulog_device (zdevice) const char *zdevice; { ubuffree (zLdevice); zLdevice = zbufcpy (zdevice); } /* A helper function for ulog. */ __inline__ static char * zstpcpy (zto, zfrom) char *zto; const char *zfrom; { while ((*zto++ = *zfrom++) != '\0') ; return zto - 1; } /* Make a log entry. We make a token concession to non ANSI_C systems, but it clearly won't always work. */ #if ! HAVE_PROTOTYPES || ! HAVE_STDARG_H #undef HAVE_VFPRINTF #define HAVE_VFPRINTF 0 #endif /*VARARGS2*/ #if HAVE_VFPRINTF void ulog (enum tlog ttype, const char *zmsg, ...) #else void ulog (ttype, zmsg, a, b, c, d, f, g, h, i, j) enum tlog ttype; const char *zmsg; #endif { #if HAVE_VFPRINTF va_list parg; #endif FILE *e, *edebug; boolean fstart, fend; const char *zhdr; char *zprefix; register char *zset; char *zformat; char *zfrom; /* Log any received signal. We do it this way to avoid calling ulog from the signal handler. A few routines call ulog to get this message out with zmsg == NULL. */ { static boolean fdoing_sigs; if (! fdoing_sigs) { int isig; fdoing_sigs = TRUE; for (isig = 0; isig < INDEXSIG_COUNT; isig++) { if (afLog_signal[isig]) { afLog_signal[isig] = FALSE; /* Apparently SunOS sends SIGINT rather than SIGHUP when hanging up, so we don't log either signal if fLog_sighup is FALSE. */ if ((isig != INDEXSIG_SIGHUP && isig != INDEXSIG_SIGINT) || fLog_sighup) ulog (LOG_ERROR, "Got %s signal", azSignal_names[isig]); } } fdoing_sigs = FALSE; } } #if DEBUG > 1 /* If we've had a debugging file open in the past, then we want to write all log file entries to the debugging file even if it's currently closed. */ if (fLfile && eLdebug == NULL && ! fLdebug_tried && iDebug != 0) { fLdebug_tried = TRUE; eLdebug = esysdep_fopen (zLdebugfile, FALSE, TRUE, TRUE); } #endif /* DEBUG > 1 */ if (! fLfile) e = stderr; #if DEBUG > 1 else if ((int) ttype >= (int) LOG_DEBUG) { e = eLdebug; /* If we can't open the debugging file, don't output any debugging messages. */ if (e == NULL) return; } #endif /* DEBUG > 1 */ else { if (eLlog == NULL && ! fLlog_tried) { const char *zprint = NULL; fLlog_tried = TRUE; #if ! HAVE_HDB_LOGGING eLlog = esysdep_fopen (zLogfile, TRUE, TRUE, TRUE); zprint = zLogfile; #else /* HAVE_HDB_LOGGING */ { const char *zcheck; int cfmt; char *zfile; /* Only run sprintf if there are no more than two unadorned %s. If we see any other formatting character, just use zLogfile as is. This is to protect the UUCP administrator against foolishness. Note that this has been reported as a security vulnerability, but it is not. */ cfmt = 0; for (zcheck = zLogfile; *zcheck != '\0'; ++zcheck) { if (*zcheck == '%') { if (zcheck[1] == 's') ++cfmt; else { cfmt = 3; break; } } } if (cfmt > 2) zfile = zbufcpy (zLogfile); else { const char *zsys; char *zbase; char *zlower; /* We want to write to .Log/program/system, e.g. .Log/uucico/uunet. The system name may not be set. */ if (zLsystem == NULL) zsys = "ANY"; else zsys = zLsystem; zbase = zsysdep_base_name (zProgram); if (zbase == NULL) zbase = zbufcpy (zProgram); /* On some systems the native uusched will invoke uucico with an upper case argv[0]. We work around that by forcing the filename to lower case here. */ for (zlower = zbase; *zlower != '\0'; zlower++) if (isupper (*zlower)) *zlower = tolower (*zlower); zfile = zbufalc (strlen (zLogfile) + strlen (zbase) + strlen (zsys) + 1); sprintf (zfile, zLogfile, zbase, zsys); ubuffree (zbase); } eLlog = esysdep_fopen (zfile, TRUE, TRUE, TRUE); if (eLlog != NULL) ubuffree (zfile); else zprint = zfile; } #endif /* HAVE_HDB_LOGGING */ if (eLlog == NULL) { /* We can't open the log file. We report the problem to stderr. This is not ideal, since if this is uucico running on an inbound call stderr is actually connected to a remote system, but is better than doing nothing. */ fprintf (stderr, "%s: %s: can not open log file: %s\n", zProgram, zprint, strerror (errno)); if (pfLfatal != NULL) (*pfLfatal) (); usysdep_exit (FALSE); } } e = eLlog; /* eLlog might be NULL here because we might try to open the log file recursively via esysdep_fopen. */ if (e == NULL) return; } if (zmsg == NULL) return; if (pfLstart != NULL) (*pfLstart) (); edebug = NULL; #if DEBUG > 1 if ((int) ttype < (int) LOG_DEBUG) edebug = eLdebug; #endif fstart = TRUE; fend = TRUE; switch (ttype) { case LOG_NORMAL: zhdr = ""; break; case LOG_ERROR: zhdr = "ERROR: "; break; case LOG_FATAL: zhdr = "FATAL: "; break; #if DEBUG > 1 case LOG_DEBUG: zhdr = "DEBUG: "; break; case LOG_DEBUG_START: zhdr = "DEBUG: "; fend = FALSE; break; case LOG_DEBUG_CONTINUE: zhdr = NULL; fstart = FALSE; fend = FALSE; break; case LOG_DEBUG_END: zhdr = NULL; fstart = FALSE; break; #endif default: zhdr = "???: "; break; } if (! fstart) zprefix = zbufcpy (""); else { if (! fLfile) { zprefix = zbufalc (strlen (zProgram) + 3); sprintf (zprefix, "%s: ", zProgram); } else { zprefix = zbufalc (strlen (zProgram) + (zLsystem == NULL ? 1 : strlen (zLsystem)) + (zLuser == NULL ? 4 : strlen (zLuser)) + sizeof "1991-12-31 12:00:00.00" + strlen (zhdr) + 100); zset = zprefix; #if HAVE_TAYLOR_LOGGING { char *zbase; zbase = zsysdep_base_name (zProgram); if (zbase == NULL) zbase = zbufcpy (zProgram); zset = zstpcpy (zset, zbase); *zset++ = ' '; ubuffree (zbase); } #else /* ! HAVE_TAYLOR_LOGGING */ zset = zstpcpy (zset, zLuser == NULL ? "uucp" : zLuser); *zset++ = ' '; #endif /* HAVE_TAYLOR_LOGGING */ zset = zstpcpy (zset, zLsystem == NULL ? "-" : zLsystem); *zset++ = ' '; #if HAVE_TAYLOR_LOGGING zset = zstpcpy (zset, zLuser == NULL ? "-" : zLuser); *zset++ = ' '; #endif /* HAVE_TAYLOR_LOGGING */ *zset++ = '('; zset = zstpcpy (zset, zldate_and_time ()); if (iLid != 0) { #if ! HAVE_HDB_LOGGING #if HAVE_TAYLOR_LOGGING sprintf (zset, " %d", iLid); #else /* ! HAVE_TAYLOR_LOGGING */ sprintf (zset, "-%d", iLid); #endif /* ! HAVE_TAYLOR_LOGGING */ #else /* HAVE_HDB_LOGGING */ /* I assume that the second number here is meant to be some sort of file sequence number, and that it should correspond to the sequence number in the statistics file. I don't have any really convenient way to do this, so I won't unless somebody thinks it's very important. */ sprintf (zset, ",%d,%d", iLid, 0); #endif /* HAVE_HDB_LOGGING */ zset += strlen (zset); } #if QNX_LOG_NODE_ID sprintf (zset, " %ld", (long) getnid ()); zset += strlen (zset); #endif *zset++ = ')'; *zset++ = ' '; strcpy (zset, zhdr); } } zformat = zbufalc (2 * strlen (zprefix) + strlen (zmsg) + 2); zset = zformat; zfrom = zprefix; while (*zfrom != '\0') { if (*zfrom == '%') *zset++ = '%'; *zset++ = *zfrom++; } ubuffree (zprefix); zset = zstpcpy (zset, zmsg); if (fend) { *zset++ = '\n'; *zset = '\0'; } #if HAVE_VFPRINTF va_start (parg, zmsg); vfprintf (e, zformat, parg); va_end (parg); if (edebug != NULL) { va_start (parg, zmsg); vfprintf (edebug, zformat, parg); va_end (parg); } #else /* ! HAVE_VFPRINTF */ fprintf (e, zformat, a, b, c, d, f, g, h, i, j); if (edebug != NULL) fprintf (edebug, zformat, a, b, c, d, f, g, h, i, j); #endif /* ! HAVE_VFPRINTF */ ubuffree (zformat); (void) fflush (e); if (edebug != NULL) (void) fflush (edebug); if (pfLend != NULL) (*pfLend) (); if (ttype == LOG_FATAL) { if (pfLfatal != NULL) (*pfLfatal) (); usysdep_exit (FALSE); } #if CLOSE_LOGFILES ulog_close (); #endif } /* Log a uuconf error. */ void ulog_uuconf (ttype, puuconf, iuuconf) enum tlog ttype; pointer puuconf; int iuuconf; { char ab[512]; (void) uuconf_error_string (puuconf, iuuconf, ab, sizeof ab); ulog (ttype, "%s", ab); } /* Close the log file. There's nothing useful we can do with errors, so we don't check for them. */ void ulog_close () { /* Make sure we logged any signal we received. */ ulog (LOG_ERROR, (const char *) NULL); if (eLlog != NULL) { (void) fclose (eLlog); eLlog = NULL; fLlog_tried = FALSE; } #if DEBUG > 1 if (eLdebug != NULL) { (void) fclose (eLdebug); eLdebug = NULL; fLdebug_tried = FALSE; } #endif } /* Add an entry to the statistics file. We may eventually want to put failed file transfers in here, but we currently do not. */ /*ARGSUSED*/ void ustats (fsucceeded, zuser, zsystem, fsent, cbytes, csecs, cmicros, fcaller) boolean fsucceeded; const char *zuser; const char *zsystem; boolean fsent; long cbytes; long csecs; long cmicros; boolean fcaller ATTRIBUTE_UNUSED; { long cbps; /* The seconds and microseconds are now counted independently, so they may be out of synch. */ if (cmicros < 0) { csecs -= ((- cmicros) / 1000000L) + 1; cmicros = 1000000L - ((- cmicros) % 1000000L); } if (cmicros >= 1000000L) { csecs += cmicros / 10000000L; cmicros = cmicros % 1000000L; } /* On a system which can determine microseconds we might very well have both csecs == 0 and cmicros == 0. */ if (csecs == 0 && cmicros < 1000) cbps = 0; else { long cmillis, cdiv, crem; /* Compute ((csecs * 1000) / cmillis) using integer division. Where DIV is integer division, we know a = (a DIV b) * b + a % b so a / b = (a DIV b) + (a % b) / b We compute the latter with a as csecs and b as cmillis, mixing the multiplication by 1000. */ cmillis = csecs * 1000 + cmicros / 1000; cdiv = (cbytes / cmillis) * 1000; crem = (cbytes % cmillis) * 1000; cbps = cdiv + (crem / cmillis); if (cmillis < 0 || cdiv < 0 || crem < 0 || cbps < 0) { /* We overflowed using milliseconds, so use seconds. */ cbps = cbytes / (csecs + ((cmicros > 500000L) ? 1 : 0)); } } if (eLstats == NULL) { if (fLstats_tried) return; fLstats_tried = TRUE; eLstats = esysdep_fopen (zLstatsfile, TRUE, TRUE, TRUE); if (eLstats == NULL) return; } #if HAVE_TAYLOR_LOGGING fprintf (eLstats, "%s %s (%s) %s%s %ld bytes in %ld.%03ld seconds (%ld bytes/sec) on port %s\n", zuser, zsystem, zldate_and_time (), fsucceeded ? "" : "failed after ", fsent ? "sent" : "received", cbytes, csecs, cmicros / 1000, cbps, zLdevice == NULL ? "unknown" : zLdevice); #endif /* HAVE_TAYLOR_LOGGING */ #if HAVE_V2_LOGGING fprintf (eLstats, "%s %s (%s) (%ld) %s %s %ld bytes %ld seconds\n", zuser, zsystem, zldate_and_time (), (long) time ((time_t *) NULL), fsent ? "sent" : "received", fsucceeded ? "data" : "failed after", cbytes, csecs + cmicros / 500000); #endif /* HAVE_V2_LOGGING */ #if HAVE_HDB_LOGGING { static int iseq; /* I don't know what the 'C' means. The sequence number should probably correspond to the sequence number in the log file, but that is currently always 0; using this fake sequence number will still at least reveal which transfers are from different calls. */ ++iseq; fprintf (eLstats, "%s!%s %c (%s) (C,%d,%d) [%s] %s %ld / %ld.%03ld secs, %ld%s%s\n", zsystem, zuser, fcaller ? 'M' : 'S', zldate_and_time (), iLid, iseq, zLdevice == NULL ? "unknown" : zLdevice, fsent ? "->" : "<-", cbytes, csecs, cmicros / 1000, cbps, " bytes/sec", fsucceeded ? "" : " [PARTIAL FILE]"); } #endif /* HAVE_HDB_LOGGING */ (void) fflush (eLstats); #if CLOSE_LOGFILES ustats_close (); #endif } /* Close the statistics file. */ void ustats_close () { if (eLstats != NULL) { if (fclose (eLstats) != 0) ulog (LOG_ERROR, "fclose: %s", strerror (errno)); eLstats = NULL; fLstats_tried = FALSE; } } /* Return the date and time in a form used for a log entry. */ static const char * zldate_and_time () { long isecs, imicros; struct tm s; #if HAVE_TAYLOR_LOGGING static char ab[sizeof "1991-12-31 12:00:00.00"]; #endif #if HAVE_V2_LOGGING static char ab[sizeof "12/31-12:00"]; #endif #if HAVE_HDB_LOGGING static char ab[sizeof "12/31-12:00:00"]; #endif isecs = ixsysdep_time (&imicros); usysdep_localtime (isecs, &s); #if HAVE_TAYLOR_LOGGING sprintf (ab, "%04d-%02d-%02d %02d:%02d:%02d.%02d", s.tm_year + 1900, s.tm_mon + 1, s.tm_mday, s.tm_hour, s.tm_min, s.tm_sec, (int) (imicros / 10000)); #endif #if HAVE_V2_LOGGING sprintf (ab, "%d/%d-%02d:%02d", s.tm_mon + 1, s.tm_mday, s.tm_hour, s.tm_min); #endif #if HAVE_HDB_LOGGING sprintf (ab, "%d/%d-%d:%02d:%02d", s.tm_mon + 1, s.tm_mday, s.tm_hour, s.tm_min, s.tm_sec); #endif return ab; } uucp-1.07/chat.c0000664000076400007640000007750507665321754007230 /* chat.c Chat routine for the UUCP package. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char chat_rcsid[] = "$Id: chat.c,v 1.50 2002/03/05 19:10:41 ian Rel $"; #endif #include #include #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "prot.h" #include "system.h" /* Local functions. */ static int icexpect P((struct sconnection *qconn, int cstrings, char **azstrings, size_t *aclens, int ctimeout, boolean fstrip)); static boolean fcsend P((struct sconnection *qconn, pointer puuconf, const char *zsend, const struct uuconf_system *qsys, const struct uuconf_dialer *qdial, const char *zphone, boolean ftranslate, boolean fstrip)); static boolean fcecho_send_strip P((struct sconnection *qconn, const char *z, size_t clen)); static boolean fcecho_send_nostrip P((struct sconnection *qconn, const char *z, size_t clen)); static boolean fcecho_send P((struct sconnection *qconn, const char *z, size_t clen, boolean fstrip)); static boolean fcphone P((struct sconnection *qconn, pointer puuconf, const struct uuconf_dialer *qdial, const char *zphone, boolean (*pfwrite) P((struct sconnection *qc, const char *zwrite, size_t cwrite)), boolean ftranslate, boolean *pfquote)); static boolean fctranslate P((pointer puuconf, const char *zphone, const char **pzprefix, const char **pzsuffix)); static boolean fcprogram P((struct sconnection *qconn, pointer puuconf, char **pzprogram, const struct uuconf_system *qsys, const struct uuconf_dialer *qdial, const char *zphone, const char *zport, long ibaud)); /* Run a chat script with the other system. The chat script is a series of expect send pairs. We wait for the expect string to show up, and then we send the send string. The chat string for a system holds the expect and send strings separated by a single space. */ boolean fchat (qconn, puuconf, qchat, qsys, qdial, zphone, ftranslate, zport, ibaud) struct sconnection *qconn; pointer puuconf; const struct uuconf_chat *qchat; const struct uuconf_system *qsys; const struct uuconf_dialer *qdial; const char *zphone; boolean ftranslate; const char *zport; long ibaud; { int cstrings; char **azstrings; size_t *aclens; char **pzchat; char *zbuf; size_t cbuflen; boolean fret; int i; /* First run the program, if any. */ if (qchat->uuconf_pzprogram != NULL) { if (! fcprogram (qconn, puuconf, qchat->uuconf_pzprogram, qsys, qdial, zphone, zport, ibaud)) return FALSE; } /* If there's no chat script, we're done. */ if (qchat->uuconf_pzchat == NULL) return TRUE; if (qchat->uuconf_pzfail == NULL) { cstrings = 1; azstrings = (char **) xmalloc (sizeof (char *)); aclens = (size_t *) xmalloc (sizeof (size_t)); } else { char **pz; /* We leave string number 0 for the chat script. */ cstrings = 1; for (pz = qchat->uuconf_pzfail; *pz != NULL; pz++) ++cstrings; azstrings = (char **) xmalloc (cstrings * sizeof (char *)); aclens = (size_t *) xmalloc (cstrings * sizeof (size_t)); /* Get the strings into the array, and handle all the escape characters. */ for (cstrings = 1, pz = qchat->uuconf_pzfail; *pz != NULL; cstrings++, pz++) { azstrings[cstrings] = zbufcpy (*pz); aclens[cstrings] = cescape (azstrings[cstrings]); } } cbuflen = 0; zbuf = NULL; fret = TRUE; pzchat = qchat->uuconf_pzchat; while (*pzchat != NULL) { size_t clen; /* Loop over subexpects and subsends. */ while (TRUE) { char *ztimeout; int ctimeout; /* Copy the expect string into the buffer so that we can modify it in cescape. */ clen = strlen (*pzchat); if (clen >= cbuflen) { ubuffree (zbuf); zbuf = zbufalc (clen + 1); cbuflen = clen; } memcpy (zbuf, *pzchat, clen + 1); azstrings[0] = zbuf; if (azstrings[0][0] == '-') ++azstrings[0]; /* \Wnum at the end of the string is a timeout. */ ctimeout = qchat->uuconf_ctimeout; ztimeout = strrchr (azstrings[0], '\\'); if (ztimeout != NULL && ztimeout[1] == 'W') { char *zend; int cval; cval = (int) strtol (ztimeout + 2, &zend, 10); if (zend != ztimeout + 2 && *zend == '\0') { ctimeout = cval; *ztimeout = '\0'; } } aclens[0] = cescape (azstrings[0]); if (aclens[0] == 0 || (aclens[0] == 2 && strcmp (azstrings[0], "\"\"") == 0)) { /* There is no subexpect sequence. If there is a subsend sequence we move on to it. Otherwise we let this expect succeed. This is somewhat inconsistent, but it seems to be the traditional approach. */ if (pzchat[1] == NULL || pzchat[1][0] != '-') break; } else { int istr; istr = icexpect (qconn, cstrings, azstrings, aclens, ctimeout, qchat->uuconf_fstrip); /* If we found the string, break out of the subexpect/subsend loop. */ if (istr == 0) break; /* If we got an error, return FALSE. */ if (istr < -1) { fret = FALSE; break; } /* If we found a failure string, log it and get out. */ if (istr > 0) { ulog (LOG_ERROR, "Chat script failed: Got \"%s\"", qchat->uuconf_pzfail[istr - 1]); fret = FALSE; break; } /* We timed out; look for a send subsequence. If none, the chat script has failed. */ if (pzchat[1] == NULL || pzchat[1][0] != '-') { ulog (LOG_ERROR, "Timed out in chat script"); fret = FALSE; break; } } /* Send the send subsequence without the leading '-'. A \"\" will send nothing. An empty string will send a carriage return. */ ++pzchat; if (! fcsend (qconn, puuconf, *pzchat + 1, qsys, qdial, zphone, ftranslate, qchat->uuconf_fstrip)) { fret = FALSE; break; } /* If there is no expect subsequence, we are done. */ if (pzchat[1] == NULL || pzchat[1][0] != '-') break; /* Move on to next expect subsequence. */ ++pzchat; } if (! fret) break; /* Move on to the send string. If there is none, we have succeeded. */ do { ++pzchat; } while (*pzchat != NULL && (*pzchat)[0] == '-'); if (*pzchat == NULL) break; if (**pzchat != '\0') { if (! fcsend (qconn, puuconf, *pzchat, qsys, qdial, zphone, ftranslate, qchat->uuconf_fstrip)) { fret = FALSE; break; } } ++pzchat; } ubuffree (zbuf); for (i = 1; i < cstrings; i++) ubuffree (azstrings[i]); xfree ((pointer) azstrings); xfree ((pointer) aclens); return fret; } /* Read characters and wait for one of a set of memory strings to come in. This returns the index into the array of the string that arrives, or -1 on timeout, or -2 on error. */ static int icexpect (qconn, cstrings, azstrings, aclens, ctimeout, fstrip) struct sconnection *qconn; int cstrings; char **azstrings; size_t *aclens; int ctimeout; boolean fstrip; { int i; size_t cmax; char *zhave; size_t chave; long iendtime; #if DEBUG > 1 int cchars; int iolddebug; #endif cmax = aclens[0]; for (i = 1; i < cstrings; i++) if (cmax < aclens[i]) cmax = aclens[i]; zhave = zbufalc (cmax); chave = 0; iendtime = ixsysdep_time ((long *) NULL) + ctimeout; #if DEBUG > 1 cchars = 0; iolddebug = iDebug; if (FDEBUGGING (DEBUG_CHAT)) { udebug_buffer ("icexpect: Looking for", azstrings[0], aclens[0]); ulog (LOG_DEBUG_START, "icexpect: Got \""); iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT); } #endif while (TRUE) { int bchar; /* If we have no more time, get out. */ if (ctimeout <= 0) { #if DEBUG > 1 if (FDEBUGGING (DEBUG_CHAT)) { ulog (LOG_DEBUG_END, "\" (timed out)"); iDebug = iolddebug; } #endif ubuffree (zhave); return -1; } /* Read one character at a time. We could use a more complex algorithm to read in larger batches, but it's probably not worth it. If the buffer is full, shift it left; we already know that no string matches, and the buffer holds the largest string, so this can't lose a match. */ if (chave >= cmax) { size_t imove; for (imove = 0; imove < cmax - 1; imove++) zhave[imove] = zhave[imove + 1]; --chave; } /* The timeout/error return values from breceive_char are the same as for this function. */ bchar = breceive_char (qconn, ctimeout, TRUE); if (bchar < 0) { #if DEBUG > 1 if (FDEBUGGING (DEBUG_CHAT)) { /* If there was an error, it will probably be logged in the middle of our string, but this is only debugging so it's not a big deal. */ ulog (LOG_DEBUG_END, "\" (%s)", bchar == -1 ? "timed out" : "error"); iDebug = iolddebug; } #endif ubuffree (zhave); return bchar; } /* Strip the parity bit if desired. */ if (fstrip) bchar &= 0x7f; zhave[chave] = (char) bchar; ++chave; #if DEBUG > 1 if (FDEBUGGING (DEBUG_CHAT)) { char ab[5]; ++cchars; if (cchars > 60) { ulog (LOG_DEBUG_END, "\""); ulog (LOG_DEBUG_START, "icexpect: Got \""); cchars = 0; } (void) cdebug_char (ab, bchar); ulog (LOG_DEBUG_CONTINUE, "%s", ab); } #endif /* See if any of the strings can be found in the buffer. Since we read one character at a time, the string can only be found at the end of the buffer. */ for (i = 0; i < cstrings; i++) { if (aclens[i] <= chave && memcmp (zhave + chave - aclens[i], azstrings[i], aclens[i]) == 0) { #if DEBUG > 1 if (FDEBUGGING (DEBUG_CHAT)) { if (i == 0) ulog (LOG_DEBUG_END, "\" (found it)"); else { ulog (LOG_DEBUG_END, "\""); udebug_buffer ("icexpect: Found", azstrings[i], aclens[i]); } iDebug = iolddebug; } #endif ubuffree (zhave); return i; } } ctimeout = (int) (iendtime - ixsysdep_time ((long *) NULL)); } } #if DEBUG > 1 /* Debugging function for fcsend. This takes the fquote variable, the length of the string (0 if this an informational string which can be printed directly) and the string itself. It returns the new value for fquote. The fquote variable is TRUE if the debugging output is in the middle of a quoted string. */ static size_t cCsend_chars; static int iColddebug; static boolean fcsend_debug P((boolean, size_t, const char *)); static boolean fcsend_debug (fquote, clen, zbuf) boolean fquote; size_t clen; const char *zbuf; { size_t cwas; if (! FDEBUGGING (DEBUG_CHAT)) return TRUE; cwas = cCsend_chars; if (clen > 0) cCsend_chars += clen; else cCsend_chars += strlen (zbuf); if (cCsend_chars > 60 && cwas > 10) { ulog (LOG_DEBUG_END, "%s", fquote ? "\"" : ""); fquote = FALSE; ulog (LOG_DEBUG_START, "fcsend: Writing"); cCsend_chars = 0; } if (clen == 0) { ulog (LOG_DEBUG_CONTINUE, "%s %s", fquote ? "\"" : "", zbuf); return FALSE; } else { size_t i; if (! fquote) ulog (LOG_DEBUG_CONTINUE, " \""); for (i = 0; i < clen; i++) { char ab[5]; (void) cdebug_char (ab, zbuf[i]); ulog (LOG_DEBUG_CONTINUE, "%s", ab); } return TRUE; } } /* Finish up the debugging information for fcsend. */ static void ucsend_debug_end P((boolean, boolean)); static void ucsend_debug_end (fquote, ferr) boolean fquote; boolean ferr; { if (! FDEBUGGING (DEBUG_CHAT)) return; if (fquote) ulog (LOG_DEBUG_CONTINUE, "\""); if (ferr) ulog (LOG_DEBUG_CONTINUE, " (error)"); ulog (LOG_DEBUG_END, "%s", ""); iDebug = iColddebug; } #else /* DEBUG <= 1 */ /* Use macro definitions to make fcsend look neater. */ #define fcsend_debug(fquote, clen, zbuf) TRUE #define ucsend_debug_end(fquote, ferror) #endif /* DEBUG <= 1 */ /* Send a string out. This has to parse escape sequences as it goes. Note that it handles the dialer escape sequences (\e, \E, \D, \T) although they make no sense for chatting with a system. */ static boolean fcsend (qconn, puuconf, z, qsys, qdial, zphone, ftranslate, fstrip) struct sconnection *qconn; pointer puuconf; const char *z; const struct uuconf_system *qsys; const struct uuconf_dialer *qdial; const char *zphone; boolean ftranslate; boolean fstrip; { boolean fnocr; boolean (*pfwrite) P((struct sconnection *, const char *, size_t)); char *zcallout_login; char *zcallout_pass; boolean fquote; if (strcmp (z, "\"\"") == 0) return TRUE; fnocr = FALSE; pfwrite = fconn_write; zcallout_login = NULL; zcallout_pass = NULL; #if DEBUG > 1 if (FDEBUGGING (DEBUG_CHAT)) { ulog (LOG_DEBUG_START, "fcsend: Writing"); fquote = FALSE; cCsend_chars = 0; iColddebug = iDebug; iDebug &=~ (DEBUG_OUTGOING | DEBUG_PORT); } #endif while (*z != '\0') { const char *zlook; boolean fsend; char bsend; zlook = z + strcspn ((char *) z, "\\BE"); if (zlook > z) { size_t c; c = zlook - z; fquote = fcsend_debug (fquote, c, z); if (! (*pfwrite) (qconn, z, c)) { ucsend_debug_end (fquote, TRUE); return FALSE; } } if (*zlook == '\0') break; z = zlook; fsend = FALSE; switch (*z) { case 'B': if (strncmp (z, "BREAK", 5) == 0) { fquote = fcsend_debug (fquote, (size_t) 0, "break"); if (! fconn_break (qconn)) { ucsend_debug_end (fquote, TRUE); return FALSE; } fnocr = TRUE; z += 5; } else { fsend = TRUE; bsend = 'B'; ++z; } break; case 'E': if (strncmp (z, "EOT", 3) == 0) { fsend = TRUE; bsend = '\004'; fnocr = TRUE; z += 3; } else { fsend = TRUE; bsend = 'E'; ++z; } break; case '\\': ++z; switch (*z) { case '-': fsend = TRUE; bsend = '-'; break; case 'b': fsend = TRUE; bsend = '\b'; break; case 'c': fnocr = TRUE; break; case 'd': fquote = fcsend_debug (fquote, (size_t) 0, "sleep"); usysdep_sleep (1); break; case 'e': fquote = fcsend_debug (fquote, (size_t) 0, "echo-check-off"); pfwrite = fconn_write; break; case 'E': fquote = fcsend_debug (fquote, (size_t) 0, "echo-check-on"); if (fstrip) pfwrite = fcecho_send_strip; else pfwrite = fcecho_send_nostrip; break; case 'K': fquote = fcsend_debug (fquote, (size_t) 0, "break"); if (! fconn_break (qconn)) { ucsend_debug_end (fquote, TRUE); return FALSE; } break; case 'n': fsend = TRUE; bsend = '\n'; break; case 'N': fsend = TRUE; bsend = '\0'; break; case 'p': fquote = fcsend_debug (fquote, (size_t) 0, "pause"); usysdep_pause (); break; case 'r': fsend = TRUE; bsend = '\r'; break; case 's': fsend = TRUE; bsend = ' '; break; case 't': fsend = TRUE; bsend = '\t'; break; case '\0': --z; /* Fall through. */ case '\\': fsend = TRUE; bsend = '\\'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': fsend = TRUE; bsend = *z - '0'; if (z[1] >= '0' && z[1] <= '7') bsend = (char) (8 * bsend + *++z - '0'); if (z[1] >= '0' && z[1] <= '7') bsend = (char) (8 * bsend + *++z - '0'); break; case 'x': fsend = TRUE; bsend = 0; while (isxdigit (BUCHAR (z[1]))) { if (isdigit (BUCHAR (z[1]))) bsend = (char) (16 * bsend + *++z - '0'); else if (isupper (BUCHAR (z[1]))) bsend = (char) (16 * bsend + *++z - 'A' + 10); else bsend = (char) (16 * bsend + *++z - 'a' + 10); } break; case 'L': { const char *zlog; char *zcopy; size_t clen; if (qsys == NULL) { ucsend_debug_end (fquote, TRUE); ulog (LOG_ERROR, "Illegal use of \\L"); return FALSE; } zlog = qsys->uuconf_zcall_login; if (zlog == NULL) { ucsend_debug_end (fquote, TRUE); ulog (LOG_ERROR, "No login defined"); return FALSE; } if (zlog[0] == '*' && zlog[1] == '\0') { if (zcallout_login == NULL) { int iuuconf; iuuconf = uuconf_callout (puuconf, qsys, &zcallout_login, &zcallout_pass); if (iuuconf == UUCONF_NOT_FOUND || zcallout_login == NULL) { ucsend_debug_end (fquote, TRUE); ulog (LOG_ERROR, "No login defined"); return FALSE; } else if (iuuconf != UUCONF_SUCCESS) { ucsend_debug_end (fquote, TRUE); ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } } zlog = zcallout_login; } zcopy = zbufcpy (zlog); clen = cescape (zcopy); fquote = fcsend_debug (fquote, (size_t) 0, "login"); fquote = fcsend_debug (fquote, clen, zcopy); if (! (*pfwrite) (qconn, zcopy, clen)) { ubuffree (zcopy); ucsend_debug_end (fquote, TRUE); return FALSE; } ubuffree (zcopy); } break; case 'P': { const char *zpass; char *zcopy; size_t clen; if (qsys == NULL) { ucsend_debug_end (fquote, TRUE); ulog (LOG_ERROR, "Illegal use of \\P"); return FALSE; } zpass = qsys->uuconf_zcall_password; if (zpass == NULL) { ucsend_debug_end (fquote, TRUE); ulog (LOG_ERROR, "No password defined"); return FALSE; } if (zpass[0] == '*' && zpass[1] == '\0') { if (zcallout_pass == NULL) { int iuuconf; iuuconf = uuconf_callout (puuconf, qsys, &zcallout_login, &zcallout_pass); if (iuuconf == UUCONF_NOT_FOUND || zcallout_pass == NULL) { ucsend_debug_end (fquote, TRUE); ulog (LOG_ERROR, "No password defined"); return FALSE; } else if (iuuconf != UUCONF_SUCCESS) { ucsend_debug_end (fquote, TRUE); ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } } zpass = zcallout_pass; } zcopy = zbufcpy (zpass); clen = cescape (zcopy); fquote = fcsend_debug (fquote, (size_t) 0, "password"); fquote = fcsend_debug (fquote, clen, zcopy); if (! (*pfwrite) (qconn, zcopy, clen)) { ubuffree (zcopy); ucsend_debug_end (fquote, TRUE); return FALSE; } ubuffree (zcopy); } break; case 'D': if (qdial == NULL || zphone == NULL) { ucsend_debug_end (fquote, TRUE); ulog (LOG_ERROR, "Illegal use of \\D"); return FALSE; } fquote = fcsend_debug (fquote, (size_t) 0, "\\D"); if (! fcphone (qconn, puuconf, qdial, zphone, pfwrite, ftranslate, &fquote)) { ucsend_debug_end (fquote, TRUE); return FALSE; } break; case 'T': if (qdial == NULL || zphone == NULL) { ucsend_debug_end (fquote, TRUE); ulog (LOG_ERROR, "Illegal use of \\T"); return FALSE; } fquote = fcsend_debug (fquote, (size_t) 0, "\\T"); if (! fcphone (qconn, puuconf, qdial, zphone, pfwrite, TRUE, &fquote)) { ucsend_debug_end (fquote, TRUE); return FALSE; } break; case 'M': fquote = fcsend_debug (fquote, (size_t) 0, "ignore-carrier"); if (! fconn_carrier (qconn, FALSE)) { ucsend_debug_end (fquote, TRUE); return FALSE; } break; case 'm': if (qdial == NULL || qdial->uuconf_fcarrier) { fquote = fcsend_debug (fquote, (size_t) 0, "need-carrier"); if (! fconn_carrier (qconn, TRUE)) { ucsend_debug_end (fquote, TRUE); return FALSE; } } break; default: /* This error message will screw up any debugging information, but it's easily avoidable. */ ulog (LOG_ERROR, "Unrecognized escape sequence \\%c in send string", *z); fsend = TRUE; bsend = *z; break; } ++z; break; #if DEBUG > 0 default: ulog (LOG_FATAL, "fcsend: Can't happen"); break; #endif } if (fsend) { fquote = fcsend_debug (fquote, (size_t) 1, &bsend); if (! (*pfwrite) (qconn, &bsend, (size_t) 1)) { ucsend_debug_end (fquote, TRUE); return FALSE; } } } xfree ((pointer) zcallout_login); xfree ((pointer) zcallout_pass); /* Output a final carriage return, unless there was a \c. Don't bother to check for an echo. */ if (! fnocr) { char b; b = '\r'; fquote = fcsend_debug (fquote, (size_t) 1, &b); if (! fconn_write (qconn, &b, (size_t) 1)) { ucsend_debug_end (fquote, TRUE); return FALSE; } } ucsend_debug_end (fquote, FALSE); return TRUE; } /* Write out a phone number with optional dialcode translation. The pfquote argument is only used for debugging. */ static boolean fcphone (qconn, puuconf, qdial, zphone, pfwrite, ftranslate, pfquote) struct sconnection *qconn; pointer puuconf; const struct uuconf_dialer *qdial; const char *zphone; boolean (*pfwrite) P((struct sconnection *qc, const char *zwrite, size_t cwrite)); boolean ftranslate; boolean *pfquote; { const char *zprefix, *zsuffix; if (ftranslate) { if (! fctranslate (puuconf, zphone, &zprefix, &zsuffix)) return FALSE; } else { zprefix = zphone; zsuffix = NULL; } while (zprefix != NULL) { while (TRUE) { const char *z; const char *zstr; z = zprefix + strcspn ((char *) zprefix, "=-"); if (z > zprefix) { size_t clen; clen = z - zprefix; *pfquote = fcsend_debug (*pfquote, clen, zprefix); if (! (*pfwrite) (qconn, zprefix, clen)) return FALSE; } if (*z == '=') zstr = qdial->uuconf_zdialtone; else if (*z == '-') zstr = qdial->uuconf_zpause; else /* *z == '\0' */ break; if (zstr != NULL) { *pfquote = fcsend_debug (*pfquote, strlen (zstr), zstr); if (! (*pfwrite) (qconn, zstr, strlen (zstr))) return FALSE; } zprefix = z + 1; } zprefix = zsuffix; zsuffix = NULL; } return TRUE; } /* Given a phone number, run it through dial code translation returning two strings. */ static boolean fctranslate (puuconf, zphone, pzprefix, pzsuffix) pointer puuconf; const char *zphone; const char **pzprefix; const char **pzsuffix; { int iuuconf; char *zdialcode, *zto; const char *zfrom; char *ztrans; *pzprefix = zphone; *pzsuffix = NULL; zdialcode = zbufalc (strlen (zphone) + 1); zfrom = zphone; zto = zdialcode; while (*zfrom != '\0' && isalpha (BUCHAR (*zfrom))) *zto++ = *zfrom++; *zto = '\0'; if (*zdialcode == '\0') { ubuffree (zdialcode); return TRUE; } iuuconf = uuconf_dialcode (puuconf, zdialcode, &ztrans); ubuffree (zdialcode); if (iuuconf == UUCONF_NOT_FOUND) return TRUE; else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } else { /* We really should figure out a way to free up ztrans here. */ *pzprefix = ztrans; *pzsuffix = zfrom; return TRUE; } } /* Write out a string making sure the each character is echoed back. There are two versions of this function, one which strips the parity bit from the characters and one which does not. This is so that I can use a single function pointer in fcsend, and to avoid using any static variables so that I can put chat scripts in a library some day. */ static boolean fcecho_send_strip (qconn, zwrite, cwrite) struct sconnection *qconn; const char *zwrite; size_t cwrite; { return fcecho_send (qconn, zwrite, cwrite, TRUE); } static boolean fcecho_send_nostrip (qconn, zwrite, cwrite) struct sconnection *qconn; const char *zwrite; size_t cwrite; { return fcecho_send (qconn, zwrite, cwrite, FALSE); } static boolean fcecho_send (qconn, zwrite, cwrite, fstrip) struct sconnection *qconn; const char *zwrite; size_t cwrite; boolean fstrip; { const char *zend; zend = zwrite + cwrite; for (; zwrite < zend; zwrite++) { int b; char bwrite; bwrite = *zwrite; if (! fconn_write (qconn, &bwrite, (size_t) 1)) return FALSE; if (fstrip) bwrite &= 0x7f; do { /* We arbitrarily wait five seconds for the echo. */ b = breceive_char (qconn, 5, TRUE); /* Now b == -1 on timeout, -2 on error. */ if (b < 0) { if (b == -1) ulog (LOG_ERROR, "Character not echoed"); return FALSE; } if (fstrip) b &= 0x7f; } while (b != BUCHAR (bwrite)); } return TRUE; } /* Run a chat program. Expand any escape sequences and call a system dependent program to run it. */ static boolean fcprogram (qconn, puuconf, pzprogram, qsys, qdial, zphone, zport, ibaud) struct sconnection *qconn; pointer puuconf; char **pzprogram; const struct uuconf_system *qsys; const struct uuconf_dialer *qdial; const char *zphone; const char *zport; long ibaud; { size_t cargs; char **pzpass, **pzarg; char **pz; char *zcallout_login; char *zcallout_pass; boolean fret; cargs = 1; for (pz = pzprogram; *pz != NULL; pz++) ++cargs; pzpass = (char **) xmalloc (cargs * sizeof (char *)); zcallout_login = NULL; zcallout_pass = NULL; fret = TRUE; /* Copy the string into memory expanding escape sequences. */ for (pz = pzprogram, pzarg = pzpass; *pz != NULL; pz++, pzarg++) { const char *zfrom; size_t calc, clen; char *zto; if (strchr (*pz, '\\') == NULL) { *pzarg = zbufcpy (*pz); continue; } *pzarg = NULL; zto = NULL; calc = 0; clen = 0; for (zfrom = *pz; *zfrom != '\0'; zfrom++) { const char *zadd = NULL; char *zfree = NULL; size_t cadd; char abadd[15]; if (*zfrom != '\\') { if (clen + 2 > calc) { char *znew; calc = clen + 50; znew = zbufalc (calc); memcpy (znew, *pzarg, clen); ubuffree (*pzarg); *pzarg = znew; zto = znew + clen; } *zto++ = *zfrom; ++clen; continue; } ++zfrom; switch (*zfrom) { case '\0': --zfrom; /* Fall through. */ case '\\': zadd = "\\"; break; case 'L': { const char *zlog; if (qsys == NULL) { ulog (LOG_ERROR, "chat-program: Illegal use of \\L"); fret = FALSE; break; } zlog = qsys->uuconf_zcall_login; if (zlog == NULL) { ulog (LOG_ERROR, "chat-program: No login defined"); fret = FALSE; break; } if (zlog[0] == '*' && zlog[1] == '\0') { if (zcallout_login == NULL) { int iuuconf; iuuconf = uuconf_callout (puuconf, qsys, &zcallout_login, &zcallout_pass); if (iuuconf == UUCONF_NOT_FOUND || zcallout_login == NULL) { ulog (LOG_ERROR, "chat-program: No login defined"); fret = FALSE; break; } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fret = FALSE; break; } } zlog = zcallout_login; } zfree = zbufcpy (zlog); (void) cescape (zfree); zadd = zfree; } break; case 'P': { const char *zpass; if (qsys == NULL) { ulog (LOG_ERROR, "chat-program: Illegal use of \\P"); fret = FALSE; break; } zpass = qsys->uuconf_zcall_password; if (zpass == NULL) { ulog (LOG_ERROR, "chat-program: No password defined"); fret = FALSE; break; } if (zpass[0] == '*' && zpass[1] == '\0') { if (zcallout_pass == NULL) { int iuuconf; iuuconf = uuconf_callout (puuconf, qsys, &zcallout_login, &zcallout_pass); if (iuuconf == UUCONF_NOT_FOUND || zcallout_pass == NULL) { ulog (LOG_ERROR, "chat-program: No password defined"); fret = FALSE; break; } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fret = FALSE; break; } } zpass = zcallout_pass; } zfree = zbufcpy (zpass); (void) cescape (zfree); zadd = zfree; } break; case 'D': if (qdial == NULL || zphone == NULL) { ulog (LOG_ERROR, "chat-program: Illegal use of \\D"); fret = FALSE; break; } zadd = zphone; break; case 'T': { const char *zprefix, *zsuffix; if (qdial == NULL || zphone == NULL) { ulog (LOG_ERROR, "chat-program: Illegal use of \\T"); fret = FALSE; break; } if (! fctranslate (puuconf, zphone, &zprefix, &zsuffix)) { fret = FALSE; break; } if (zsuffix == NULL) zadd = zprefix; else { size_t cprefix; cprefix = strlen (zprefix); if (clen + cprefix + 1 > calc) { char *znew; calc = clen + cprefix + 20; znew = zbufalc (calc); memcpy (znew, *pzarg, clen); ubuffree (*pzarg); *pzarg = znew; zto = znew + clen; } memcpy (zto, zprefix, cprefix); zto += cprefix; clen += cprefix; zadd = zsuffix; } } break; case 'Y': if (zLdevice == NULL && zport == NULL) { ulog (LOG_ERROR, "chat-program: Illegal use of \\Y"); fret = FALSE; break; } /* zLdevice will generally make more sense than zport, but it might not be set yet. */ zadd = zLdevice; if (zadd == NULL) zadd = zport; break; case 'Z': if (qsys == NULL) { ulog (LOG_ERROR, "chat-program: Illegal use of \\Z"); fret = FALSE; break; } zadd = qsys->uuconf_zname; break; case 'S': { if (ibaud == 0) { ulog (LOG_ERROR, "chat-program: Illegal use of \\S"); fret = FALSE; break; } sprintf (abadd, "%ld", ibaud); zadd = abadd; } break; default: { ulog (LOG_ERROR, "chat-program: Unrecognized escape sequence \\%c", *zfrom); abadd[0] = *zfrom; abadd[1] = '\0'; zadd = abadd; } break; } if (! fret) break; cadd = strlen (zadd); if (clen + cadd + 1 > calc) { char *znew; calc = clen + cadd + 20; znew = zbufalc (calc); memcpy (znew, *pzarg, clen); ubuffree (*pzarg); *pzarg = znew; zto = znew + clen; } memcpy (zto, zadd, cadd + 1); zto += cadd; clen += cadd; ubuffree (zfree); } if (! fret) break; *zto++ = '\0'; ++clen; } *pzarg = NULL; if (fret) fret = fconn_run_chat (qconn, pzpass); for (pz = pzpass; *pz != NULL; pz++) ubuffree (*pz); xfree ((pointer) pzpass); xfree ((pointer) zcallout_login); xfree ((pointer) zcallout_pass); return fret; } uucp-1.07/conn.c0000664000076400007640000003461107665321755007236 /* conn.c Connection routines for the Taylor UUCP package. Copyright (C) 1991, 1992, 1993, 1994, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char conn_rcsid[] = "$Id: conn.c,v 1.18 2002/03/05 19:10:41 ian Rel $"; #endif #include #include "uudefs.h" #include "uuconf.h" #include "conn.h" /* Create a new connection. This relies on system dependent functions to set the qcmds and psysdep fields. If qport is NULL, it opens a standard input port, in which case ttype is the type of port to use. */ boolean fconn_init (qport, qconn, ttype) struct uuconf_port *qport; struct sconnection *qconn; enum uuconf_porttype ttype; { qconn->qport = qport; switch (qport == NULL ? ttype : qport->uuconf_ttype) { case UUCONF_PORTTYPE_STDIN: return fsysdep_stdin_init (qconn); case UUCONF_PORTTYPE_MODEM: return fsysdep_modem_init (qconn); case UUCONF_PORTTYPE_DIRECT: return fsysdep_direct_init (qconn); #if HAVE_TCP case UUCONF_PORTTYPE_TCP: return fsysdep_tcp_init (qconn); #endif #if HAVE_TLI case UUCONF_PORTTYPE_TLI: return fsysdep_tli_init (qconn); #endif case UUCONF_PORTTYPE_PIPE: return fsysdep_pipe_init (qconn); default: ulog (LOG_ERROR, "Unknown or unsupported port type"); return FALSE; } } /* Connection dispatch routines. */ /* Free a connection. */ void uconn_free (qconn) struct sconnection *qconn; { (*qconn->qcmds->pufree) (qconn); } /* Lock a connection. */ boolean fconn_lock (qconn, fin, fuser) struct sconnection *qconn; boolean fin; boolean fuser; { boolean (*pflock) P((struct sconnection *, boolean, boolean)); pflock = qconn->qcmds->pflock; if (pflock == NULL) return TRUE; return (*pflock) (qconn, fin, fuser); } /* Unlock a connection. */ boolean fconn_unlock (qconn) struct sconnection *qconn; { boolean (*pfunlock) P((struct sconnection *)); pfunlock = qconn->qcmds->pfunlock; if (pfunlock == NULL) return TRUE; return (*pfunlock) (qconn); } /* Open a connection. */ boolean fconn_open (qconn, ibaud, ihighbaud, fwait, fuser) struct sconnection *qconn; long ibaud; long ihighbaud; boolean fwait; boolean fuser; { boolean fret; #if DEBUG > 1 if (FDEBUGGING (DEBUG_PORT)) { char abspeed[20]; if (ibaud == (long) 0) strcpy (abspeed, "default speed"); else sprintf (abspeed, "speed %ld", ibaud); if (qconn->qport == NULL) ulog (LOG_DEBUG, "fconn_open: Opening stdin port (%s)", abspeed); else if (qconn->qport->uuconf_zname == NULL) ulog (LOG_DEBUG, "fconn_open: Opening unnamed port (%s)", abspeed); else ulog (LOG_DEBUG, "fconn_open: Opening port %s (%s)", qconn->qport->uuconf_zname, abspeed); } #endif /* If the system provides a range of baud rates, we select the highest baud rate supported by the port. */ if (ihighbaud != 0 && qconn->qport != NULL) { struct uuconf_port *qport; qport = qconn->qport; ibaud = ihighbaud; if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM) { if (qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud != 0) { if (qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud < ibaud) ibaud = qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud; } else if (qport->uuconf_u.uuconf_smodem.uuconf_ibaud != 0) ibaud = qport->uuconf_u.uuconf_smodem.uuconf_ibaud; } else if (qport->uuconf_ttype == UUCONF_PORTTYPE_DIRECT) { if (qport->uuconf_u.uuconf_sdirect.uuconf_ibaud != 0) ibaud = qport->uuconf_u.uuconf_sdirect.uuconf_ibaud; } } /* This will normally be overridden by the port specific open routine. */ if (qconn->qport == NULL) ulog_device ("stdin"); else ulog_device (qconn->qport->uuconf_zname); fret = (*qconn->qcmds->pfopen) (qconn, ibaud, fwait, fuser); if (! fret) ulog_device ((const char *) NULL); return fret; } /* Close a connection. */ boolean fconn_close (qconn, puuconf, qdialer, fsuccess) struct sconnection *qconn; pointer puuconf; struct uuconf_dialer *qdialer; boolean fsuccess; { boolean fret; DEBUG_MESSAGE0 (DEBUG_PORT, "fconn_close: Closing connection"); /* Don't report hangup signals while we're closing. */ fLog_sighup = FALSE; fret = (*qconn->qcmds->pfclose) (qconn, puuconf, qdialer, fsuccess); /* Ignore any SIGHUP we may have gotten, and make sure any signal reporting has been done before we reset fLog_sighup. */ afSignal[INDEXSIG_SIGHUP] = FALSE; ulog (LOG_ERROR, (const char *) NULL); fLog_sighup = TRUE; ulog_device ((const char *) NULL); return fret; } /* Dial out on the connection. */ boolean fconn_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound) struct sconnection *qconn; pointer puuconf; const struct uuconf_system *qsys; const char *zphone; struct uuconf_dialer *qdialer; enum tdialerfound *ptdialerfound; { struct uuconf_dialer sdialer; enum tdialerfound tfound; boolean (*pfdial) P((struct sconnection *, pointer, const struct uuconf_system *, const char *, struct uuconf_dialer *, enum tdialerfound *)); if (qdialer == NULL) qdialer = &sdialer; if (ptdialerfound == NULL) ptdialerfound = &tfound; qdialer->uuconf_zname = NULL; *ptdialerfound = DIALERFOUND_FALSE; pfdial = qconn->qcmds->pfdial; if (pfdial == NULL) return TRUE; return (*pfdial) (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound); } /* Read data from the connection. */ boolean fconn_read (qconn, zbuf, pclen, cmin, ctimeout, freport) struct sconnection *qconn; char *zbuf; size_t *pclen; size_t cmin; int ctimeout; boolean freport; { boolean fret; fret = (*qconn->qcmds->pfread) (qconn, zbuf, pclen, cmin, ctimeout, freport); #if DEBUG > 1 if (FDEBUGGING (DEBUG_INCOMING)) udebug_buffer ("fconn_read: Read", zbuf, *pclen); else if (FDEBUGGING (DEBUG_PORT)) ulog (LOG_DEBUG, "fconn_read: Read %lu", (unsigned long) *pclen); #endif return fret; } /* Write data to the connection. */ boolean fconn_write (qconn, zbuf, clen) struct sconnection *qconn; const char *zbuf; size_t clen; { #if DEBUG > 1 if (FDEBUGGING (DEBUG_OUTGOING)) udebug_buffer ("fconn_write: Writing", zbuf, clen); else if (FDEBUGGING (DEBUG_PORT)) ulog (LOG_DEBUG, "fconn_write: Writing %lu", (unsigned long) clen); #endif return (*qconn->qcmds->pfwrite) (qconn, zbuf, clen); } /* Read and write data. */ boolean fconn_io (qconn, zwrite, pcwrite, zread, pcread) struct sconnection *qconn; const char *zwrite; size_t *pcwrite; char *zread; size_t *pcread; { boolean fret; #if DEBUG > 1 size_t cwrite = *pcwrite; size_t cread = *pcread; if (cread == 0 || cwrite == 0) ulog (LOG_FATAL, "fconn_io: cread %lu; cwrite %lu", (unsigned long) cread, (unsigned long) cwrite); #endif #if DEBUG > 1 if (FDEBUGGING (DEBUG_OUTGOING)) udebug_buffer ("fconn_io: Writing", zwrite, cwrite); #endif fret = (*qconn->qcmds->pfio) (qconn, zwrite, pcwrite, zread, pcread); DEBUG_MESSAGE4 (DEBUG_PORT, "fconn_io: Wrote %lu of %lu, read %lu of %lu", (unsigned long) *pcwrite, (unsigned long) cwrite, (unsigned long) *pcread, (unsigned long) cread); #if DEBUG > 1 if (*pcread > 0 && FDEBUGGING (DEBUG_INCOMING)) udebug_buffer ("fconn_io: Read", zread, *pcread); #endif return fret; } /* Send a break character to a connection. Some port types may not support break characters, in which case we just return TRUE. */ boolean fconn_break (qconn) struct sconnection *qconn; { boolean (*pfbreak) P((struct sconnection *)); pfbreak = qconn->qcmds->pfbreak; if (pfbreak == NULL) return TRUE; DEBUG_MESSAGE0 (DEBUG_PORT, "fconn_break: Sending break character"); return (*pfbreak) (qconn); } /* Change the setting of a connection. Some port types may not support this, in which case we just return TRUE. */ boolean fconn_set (qconn, tparity, tstrip, txonxoff) struct sconnection *qconn; enum tparitysetting tparity; enum tstripsetting tstrip; enum txonxoffsetting txonxoff; { boolean (*pfset) P((struct sconnection *, enum tparitysetting, enum tstripsetting, enum txonxoffsetting)); pfset = qconn->qcmds->pfset; if (pfset == NULL) return TRUE; DEBUG_MESSAGE3 (DEBUG_PORT, "fconn_set: Changing setting to %d, %d, %d", (int) tparity, (int) tstrip, (int) txonxoff); return (*pfset) (qconn, tparity, tstrip, txonxoff); } /* Require or ignore carrier on a connection. */ boolean fconn_carrier (qconn, fcarrier) struct sconnection *qconn; boolean fcarrier; { boolean (*pfcarrier) P((struct sconnection *, boolean)); pfcarrier = qconn->qcmds->pfcarrier; if (pfcarrier == NULL) return TRUE; return (*pfcarrier) (qconn, fcarrier); } /* Run a chat program on a connection. */ boolean fconn_run_chat (qconn, pzprog) struct sconnection *qconn; char **pzprog; { return (*qconn->qcmds->pfchat) (qconn, pzprog); } /* Get the baud rate of a connection. */ long iconn_baud (qconn) struct sconnection *qconn; { long (*pibaud) P((struct sconnection *)); pibaud = qconn->qcmds->pibaud; if (pibaud == NULL) return 0; return (*pibaud) (qconn); } /* Run through a dialer sequence. The pzdialer argument is a list of strings, which are considered in dialer/token pairs. The dialer string names a dialer to use. The token string is what \D and \T in the chat script expand to. If there is no token for the last dialer, the zphone argument is used. The qdialer argument is filled in with information for the first dialer, and *ptdialerfound is set to whether the information should be freed or not. However, if *ptdialerfound is not DIALERFOUND_FALSE when this function is called, then the information for the first dialer is already in qdialer. */ boolean fconn_dial_sequence (qconn, puuconf, pzdialer, qsys, zphone, qdialer, ptdialerfound) struct sconnection *qconn; pointer puuconf; char **pzdialer; const struct uuconf_system *qsys; const char *zphone; struct uuconf_dialer *qdialer; enum tdialerfound *ptdialerfound; { const char *zname; boolean ffirst, ffreefirst; if (qconn->qport == NULL) zname = NULL; else zname = qconn->qport->uuconf_zname; ffirst = TRUE; ffreefirst = FALSE; while (*pzdialer != NULL) { struct uuconf_dialer *q; struct uuconf_dialer s; const char *ztoken; boolean ftranslate; if (! ffirst) q = &s; else q = qdialer; if (! ffirst || *ptdialerfound == DIALERFOUND_FALSE) { int iuuconf; iuuconf = uuconf_dialer_info (puuconf, *pzdialer, q); if (iuuconf == UUCONF_NOT_FOUND) { ulog (LOG_ERROR, "%s: Dialer not found", *pzdialer); if (ffreefirst) (void) uuconf_dialer_free (puuconf, qdialer); return FALSE; } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); if (ffreefirst) (void) uuconf_dialer_free (puuconf, qdialer); return FALSE; } if (ffirst) { *ptdialerfound = DIALERFOUND_FREE; ffreefirst = TRUE; } } ++pzdialer; ztoken = *pzdialer; ftranslate = FALSE; if (ztoken == NULL || strcmp (ztoken, "\\D") == 0) ztoken = zphone; else if (strcmp (ztoken, "\\T") == 0) { ztoken = zphone; ftranslate = TRUE; } if (! fchat (qconn, puuconf, &q->uuconf_schat, qsys, q, ztoken, ftranslate, zname, iconn_baud (qconn))) { if (q == &s) (void) uuconf_dialer_free (puuconf, q); if (ffreefirst) (void) uuconf_dialer_free (puuconf, qdialer); return FALSE; } if (ffirst) ffirst = FALSE; else (void) uuconf_dialer_free (puuconf, q); if (*pzdialer != NULL) ++pzdialer; } return TRUE; } /* Modem dialing routine. */ /*ARGSUSED*/ boolean fmodem_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound) struct sconnection *qconn; pointer puuconf; const struct uuconf_system *qsys; const char *zphone; struct uuconf_dialer *qdialer; enum tdialerfound *ptdialerfound; { char **pzdialer; *ptdialerfound = DIALERFOUND_FALSE; pzdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer; if (pzdialer != NULL && *pzdialer != NULL) { int iuuconf; boolean fret; iuuconf = uuconf_dialer_info (puuconf, *pzdialer, qdialer); if (iuuconf == UUCONF_NOT_FOUND) { ulog (LOG_ERROR, "%s: Dialer not found", *pzdialer); return FALSE; } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } *ptdialerfound = DIALERFOUND_FREE; fret = (fsysdep_modem_begin_dial (qconn, qdialer) && fconn_dial_sequence (qconn, puuconf, pzdialer, qsys, zphone, qdialer, ptdialerfound) && fsysdep_modem_end_dial (qconn, qdialer)); if (! fret) (void) uuconf_dialer_free (puuconf, qdialer); return fret; } else if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_qdialer != NULL) { struct uuconf_dialer *q; const char *zname; q = qconn->qport->uuconf_u.uuconf_smodem.uuconf_qdialer; *qdialer = *q; *ptdialerfound = DIALERFOUND_TRUE; if (qconn->qport == NULL) zname = NULL; else zname = qconn->qport->uuconf_zname; return (fsysdep_modem_begin_dial (qconn, q) && fchat (qconn, puuconf, &q->uuconf_schat, qsys, q, zphone, FALSE, zname, iconn_baud (qconn)) && fsysdep_modem_end_dial (qconn, q)); } else { ulog (LOG_ERROR, "No dialer information"); return FALSE; } } uucp-1.07/copy.c0000664000076400007640000001205307665321755007247 /* copy.c Copy one file to another for the UUCP package. Copyright (C) 1991, 1992, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char copy_rcsid[] = "$Id: copy.c,v 1.22 2002/03/05 19:10:41 ian Rel $"; #endif #include "uudefs.h" #include "system.h" #include "sysdep.h" #include #include /* Copy one file to another. */ #if USE_STDIO boolean fcopy_file (zfrom, zto, fpublic, fmkdirs, fsignals) const char *zfrom; const char *zto; boolean fpublic; boolean fmkdirs; boolean fsignals; { FILE *efrom; boolean fret; efrom = fopen (zfrom, BINREAD); if (efrom == NULL) { ulog (LOG_ERROR, "fopen (%s): %s", zfrom, strerror (errno)); return FALSE; } fret = fcopy_open_file (efrom, zto, fpublic, fmkdirs, fsignals); (void) fclose (efrom); return fret; } boolean fcopy_open_file (efrom, zto, fpublic, fmkdirs, fsignals) FILE *efrom; const char *zto; boolean fpublic; boolean fmkdirs; boolean fsignals; { FILE *eto; char ab[8192]; size_t c; eto = esysdep_fopen (zto, fpublic, FALSE, fmkdirs); if (eto == NULL) return FALSE; while ((c = fread (ab, sizeof (char), sizeof ab, efrom)) != 0) { if (fwrite (ab, sizeof (char), (size_t) c, eto) != c) { ulog (LOG_ERROR, "fwrite: %s", strerror (errno)); (void) fclose (eto); (void) remove (zto); return FALSE; } if (fsignals && FGOT_SIGNAL ()) { /* Log the signal. */ ulog (LOG_ERROR, (const char *) NULL); (void) fclose (eto); (void) remove (zto); return FALSE; } } if (! fsysdep_sync (eto, zto)) { (void) fclose (eto); (void) remove (zto); return FALSE; } if (fclose (eto) != 0) { ulog (LOG_ERROR, "fclose: %s", strerror (errno)); (void) remove (zto); return FALSE; } if (ferror (efrom)) { ulog (LOG_ERROR, "fread: %s", strerror (errno)); (void) remove (zto); return FALSE; } return TRUE; } #else /* ! USE_STDIO */ #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif boolean fcopy_file (zfrom, zto, fpublic, fmkdirs, fsignals) const char *zfrom; const char *zto; boolean fpublic; boolean fmkdirs; boolean fsignals; { int ofrom; boolean fret; ofrom = open (zfrom, O_RDONLY | O_NOCTTY, 0); if (ofrom < 0) { ulog (LOG_ERROR, "open (%s): %s", zfrom, strerror (errno)); return FALSE; } fret = fcopy_open_file (ofrom, zto, fpublic, fmkdirs, fsignals); (void) close (ofrom); return fret; } boolean fcopy_open_file (ofrom, zto, fpublic, fmkdirs, fsignals) int ofrom; const char *zto; boolean fpublic; boolean fmkdirs; boolean fsignals; { int oto; char ab[8192]; int c; /* These file mode arguments are from the UNIX version of sysdep.h; each system dependent header file will need their own definitions. */ oto = creat (zto, fpublic ? IPUBLIC_FILE_MODE : IPRIVATE_FILE_MODE); if (oto < 0) { if (errno == ENOENT && fmkdirs) { if (! fsysdep_make_dirs (zto, fpublic)) return FALSE; oto = creat (zto, fpublic ? IPUBLIC_FILE_MODE : IPRIVATE_FILE_MODE); } if (oto < 0) { ulog (LOG_ERROR, "open (%s): %s", zto, strerror (errno)); return FALSE; } } while ((c = read (ofrom, ab, sizeof ab)) > 0) { if (write (oto, ab, (size_t) c) != c) { ulog (LOG_ERROR, "write: %s", strerror (errno)); (void) close (oto); (void) remove (zto); return FALSE; } if (fsignals && FGOT_SIGNAL ()) { /* Log the signal. */ ulog (LOG_ERROR, (const char *) NULL); (void) fclose (eto); (void) remove (zto); return FALSE; } } if (! fsysdep_sync (oto, zto)) { (void) close (oto); (void) remove (zto); return FALSE; } if (close (oto) < 0) { ulog (LOG_ERROR, "close: %s", strerror (errno)); (void) remove (zto); return FALSE; } if (c < 0) { ulog (LOG_ERROR, "read: %s", strerror (errno)); (void) remove (zto); return FALSE; } return TRUE; } #endif /* ! USE_STDIO */ uucp-1.07/uucp.h0000664000076400007640000003127507665321757007267 /* uucp.h Header file for the UUCP package. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ /* Get the system configuration parameters. */ #include "config.h" #include "policy.h" /* Get a definition for ANSI_C if we weren't given one. */ #ifndef ANSI_C #ifdef __STDC__ #define ANSI_C 1 #else /* ! defined (__STDC__) */ #define ANSI_C 0 #endif /* ! defined (__STDC__) */ #endif /* ! defined (ANSI_C) */ /* Pass this definition into uuconf.h. */ #define UUCONF_ANSI_C ANSI_C /* We always include some standard header files. We need to define sig_atomic_t. */ #include #include #if HAVE_STDDEF_H #include #endif /* On some systems we need to get sig_atomic_t or size_t or time_t. */ #if ! HAVE_SIG_ATOMIC_T_IN_SIGNAL_H && HAVE_SIG_ATOMIC_T_IN_TYPES_H #define USE_TYPES_H 1 #else #if ! HAVE_SIZE_T_IN_STDDEF_H && HAVE_SIZE_T_IN_TYPES_H #define USE_TYPES_H 1 #else #if ! HAVE_TIME_T_IN_TIME_H && HAVE_TIME_T_IN_TYPES_H #define USE_TYPES_H 1 #endif #endif #endif #ifndef USE_TYPES_H #define USE_TYPES_H 0 #endif #if USE_TYPES_H #include #endif /* Make sure we have sig_atomic_t. */ #if ! HAVE_SIG_ATOMIC_T_IN_SIGNAL_H && ! HAVE_SIG_ATOMIC_T_IN_TYPES_H #ifndef SIG_ATOMIC_T /* There is no portable definition for sig_atomic_t. */ #define SIG_ATOMIC_T char #endif /* ! defined (SIG_ATOMIC_T) */ typedef SIG_ATOMIC_T sig_atomic_t; #endif /* ! HAVE_SIG_ATOMIC_T_IN_SIGNAL_H && ! HAVE_SIG_ATOMIC_T_IN_TYPES_H */ /* Make sure we have size_t. */ #if ! HAVE_SIZE_T_IN_STDDEF_H && ! HAVE_SIZE_T_IN_TYPES_H #ifndef SIZE_T #define SIZE_T unsigned #endif /* ! defined (SIZE_T) */ typedef SIZE_T size_t; #endif /* ! HAVE_SIZE_T_IN_STDDEF_H && ! HAVE_SIZE_T_IN_TYPES_H */ /* Make sure we have time_t. We use long as the default. We don't bother to let conf.h override this, since on a system which doesn't define time_t long must be correct. */ #if ! HAVE_TIME_T_IN_TIME_H && ! HAVE_TIME_T_IN_TYPES_H typedef long time_t; #endif /* Set up some definitions for both ANSI C and Classic C. P() -- for function prototypes (e.g. extern int foo P((int)) ). pointer -- for a generic pointer (i.e. void *). constpointer -- for a generic pointer to constant data. BUCHAR -- to convert a character to unsigned. */ #if ANSI_C #if ! HAVE_VOID || ! HAVE_UNSIGNED_CHAR || ! HAVE_PROTOTYPES #error ANSI C compiler without void or unsigned char or prototypes #endif #define P(x) x typedef void *pointer; typedef const void *constpointer; #define BUCHAR(b) ((unsigned char) (b)) #else /* ! ANSI_C */ /* Handle uses of volatile and void in Classic C. */ #define volatile #if ! HAVE_VOID #define void int #endif #if HAVE_PROTOTYPES #define P(x) x #else #define P(x) () #endif typedef char *pointer; typedef const char *constpointer; #if HAVE_UNSIGNED_CHAR #define BUCHAR(b) ((unsigned char) (b)) #else /* ! HAVE_UNSIGNED_CHAR */ /* This should work on most systems, but not necessarily all. */ #define BUCHAR(b) ((b) & 0xff) #endif /* ! HAVE_UNSIGNED_CHAR */ #endif /* ! ANSI_C */ /* Make sure we have a definition for offsetof. */ #ifndef offsetof #define offsetof(type, field) \ ((size_t) ((char *) &(((type *) 0)->field) - (char *) (type *) 0)) #endif /* Only use inline with gcc. */ #ifndef __GNUC__ #define __inline__ #endif /* Some boilerplate code to permit using gcc attributes while supporting older versions of gcc and also other compilers. */ /* This macro simplifies testing whether we are using gcc, and if it is of a particular minimum version. (Both major & minor numbers are significant.) This macro will evaluate to 0 if we are not using gcc at all. */ #ifndef GCC_VERSION #define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) #endif /* GCC_VERSION */ /* Define macros for some gcc attributes. This permits us to use the macros freely, and know that they will come into play for the version of gcc in which they are supported. */ #if (GCC_VERSION < 2007) # define __attribute__(x) #endif /* Attribute __malloc__ on functions was valid as of gcc 2.96. */ #ifndef ATTRIBUTE_MALLOC # if (GCC_VERSION >= 2096) # define ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) # else # define ATTRIBUTE_MALLOC # endif /* GNUC >= 2.96 */ #endif /* ATTRIBUTE_MALLOC */ /* Attributes on labels were valid as of gcc 2.93. */ #ifndef ATTRIBUTE_UNUSED_LABEL # if (GCC_VERSION >= 2093) # define ATTRIBUTE_UNUSED_LABEL ATTRIBUTE_UNUSED # else # define ATTRIBUTE_UNUSED_LABEL # endif /* GNUC >= 2.93 */ #endif /* ATTRIBUTE_UNUSED_LABEL */ #ifndef ATTRIBUTE_UNUSED #define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) #endif /* ATTRIBUTE_UNUSED */ #ifndef ATTRIBUTE_NORETURN #define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) #endif /* ATTRIBUTE_NORETURN */ #ifndef ATTRIBUTE_PRINTF #define ATTRIBUTE_PRINTF(m, n) __attribute__ ((__format__ (__printf__, m, n))) #define ATTRIBUTE_PRINTF_1 ATTRIBUTE_PRINTF(1, 2) #define ATTRIBUTE_PRINTF_2 ATTRIBUTE_PRINTF(2, 3) #define ATTRIBUTE_PRINTF_3 ATTRIBUTE_PRINTF(3, 4) #define ATTRIBUTE_PRINTF_4 ATTRIBUTE_PRINTF(4, 5) #define ATTRIBUTE_PRINTF_5 ATTRIBUTE_PRINTF(5, 6) #endif /* ATTRIBUTE_PRINTF */ /* Get the string functions, which are used throughout the code. */ #if HAVE_MEMORY_H #include #else /* We really need a definition for memchr, and this should not conflict with anything in . I hope. */ extern pointer memchr (); #endif #if HAVE_STRING_H #include #else /* ! HAVE_STRING_H */ #if HAVE_STRINGS_H #include #else /* ! HAVE_STRINGS_H */ extern char *strcpy (), *strncpy (), *strchr (), *strrchr (), *strtok (); extern char *strcat (), *strerror (), *strstr (); extern size_t strlen (), strspn (), strcspn (); #if ! HAVE_MEMORY_H extern pointer memcpy (), memchr (); #endif /* ! HAVE_MEMORY_H */ #endif /* ! HAVE_STRINGS_H */ #endif /* ! HAVE_STRING_H */ /* Get what we need from . */ #if HAVE_STDLIB_H #include #else /* ! HAVE_STDLIB_H */ extern pointer malloc (), realloc (), bsearch (); extern long strtol (); extern unsigned long strtoul (); extern char *getenv (); #endif /* ! HAVE_STDLIB_H */ /* NeXT uses to declare a bunch of functions. */ #if HAVE_LIBC_H #include #endif /* Make sure we have the EXIT_ macros. */ #ifndef EXIT_SUCCESS #define EXIT_SUCCESS (0) #endif #ifndef EXIT_FAILURE #define EXIT_FAILURE (1) #endif /* If we need to declare errno, do so. I don't want to always do this, because some system might theoretically have a different declaration for errno. On a POSIX system this is sure to work. */ #if ! HAVE_ERRNO_DECLARATION extern int errno; #endif /* If the system has the socket call, guess that we can compile the TCP code. */ #define HAVE_TCP HAVE_SOCKET /* If the system has the t_open call, guess that we can compile the TLI code. */ #define HAVE_TLI HAVE_T_OPEN /* The boolean type holds boolean values. */ typedef int boolean; #undef TRUE #undef FALSE #define TRUE (1) #define FALSE (0) /* The openfile_t type holds an open file. This depends on whether we are using stdio or not. */ #if USE_STDIO typedef FILE *openfile_t; #define EFILECLOSED ((FILE *) NULL) #define ffileisopen(e) ((e) != NULL) #define ffileeof(e) feof (e) #define cfileread(e, z, c) fread ((z), 1, (c), (e)) #define cfilewrite(e, z, c) fwrite ((z), 1, (c), (e)) #define ffileioerror(e, c) ferror (e) #ifdef SEEK_SET #define ffileseek(e, i) (fseek ((e), (long) (i), SEEK_SET) == 0) #define ffilerewind(e) (fseek ((e), (long) 0, SEEK_SET) == 0) #else #define ffileseek(e, i) (fseek ((e), (long) (i), 0) == 0) #define ffilerewind(e) (fseek ((e), (long) 0, 0) == 0) #endif #ifdef SEEK_END #define ffileseekend(e) (fseek ((e), (long) 0, SEEK_END) == 0) #else #define ffileseekend(e) (fseek ((e), (long) 0, 2) == 0) #endif #define ffileclose(e) (fclose (e) == 0) #define fstdiosync(e, z) (fsysdep_sync (e, z)) #else /* ! USE_STDIO */ #if ! USE_TYPES_H #undef USE_TYPES_H #define USE_TYPES_H 1 #include #endif #if HAVE_UNISTD_H #include #endif #ifdef OFF_T typedef OFF_T off_t; #undef OFF_T #endif typedef int openfile_t; #define EFILECLOSED (-1) #define ffileisopen(e) ((e) >= 0) #define ffileeof(e) (FALSE) #define cfileread(e, z, c) read ((e), (z), (c)) #define cfilewrite(e, z, c) write ((e), (z), (c)) #define ffileioerror(e, c) ((c) < 0) #ifdef SEEK_SET #define ffileseek(e, i) (lseek ((e), (off_t) i, SEEK_SET) >= 0) #define ffilerewind(e) (lseek ((e), (off_t) 0, SEEK_SET) >= 0) #else #define ffileseek(e, i) (lseek ((e), (off_t) i, 0) >= 0) #define ffilerewind(e) (lseek ((e), (off_t) 0, 0) >= 0) #endif #ifdef SEEK_END #define ffileseekend(e) (lseek ((e), (off_t) 0, SEEK_END) >= 0) #else #define ffileseekend(e) (lseek ((e), (off_t) 0, 2) >= 0) #endif #define ffileclose(e) (close (e) >= 0) #define fstdiosync(e, z) (fsysdep_sync (fileno (e), z)) #endif /* ! USE_STDIO */ /* A prototype for main to avoid warnings from gcc 2.0 -Wmissing-prototype option. */ extern int main P((int argc, char **argv)); /* Some standard routines which we only define if they are not present on the system we are compiling on. */ #if ! HAVE_GETLINE /* Read a line from a file. */ extern int getline P((char **pz, size_t *pc, FILE *e)); #endif #if ! HAVE_REMOVE /* Erase a file. */ #undef remove extern int remove P((const char *zfile)); #endif #if ! HAVE_STRDUP /* Copy a string into memory. */ extern char *strdup P((const char *z)); #endif #if ! HAVE_STRSTR /* Look for one string within another. */ extern char *strstr P((const char *zouter, const char *zinner)); #endif #if ! HAVE_STRCASECMP #if HAVE_STRICMP #define strcasecmp stricmp #else /* ! HAVE_STRICMP */ /* Rename strcasecmp to avoid ANSI C name space. */ #define strcasecmp xstrcasecmp extern int strcasecmp P((const char *z1, const char *z2)); #endif /* ! HAVE_STRICMP */ #endif /* ! HAVE_STRCASECMP */ #if ! HAVE_STRNCASECMP #if HAVE_STRNICMP #define strncasecmp strnicmp #else /* ! HAVE_STRNICMP */ /* Rename strncasecmp to avoid ANSI C name space. */ #define strncasecmp xstrncasecmp extern int strncasecmp P((const char *z1, const char *z2, size_t clen)); #endif /* ! HAVE_STRNICMP */ #endif /* ! HAVE_STRNCASECMP */ #if ! HAVE_STRERROR /* Get a string corresponding to an error message. */ #undef strerror extern char *strerror P((int ierr)); #endif /* Get the appropriate definitions for memcmp, memcpy, memchr and bzero. */ #if ! HAVE_MEMCMP #if HAVE_BCMP #define memcmp(p1, p2, c) bcmp ((p1), (p2), (c)) #else /* ! HAVE_BCMP */ extern int memcmp P((constpointer p1, constpointer p2, size_t c)); #endif /* ! HAVE_BCMP */ #endif /* ! HAVE_MEMCMP */ #if ! HAVE_MEMCPY #if HAVE_BCOPY #define memcpy(pto, pfrom, c) bcopy ((pfrom), (pto), (c)) #else /* ! HAVE_BCOPY */ extern pointer memcpy P((pointer pto, constpointer pfrom, size_t c)); #endif /* ! HAVE_BCOPY */ #endif /* ! HAVE_MEMCPY */ #if ! HAVE_MEMCHR extern pointer memchr P((constpointer p, int b, size_t c)); #endif #if ! HAVE_BZERO #if HAVE_MEMSET #define bzero(p, c) memset ((p), 0, (c)) #else /* ! HAVE_MEMSET */ extern void bzero P((pointer p, int c)); #endif /* ! HAVE_MEMSET */ #endif /* ! HAVE_BZERO */ /* Look up a character in a string. */ #if ! HAVE_STRCHR #if HAVE_INDEX #define strchr index extern char *index (); #else /* ! HAVE_INDEX */ extern char *strchr P((const char *z, int b)); #endif /* ! HAVE_INDEX */ #endif /* ! HAVE_STRCHR */ #if ! HAVE_STRRCHR #if HAVE_RINDEX #define strrchr rindex extern char *rindex (); #else /* ! HAVE_RINDEX */ extern char *strrchr P((const char *z, int b)); #endif /* ! HAVE_RINDEX */ #endif /* ! HAVE_STRRCHR */ /* Turn a string into a long integer. */ #if ! HAVE_STRTOL extern long strtol P((const char *, char **, int)); #endif /* Turn a string into a long unsigned integer. */ #if ! HAVE_STRTOUL extern unsigned long strtoul P((const char *, char **, int)); #endif /* Lookup a key in a sorted array. */ #if ! HAVE_BSEARCH extern pointer bsearch P((constpointer pkey, constpointer parray, size_t celes, size_t cbytes, int (*pficmp) P((constpointer, constpointer)))); #endif uucp-1.07/uudefs.h0000664000076400007640000003735407665321757007612 /* uudefs.h Miscellaneous definitions for the UUCP package. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #if ANSI_C /* These structures are used in prototypes but are not defined in this header file. */ struct uuconf_system; struct uuconf_timespan; #endif /* The tlog enumeration holds the different types of logging. */ enum tlog { /* Normal log entry. */ LOG_NORMAL, /* Error log entry. */ LOG_ERROR, /* Fatal log entry. */ LOG_FATAL #if DEBUG > 1 , /* Debugging log entry. */ LOG_DEBUG, /* Start debugging log entry. */ LOG_DEBUG_START, /* Continue debugging log entry. */ LOG_DEBUG_CONTINUE, /* End debugging log entry. */ LOG_DEBUG_END #endif }; /* The tstatus_type enumeration holds the kinds of status information we put in the status file. The order of entries here corresponds to the order of entries in the azStatus array. */ enum tstatus_type { /* Conversation complete. */ STATUS_COMPLETE, /* Port unavailable. */ STATUS_PORT_FAILED, /* Dial failed. */ STATUS_DIAL_FAILED, /* Login failed. */ STATUS_LOGIN_FAILED, /* Handshake failed. */ STATUS_HANDSHAKE_FAILED, /* Failed after logging in. */ STATUS_FAILED, /* Talking to remote system. */ STATUS_TALKING, /* Wrong time to call. */ STATUS_WRONG_TIME, /* Number of status values. */ STATUS_VALUES }; /* An array to convert status entries to strings. If more status entries are added, this array must be extended. */ extern const char *azStatus[]; /* The sstatus structure holds the contents of a system status file. */ struct sstatus { /* Current status of conversation. */ enum tstatus_type ttype; /* Number of failed retries. */ int cretries; /* Time of last call in seconds since epoch (determined by ixsysdep_time). */ long ilast; /* Number of seconds until a retry is permitted. */ int cwait; /* String in status file. Only used when reading status file, not when writing. May be NULL. Should be freed with ubuffree. */ char *zstring; }; /* How long we have to wait for the next call, given the number of retries we have already made. This should probably be configurable. */ #define CRETRY_WAIT(c) ((c) * 10 * 60) /* The scmd structure holds a complete UUCP command. */ struct scmd { /* Command ('S' for send, 'R' for receive, 'X' for execute, 'E' for simple execution, 'H' for hangup, 'Y' for hangup confirm, 'N' for hangup deny). */ char bcmd; /* Grade of the command ('\0' if from remote system). */ char bgrade; /* Sequence handle for fsysdep_did_work. */ pointer pseq; /* File name to transfer from. */ const char *zfrom; /* File name to transfer to. */ const char *zto; /* User who requested transfer. */ const char *zuser; /* Options. */ const char *zoptions; /* Temporary file name ('S' and 'E'). */ const char *ztemp; /* Mode to give newly created file ('S' and 'E'). */ unsigned int imode; /* User to notify on remote system (optional; 'S' and 'E'). */ const char *znotify; /* File size (-1 if not supplied) ('S', 'E' and 'R'). */ long cbytes; /* Command to execute ('E'). */ const char *zcmd; /* Position to restart from ('R'). */ long ipos; }; #if DEBUG > 1 /* We allow independent control over several different types of debugging output, using a bit string with individual bits dedicated to particular debugging types. */ /* The bit string is stored in iDebug. */ extern int iDebug; /* Debug abnormal events. */ #define DEBUG_ABNORMAL (01) /* Debug chat scripts. */ #define DEBUG_CHAT (02) /* Debug initial handshake. */ #define DEBUG_HANDSHAKE (04) /* Debug UUCP protocol. */ #define DEBUG_UUCP_PROTO (010) /* Debug protocols. */ #define DEBUG_PROTO (020) /* Debug port actions. */ #define DEBUG_PORT (040) /* Debug configuration files. */ #define DEBUG_CONFIG (0100) /* Debug spool directory actions. */ #define DEBUG_SPOOLDIR (0200) /* Debug executions. */ #define DEBUG_EXECUTE (0400) /* Debug incoming data. */ #define DEBUG_INCOMING (01000) /* Debug outgoing data. */ #define DEBUG_OUTGOING (02000) /* Maximum possible value for iDebug. */ #define DEBUG_MAX (03777) /* Intializer for array of debug names. The index of the name in the array is the corresponding bit position in iDebug. We only check for prefixes, so these names only need to be long enough to distinguish each name from every other. The last entry must be NULL. The string "all" is also recognized to turn on all debugging. */ #define DEBUG_NAMES \ { "a", "ch", "h", "u", "pr", "po", "co", "s", "e", "i", "o", NULL } /* The prefix to use to turn off all debugging. */ #define DEBUG_NONE "n" /* Check whether a particular type of debugging is being done. */ #define FDEBUGGING(i) ((iDebug & (i)) != 0) /* These macros are used to output debugging information. I use several different macros depending on the number of arguments because no macro can take a variable number of arguments and I don't want to use double parentheses. */ #define DEBUG_MESSAGE0(i, z) \ do { if (FDEBUGGING (i)) ulog (LOG_DEBUG, (z)); } while (0) #define DEBUG_MESSAGE1(i, z, a1) \ do { if (FDEBUGGING (i)) ulog (LOG_DEBUG, (z), (a1)); } while (0) #define DEBUG_MESSAGE2(i, z, a1, a2) \ do { if (FDEBUGGING (i)) ulog (LOG_DEBUG, (z), (a1), (a2)); } while (0) #define DEBUG_MESSAGE3(i, z, a1, a2, a3) \ do \ { \ if (FDEBUGGING (i)) \ ulog (LOG_DEBUG, (z), (a1), (a2), (a3)); \ } \ while (0) #define DEBUG_MESSAGE4(i, z, a1, a2, a3, a4) \ do \ { \ if (FDEBUGGING (i)) \ ulog (LOG_DEBUG, (z), (a1), (a2), (a3), (a4)); \ } \ while (0) #else /* DEBUG <= 1 */ /* If debugging information is not being compiled, provide versions of the debugging macros which just disappear. */ #define DEBUG_MESSAGE0(i, z) #define DEBUG_MESSAGE1(i, z, a1) #define DEBUG_MESSAGE2(i, z, a1, a2) #define DEBUG_MESSAGE3(i, z, a1, a2, a3) #define DEBUG_MESSAGE4(i, z, a1, a2, a3, a4) #endif /* DEBUG <= 1 */ /* Functions. */ /* Given an unknown system name, return information for an unknown system. If unknown systems are not permitted, this returns FALSE. Otherwise, it translates the name as necessary for the spool directory, and fills in *qsys. */ extern boolean funknown_system P((pointer puuconf, const char *zsystem, struct uuconf_system *qsys)); /* See whether a file belongs in the spool directory. */ extern boolean fspool_file P((const char *zfile)); /* See if the current time matches a time span. If not, return FALSE. Otherwise, return TRUE and set *pival and *pcretry to the values from the matching element of the span. */ extern boolean ftimespan_match P((const struct uuconf_timespan *qspan, long *pival, int *pcretry)); /* Remove all occurrences of the local system name followed by an exclamation point from the start of the argument. Return the possibly shortened argument. */ extern char *zremove_local_sys P((struct uuconf_system *qlocalsys, char *z)); /* Determine the maximum size that may ever be transferred, given a timesize span. If there are any time gaps larger than 1 hour not described by the timesize span, this returns -1. Otherwise it returns the largest size that may be transferred at some time. */ extern long cmax_size_ever P((const struct uuconf_timespan *qtimesize)); /* Send mail about a file transfer. */ extern boolean fmail_transfer P((boolean fok, const char *zuser, const char *zmail, const char *zwhy, const char *zfrom, const char *zfromsys, const char *zto, const char *ztosys, const char *zsaved)); /* See whether a file is in one of a list of directories. The zpubdir argument is used to pass the directory names to zsysdep_local_file. If fcheck is FALSE, this does not check accessibility. Otherwise, if freadable is TRUE, the user zuser must have read access to the file and all appropriate directories; if freadable is FALSE zuser must have write access to the appropriate directories. The zuser argument may be NULL, in which case all users must have the appropriate access (this is used for a remote request). */ extern boolean fin_directory_list P((const char *zfile, char **pzdirs, const char *zpubdir, boolean fcheck, boolean freadable, const char *zuser)); /* Parse a command string. */ extern boolean fparse_cmd P((char *zcmd, struct scmd *qcmd)); /* Return whether a command needs quoting. */ extern boolean fcmd_needs_quotes P((const struct scmd *qcmd)); /* Quote the strings in a command, creating a new command. */ extern void uquote_cmd P((const struct scmd *qorig, struct scmd *qnew)); /* Free a command structure created by uquote_cmd. */ extern void ufree_quoted_cmd P((struct scmd *qcmd)); /* Backslash qoute a string, returning a newly allocated string. If fbackslashonly, only quote backslashes. Otherwise, quote backslashes and all nonprinting characters. */ extern char *zquote_cmd_string P((const char *zorig, boolean fbackslashonly)); /* Make a log entry. */ #ifdef __GNUC__ #define GNUC_VERSION __GNUC__ #else #define GNUC_VERSION 0 #endif #if ANSI_C && HAVE_VFPRINTF extern void ulog P((enum tlog ttype, const char *zfmt, ...)) ATTRIBUTE_PRINTF_2; #else extern void ulog (); #endif #undef GNUC_VERSION /* Report an error returned by one of the uuconf routines. */ extern void ulog_uuconf P((enum tlog ttype, pointer puuconf, int iuuconf)); /* Set the function to call if a fatal error occurs. */ extern void ulog_fatal_fn P((void (*pfn) P((void)))); /* If ffile is TRUE, send log entries to the log file rather than to stderr. */ extern void ulog_to_file P((pointer puuconf, boolean ffile)); /* Set the ID number used by the logging functions. */ extern void ulog_id P((int iid)); /* Set the system name used by the logging functions. */ extern void ulog_system P((const char *zsystem)); /* Set the system and user name used by the logging functions. */ extern void ulog_user P((const char *zuser)); /* Set the device name used by the logging functions. */ extern void ulog_device P((const char *zdevice)); /* Close the log file. */ extern void ulog_close P((void)); /* Make an entry in the statistics file. */ extern void ustats P((boolean fsucceeded, const char *zuser, const char *zsystem, boolean fsent, long cbytes, long csecs, long cmicros, boolean fcaller)); /* Close the statistics file. */ extern void ustats_close P((void)); #if DEBUG > 1 /* A debugging routine to output a buffer. This outputs zhdr, the buffer length clen, and the contents of the buffer in quotation marks. */ extern void udebug_buffer P((const char *zhdr, const char *zbuf, size_t clen)); /* A debugging routine to make a readable version of a character. This takes a buffer at least 5 bytes long, and returns the length of the string it put into it (not counting the null byte). */ extern size_t cdebug_char P((char *z, int ichar)); /* Parse a debugging option string. This can either be a number or a comma separated list of debugging names. This returns a value for iDebug. */ extern int idebug_parse P((const char *)); #endif /* DEBUG <= 1 */ /* Copy one file to another. */ extern boolean fcopy_file P((const char *zfrom, const char *zto, boolean fpublic, boolean fmkdirs, boolean fsignals)); /* Copy an open file to another. */ extern boolean fcopy_open_file P((openfile_t efrom, const char *zto, boolean fpublic, boolean fmkdirs, boolean fsignals)); /* Translate escape sequences in a buffer, leaving the result in the same buffer and returning the length. */ extern size_t cescape P((char *zbuf)); /* Get a buffer to hold a string of a given size. The buffer should be freed with ubuffree. */ extern char *zbufalc P((size_t csize)); /* Call zbufalc to allocate a buffer and copy a string into it. */ extern char *zbufcpy P((const char *z)); /* Free up a buffer returned by zbufalc or zbufcpy. */ extern void ubuffree P((char *z)); /* Allocate memory without fail. */ extern pointer xmalloc P((size_t)); /* Realloc memory without fail. */ extern pointer xrealloc P((pointer, size_t)); /* Free memory (accepts NULL pointers, which some libraries erroneously do not). */ extern void xfree P((pointer)); /* Global variables. */ /* The name of the program being run. Set from argv[0]. */ extern const char *zProgram; /* When a signal occurs, the signal handlers sets the appropriate element of the arrays afSignal and afLog_signal to TRUE. The afSignal array is used to check whether a signal occurred. The afLog_signal array tells ulog to log the signal; ulog will clear the element after logging it, which means that if a signal comes in at just the right moment it will not be logged. It will always be recorded in afSignal, though. At the moment we handle 5 signals: SIGHUP, SIGINT, SIGQUIT, SIGTERM and SIGPIPE (the Unix code also handles SIGALRM). If we want to handle more, the afSignal array must be extended; I see little point to handling any of the other ANSI C or POSIX signals, as they are either unlikely to occur (SIGABRT, SIGUSR1) or nearly impossible to handle cleanly (SIGILL, SIGSEGV). SIGHUP is only logged if fLog_sighup is TRUE. */ #define INDEXSIG_SIGHUP (0) #define INDEXSIG_SIGINT (1) #define INDEXSIG_SIGQUIT (2) #define INDEXSIG_SIGTERM (3) #define INDEXSIG_SIGPIPE (4) #define INDEXSIG_COUNT (5) extern volatile sig_atomic_t afSignal[INDEXSIG_COUNT]; extern volatile sig_atomic_t afLog_signal[INDEXSIG_COUNT]; extern boolean fLog_sighup; /* The names of the signals to use in error messages, as an initializer for an array. */ #define INDEXSIG_NAMES \ { "hangup", "interrupt", "quit", "termination", "SIGPIPE" } /* Check to see whether we've received a signal. It would be nice if we could use a single variable for this, but we sometimes want to clear our knowledge of a signal and that would cause race conditions (clearing a single element of the array is not a race assuming that we don't care about a particular signal, even if it occurs after we've examined the array). */ #define FGOT_SIGNAL() \ (afSignal[INDEXSIG_SIGHUP] || afSignal[INDEXSIG_SIGINT] \ || afSignal[INDEXSIG_SIGQUIT] || afSignal[INDEXSIG_SIGTERM] \ || afSignal[INDEXSIG_SIGPIPE]) /* If we get a SIGINT in uucico, we continue the current communication session but don't start any new ones. This macros checks for any signal other than SIGINT, which means we should get out immediately. */ #define FGOT_QUIT_SIGNAL() \ (afSignal[INDEXSIG_SIGHUP] || afSignal[INDEXSIG_SIGQUIT] \ || afSignal[INDEXSIG_SIGTERM] || afSignal[INDEXSIG_SIGPIPE]) /* Device name to log. This is set by fconn_open. It may be NULL. */ extern char *zLdevice; /* If not NULL, ulog calls this function before outputting anything. This is used to support cu. */ extern void (*pfLstart) P((void)); /* If not NULL, ulog calls this function after outputting everything. This is used to support cu. */ extern void (*pfLend) P((void)); uucp-1.07/uuconf.h0000664000076400007640000020564107665321757007612 /* uuconf.h Header file for UUCP configuration routines. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The use of an object file which uses material from this header file, and from no other portion of the uuconf library, is unrestricted, as described in paragraph 4 of section 5 of version 2 of the GNU Library General Public License (this sentence is merely informative, and does not modify the License in any way). The author of the program may be contacted at ian@airs.com. */ #ifndef UUCONF_H #define UUCONF_H #include /* The macro UUCONF_ANSI_C may be used to override __STDC__. */ #ifndef UUCONF_ANSI_C #ifdef __STDC__ #define UUCONF_ANSI_C 1 #else /* ! defined (__STDC__) */ #define UUCONF_ANSI_C 0 #endif /* ! defined (__STDC__) */ #endif /* ! defined (UUCONF_ANSI_C) */ #if UUCONF_ANSI_C #define UUCONF_CONST const typedef void *UUCONF_POINTER; #include typedef size_t UUCONF_SIZE_T; #else #define UUCONF_CONST typedef char *UUCONF_POINTER; typedef unsigned int UUCONF_SIZE_T; #endif /* The field names of each of the following structures begin with "uuconf_". This is to avoid any conflicts with user defined macros. The first character following the "uuconf_" string indicates the type of the field. z -- a string (char *) c -- a count (normally int) i -- an integer value (normally int) f -- a boolean value (normally int) b -- a single character value (char or int) t -- an enum (enum XX) s -- a structure (struct XX) u -- a union (union XX) q -- a pointer to a structure (struct XX *) p -- a pointer to something other than a string */ /* The information which is kept for a chat script. */ struct uuconf_chat { /* The script itself. This is a NULL terminated list of expect/send pairs. The first string is an expect string. A string starting with a '-' indicates subsend string; the following strings which start with '-' are subexpect/subsend strings. This field may be NULL, in which case there is no chat script (but pzprogram may hold a program to run). */ char **uuconf_pzchat; /* The chat program to run. This is a NULL terminated list of arguments; element 0 is the program. May be NULL, in which case there is no program. */ char **uuconf_pzprogram; /* The timeout in seconds to use for expect strings in the chat script. */ int uuconf_ctimeout; /* The NULL terminated list of failure strings. If any of these strings appear, the chat script is aborted. May be NULL, in which case there are no failure strings. */ char **uuconf_pzfail; /* Non-zero if incoming characters should be stripped to seven bits (by anding with 0x7f). */ int uuconf_fstrip; }; /* The information which is kept for a time specification. This is a linked list of structures. Each element of the list represents a span of time, giving a starting time and an ending time. The time only depends on the day of the week, not on the day of the month or of the year. The time is only specified down to the minute, not down to the second or below. The list is sorted by starting time. The starting and ending time are expressed in minutes since the beginning of the week, which is considered to be 12 midnight on Sunday. Thus 60 is 1 am on Sunday, 1440 (== 60 * 24) is 12 midnight on Monday, and the largest possible value is 10080 (== 60 * 24 * 7) which is 12 midnight on the following Sunday. Each span of time has a value associated with it. This is the lowest grade or the largest file size that may be transferred during that time, depending on the source of the time span. When time specifications overlap, the value used for the overlap is the higher grade or the smaller file size. Thus specifying ``call-timegrade z Any'' and ``call-timegrade Z Mo'' means that only grade Z or higher may be sent on Monday, since Z is the higer grade of the overlapping spans. The final array wil have no overlaps. Each span also has a retry time associated with it. This permits different retry times to be used at different times of day. The retry time is only relevant if the span came from a ``time'' or ``timegrade'' command for a system. */ struct uuconf_timespan { /* Next element in list. */ struct uuconf_timespan *uuconf_qnext; /* Starting minute (-1 at the end of the array). */ int uuconf_istart; /* Ending minute. */ int uuconf_iend; /* Value for this span (lowest grade or largest file that may be transferred at this time). */ long uuconf_ival; /* Retry time. */ int uuconf_cretry; }; /* The information which is kept for protocol parameters. Protocol parameter information is stored as an array of the following structures. */ struct uuconf_proto_param { /* The name of the protocol to which this entry applies. This is '\0' for the last element of the array. */ int uuconf_bproto; /* Specific entries for this protocol. This points to an array ending in an element with a uuconf_cargs field of 0. */ struct uuconf_proto_param_entry *uuconf_qentries; }; /* Each particular protocol parameter entry is one of the following structures. */ struct uuconf_proto_param_entry { /* The number of arguments to the ``protocol-parameter'' command (not counting ``protocol-parameter'' itself). This is 0 for the last element of the array. */ int uuconf_cargs; /* The actual arguments to the ``protocol-parameter'' command; this is an array with cargs entries. */ char **uuconf_pzargs; }; /* The information which is kept for a system. The zname and zalias fields will be the same for all alternates. Every other fields is specific to the particular alternate in which it appears (although most will be the same for all alternates). */ struct uuconf_system { /* The name of the system. */ char *uuconf_zname; /* A list of aliases for the system. This is a NULL terminated list of strings. May be NULL, in which case there are no aliases. */ char **uuconf_pzalias; /* A linked list of alternate call in or call out information. Each alternative way to call this system occupies an element of this list. May be NULL, in which case there are no alternates. */ struct uuconf_system *uuconf_qalternate; /* The name for this particular alternate. May be NULL, in which case this alternate does not have a name. */ char *uuconf_zalternate; /* If non-zero, this alternate may be used for calling out. */ int uuconf_fcall; /* If non-zero, this alternate may be used for accepting a call. */ int uuconf_fcalled; /* The times at which this system may be called. The ival field of each uuconf_timespan structure is the lowest grade which may be transferred at that time. The cretry field is the number of minutes to wait before retrying the call, or 0 if it was not specified. May be NULL, in which case the system may never be called. */ struct uuconf_timespan *uuconf_qtimegrade; /* The times at which to request a particular grade of the system when calling it, and the grades to request. The ival field of each uuconf_timespan structure is the lowest grade which the other system should transfer at that time. May be NULL, in which case there are no grade restrictions. */ struct uuconf_timespan *uuconf_qcalltimegrade; /* The times at which to allow a particular grade of work to be transferred to the system, when it calls in. The ival field of each uuconf_timespan structure is the lowest grade which should be transferred at that time. May be NULL, in which case there are no grade restrictions. */ struct uuconf_timespan *uuconf_qcalledtimegrade; /* The maximum number of times to retry calling this system. If this is 0, there is no limit. */ int uuconf_cmax_retries; /* The number of minutes to wait between successful calls to a system. */ int uuconf_csuccess_wait; /* The size restrictions by time for local requests during a locally placed call. The ival field of each uuconf_timespan structure is the size in bytes of the largest file which may be transferred at that time. May be NULL, in which case there are no size restrictions. */ struct uuconf_timespan *uuconf_qcall_local_size; /* The size restrictions by time for remote requests during a locally placed call. May be NULL. */ struct uuconf_timespan *uuconf_qcall_remote_size; /* The size restrictions by time for local requests during a remotely placed call. May be NULL. */ struct uuconf_timespan *uuconf_qcalled_local_size; /* The size restrictions by time for remote requests during a remotely placed call. May be NULL. */ struct uuconf_timespan *uuconf_qcalled_remote_size; /* Baud rate, or speed. Zero means any baud rate. If ihighbaud is non-zero, this is the low baud rate of a range. */ long uuconf_ibaud; /* If non-zero, ibaud is the low baud rate of a range and ihighbaud is the high baud rate. */ long uuconf_ihighbaud; /* Port name to use. May be NULL. If an HDB configuration file contains a modem class (alphabetic characters preceeding the baud rate), the class is appended to the port name. */ char *uuconf_zport; /* Specific port information, if the system entry includes port information. May be NULL. */ struct uuconf_port *uuconf_qport; /* Phone number to call, or address to use for a TCP connection. May be NULL, in which case a dialer script may not use \D or \T for this system, and a TCP port will use the system name. */ char *uuconf_zphone; /* Chat script to use when logging in to the system. */ struct uuconf_chat uuconf_schat; /* Login name to use for \L in the login chat script. This should normally be accessed via uuconf_callout. If it is "*", uuconf_callout will look it up in the call out file. This may be NULL, in which case the login script may not use \L. */ char *uuconf_zcall_login; /* Password to use for \P in the login chat script. This should normally be accessed via uuconf_callout. If it is "*", uuconf_callout will look it up in the call out file. This may be NULL, in which case the login script may not use \P. */ char *uuconf_zcall_password; /* The login name this system must use when calling in. This may be different for different alternates. This should only be examined if uuconf_fcalled is TRUE. If this is NULL or "ANY" then uuconf_validate must be called to make sure that whatever login name was used is permitted for this machine. */ char *uuconf_zcalled_login; /* If non-zero, then when this system calls in the call should not be allowed to proceed and the system should be called back. */ int uuconf_fcallback; /* If non-zero, then conversation sequence numbers should be used with this system. */ int uuconf_fsequence; /* A list of protocols to use with this system. Each protocol has a single character name. May be NULL, in which case any known protocol may be used. */ char *uuconf_zprotocols; /* Array of protocol parameters. Ends in an entry with a uuconf_bproto field of '\0'. May be NULL. */ struct uuconf_proto_param *uuconf_qproto_params; /* Chat script to run when called by this system. */ struct uuconf_chat uuconf_scalled_chat; /* Debugging level to set during a conversation. May be NULL. */ char *uuconf_zdebug; /* Maximum remote debugging level this system may request. May be NULL. */ char *uuconf_zmax_remote_debug; /* Non-zero if the remote system may request us to send files from the local system to the remote. */ int uuconf_fsend_request; /* Non-zero if the remote system may request us to receive files from the remote system to the local. */ int uuconf_frec_request; /* Non-zero if local requests are permitted when calling this system. */ int uuconf_fcall_transfer; /* Non-zero if local requests are permitted when this system calls in. */ int uuconf_fcalled_transfer; /* NULL terminated list of directories from which files may be sent by local request. */ char **uuconf_pzlocal_send; /* NULL terminated list of directories from which files may be sent by remote request. */ char **uuconf_pzremote_send; /* NULL terminated list of directories into which files may be received by local request. */ char **uuconf_pzlocal_receive; /* NULL terminated list of directories into which files may be received by remote request. */ char **uuconf_pzremote_receive; /* Path to use for command execution. This is a NULL terminated list of directories. */ char **uuconf_pzpath; /* NULL terminated List of commands that may be executed. */ char **uuconf_pzcmds; /* Amount of free space to leave when accepting a file from this system, in bytes. */ long uuconf_cfree_space; /* NULL terminated list of systems that this system may forward from. May be NULL if there are no systems from which files may be forwarded. The list may include "ANY". */ char **uuconf_pzforward_from; /* NULL terminated list of systems that this system may forward to. May be NULL if there are no systems to which files may be forwarded. The list may include "ANY". */ char **uuconf_pzforward_to; /* The public directory to use for this sytem. */ const char *uuconf_zpubdir; /* The local name to use for this remote system. May be NULL if the usual local name should be used. */ char *uuconf_zlocalname; /* The maximum number of seconds to spend sending one file when there are other files to send when using a protocol which permits interrupting a file send. This is zero if there is no limit. */ long uuconf_cmax_file_time; /* Memory allocation block for the system. */ UUCONF_POINTER uuconf_palloc; }; /* Types of ports. */ enum uuconf_porttype { /* Unknown port type. A port of this type should never be returned by the uuconf functions. */ UUCONF_PORTTYPE_UNKNOWN, /* Read from standard input and write to standard output. Not normally used. */ UUCONF_PORTTYPE_STDIN, /* A modem port. */ UUCONF_PORTTYPE_MODEM, /* A direct connect port. */ UUCONF_PORTTYPE_DIRECT, /* A TCP port. Not supported on all systems. */ UUCONF_PORTTYPE_TCP, /* A TLI port. Not supported on all systems. */ UUCONF_PORTTYPE_TLI, /* A pipe port. Not supported on all systems. */ UUCONF_PORTTYPE_PIPE }; /* Additional information for a stdin port (there is none). */ struct uuconf_stdin_port { int uuconf_idummy; }; /* Additional information for a modem port. */ struct uuconf_modem_port { /* The device name. May be NULL, in which case the port name is used instead. */ char *uuconf_zdevice; /* The device name to send the dialer chat script to. May be NULL, in which case the chat script is sent to the usual device. */ char *uuconf_zdial_device; /* The default baud rate (speed). If zero, there is no default. */ long uuconf_ibaud; /* The low baud rate, if a range is used. If zero, a range is not used and ihighbaud should be ignored. */ long uuconf_ilowbaud; /* The high baud rate, if ilowbaud is non-zero. */ long uuconf_ihighbaud; /* Non-zero if the port supports carrier detect. */ int uuconf_fcarrier; /* Non-zero if the port supports hardware flow control. */ int uuconf_fhardflow; /* A NULL terminated sequence of dialer/token pairs (element 0 is a dialer name, element 1 is a token, etc.) May be NULL, in which case qdialer should not be NULL. */ char **uuconf_pzdialer; /* Specific dialer information. Only used if pzdialer is NULL. */ struct uuconf_dialer *uuconf_qdialer; }; /* Additional information for a direct connect port. */ struct uuconf_direct_port { /* The device name. May be NULL, in which case the port name is used instead. */ char *uuconf_zdevice; /* The baud rate (speed). */ long uuconf_ibaud; /* Non-zero if the port uses carrier detect. */ int uuconf_fcarrier; /* Non-zero if the port supports hardware flow control. */ int uuconf_fhardflow; }; /* Additional information for a TCP port. */ struct uuconf_tcp_port { /* The TCP port number to use. May be a name or a number. May be NULL, in which case "uucp" is looked up using getservbyname. */ char *uuconf_zport; /* The IP version number to use. This is 0 for any, 4 for IPv4, 6 for IPv6. */ int uuconf_iversion; /* A NULL terminated sequence of dialer/token pairs (element 0 is a dialer name, element 1 is a token, etc.) May be NULL. */ char **uuconf_pzdialer; }; /* Additional information for a TLI port. */ struct uuconf_tli_port { /* Device name to open. May be NULL, in which case the port name is used. */ char *uuconf_zdevice; /* Whether this port should be turned into a stream, permitting the read and write calls instead of the t_rcv and t_send calls. */ int uuconf_fstream; /* A NULL terminated list of modules to push after making the connection. May be NULL, in which case if fstream is non-zero, then "tirdwr" is pushed onto the stream, and otherwise nothing is pushed. */ char **uuconf_pzpush; /* A NULL terminated sequence of dialer/token pairs (element 0 is a dialer name, element 1 is a token, etc.) May be NULL. If element 0 is TLI or TLIS, element 1 is used as the address to connect to; otherwise uuconf_zphone from the system information is used. */ char **uuconf_pzdialer; /* Address to use when operating as a server. This may contain escape sequences. */ char *uuconf_zservaddr; }; /* Additional information for a pipe port. */ struct uuconf_pipe_port { /* The command and its arguments. */ char **uuconf_pzcmd; }; /* Information kept for a port. */ struct uuconf_port { /* The name of the port. */ char *uuconf_zname; /* The type of the port. */ enum uuconf_porttype uuconf_ttype; /* The list of protocols supported by the port. The name of each protocol is a single character. May be NULL, in which case any protocol is permitted. */ char *uuconf_zprotocols; /* Array of protocol parameters. Ends in an entry with a uuconf_bproto field of '\0'. May be NULL. */ struct uuconf_proto_param *uuconf_qproto_params; /* The set of reliability bits. */ int uuconf_ireliable; /* The lock file name to use. */ char *uuconf_zlockname; /* Memory allocation block for the port. */ UUCONF_POINTER uuconf_palloc; /* The type specific information. */ union { struct uuconf_stdin_port uuconf_sstdin; struct uuconf_modem_port uuconf_smodem; struct uuconf_direct_port uuconf_sdirect; struct uuconf_tcp_port uuconf_stcp; struct uuconf_tli_port uuconf_stli; struct uuconf_pipe_port uuconf_spipe; } uuconf_u; }; /* Information kept about a dialer. */ struct uuconf_dialer { /* The name of the dialer. */ char *uuconf_zname; /* The chat script to use when dialing out. */ struct uuconf_chat uuconf_schat; /* The string to send when a `=' appears in the phone number. */ char *uuconf_zdialtone; /* The string to send when a `-' appears in the phone number. */ char *uuconf_zpause; /* Non-zero if the dialer supports carrier detect. */ int uuconf_fcarrier; /* The number of seconds to wait for carrier after the chat script is complete. Only used if fcarrier is non-zero. Only supported on some systems. */ int uuconf_ccarrier_wait; /* If non-zero, DTR should be toggled before dialing. Only supported on some systems. */ int uuconf_fdtr_toggle; /* If non-zero, sleep for 1 second after toggling DTR. Ignored if fdtr_toggle is zero. */ int uuconf_fdtr_toggle_wait; /* The chat script to use when a call is complete. */ struct uuconf_chat uuconf_scomplete; /* The chat script to use when a call is aborted. */ struct uuconf_chat uuconf_sabort; /* Array of protocol parameters. Ends in an entry with a uuconf_bproto field of '\0'. May be NULL. */ struct uuconf_proto_param *uuconf_qproto_params; /* The set of reliability bits. */ int uuconf_ireliable; /* Memory allocation block for the dialer. */ UUCONF_POINTER uuconf_palloc; }; /* Information returned by uuconf_config_files. Any field in this struct may be NULL, indicating that the corresponding files will not be read. */ struct uuconf_config_file_names { /* Taylor UUCP config file name. */ UUCONF_CONST char *uuconf_ztaylor_config; /* Taylor UUCP sys file names; NULL terminated. */ UUCONF_CONST char * UUCONF_CONST *uuconf_pztaylor_sys; /* Taylor UUCP port file names; NULL terminated. */ UUCONF_CONST char * UUCONF_CONST *uuconf_pztaylor_port; /* Taylor UUCP dial file names; NULL terminated. */ UUCONF_CONST char * UUCONF_CONST *uuconf_pztaylor_dial; /* UUCP dialcode file names; NULL terminated. */ UUCONF_CONST char * UUCONF_CONST *uuconf_pzdialcode; /* Taylor UUCP passwd file names; NULL terminated. */ UUCONF_CONST char * UUCONF_CONST *uuconf_pztaylor_pwd; /* Taylor UUCP call file names; NULL terminated. */ UUCONF_CONST char * UUCONF_CONST *uuconf_pztaylor_call; /* V2 system file name. */ UUCONF_CONST char *uuconf_zv2_systems; /* V2 device file name. */ UUCONF_CONST char *uuconf_zv2_device; /* V2 user permissions file name. */ UUCONF_CONST char *uuconf_zv2_userfile; /* V2 user permitted commands file name. */ UUCONF_CONST char *uuconf_zv2_cmds; /* HDB system file names; NULL terminated. */ UUCONF_CONST char * UUCONF_CONST *uuconf_pzhdb_systems; /* HDB device file names; NULL terminated. */ UUCONF_CONST char * UUCONF_CONST *uuconf_pzhdb_devices; /* HDB dialer file names; NULL terminated. */ UUCONF_CONST char * UUCONF_CONST *uuconf_pzhdb_dialers; /* HDB permissions file name. */ UUCONF_CONST char *uuconf_zhdb_permissions; }; /* Reliability bits for the ireliable field of ports and dialers. These bits are used to decide which protocol to run. A given protocol will have a set of these bits, and each of them must be turned on for the port before we will permit that protocol to be used. This will be overridden by the zprotocols field. */ /* Whether a set of reliability bits is given. If this bit is not set, then there is no reliability information. */ #define UUCONF_RELIABLE_SPECIFIED (01) /* Set if the connection is eight bit transparent. */ #define UUCONF_RELIABLE_EIGHT (02) /* Set if the connection is error-free. */ #define UUCONF_RELIABLE_RELIABLE (04) /* Set if the connection is end-to-end reliable (e.g. TCP). */ #define UUCONF_RELIABLE_ENDTOEND (010) /* Set if the connection is full-duplex; that is, no time consuming line turnaround is required before sending data in the reverse direction. If the connection is truly half-duplex, in the sense that communication can only flow in one direction, UUCP can not be used. */ #define UUCONF_RELIABLE_FULLDUPLEX (020) /* UUCP grades range from 0 to 9, A to Z, a to z in order from highest to lowest (work of higher grades is done before work of lower grades). */ /* The highest grade. */ #define UUCONF_GRADE_HIGH ('0') /* The lowest grade. */ #define UUCONF_GRADE_LOW ('z') /* Whether a character is a legal grade (requires ). */ #define UUCONF_GRADE_LEGAL(b) (isalnum (BUCHAR (b))) /* Return < 0 if the first grade should be done before the second grade, == 0 if they are the same, or > 0 if the first grade should be done after the second grade. On an ASCII system, this can just be b1 - b2. */ #define UUCONF_GRADE_CMP(b1, b2) (uuconf_grade_cmp ((b1), (b2))) /* Definitions for bits returned by uuconf_strip. */ #define UUCONF_STRIP_LOGIN (01) #define UUCONF_STRIP_PROTO (02) /* uuconf_runuuxqt returns either a positive number (the number of execution files to receive between uuxqt invocations) or one of these constant values. */ #define UUCONF_RUNUUXQT_NEVER (0) #define UUCONF_RUNUUXQT_ONCE (-1) #define UUCONF_RUNUUXQT_PERCALL (-2) /* Most of the uuconf functions returns an error code. A value of zero (UUCONF_SUCCESS) indicates success. */ /* If this bit is set in the returned error code, then the uuconf_errno function may be used to obtain the errno value as set by the function which caused the failure. */ #define UUCONF_ERROR_ERRNO (0x100) /* If this bit is set in the returned error code, then the uuconf_filename function may be used to get the name of a file associated with the error. */ #define UUCONF_ERROR_FILENAME (0x200) /* If this bit is set in the returned error code, then the uuconf_lineno function may be used to get a line number associated with the error; normally if this is set UUCONF_ERROR_FILENAME will also be set. */ #define UUCONF_ERROR_LINENO (0x400) /* There are two UUCONF_CMDTABRET bits that may be set in the return value of uuconf_cmd_line or uuconf_cmd_args, described below. They do not indicate an error, but instead give instructions to the calling function, often uuconf_cmd_file. They may also be set in the return value of a user function listed in a uuconf_cmdtab table, in which case they will be honored by uuconf_cmd_file. */ /* This bit means that the memory occupied by the arguments passed to the function should be preserved, and not overwritten or freed. It refers only to the contents of the arguments; the contents of the argv array itself may always be destroyed. If this bit is set in the return value of uuconf_cmd_line or uuconf_cmd_args, it must be honored. It will be honored by uuconf_cmd_file. This may be combined with an error code or with UUCONF_CMDTABRET_EXIT, although neither uuconf_cmd_file or uuconf_cmd_line will do so. */ #define UUCONF_CMDTABRET_KEEP (0x800) /* This bit means that uuconf_cmd_file should exit, rather than go on to read and process the next line. If uuconf_cmd_line or uuconf_cmd_args encounter an error, the return value will have this bit set along with the error code. A user function may set this bit with or without an error; the return value of the user function will be returned by uuconf_cmd_file, except that the UUCONF_CMDTABRET_KEEP and UUCONF_CMDTABRET_EXIT bits will be cleared. */ #define UUCONF_CMDTABRET_EXIT (0x1000) /* This macro may be used to extract the specific error value. */ #define UUCONF_ERROR_VALUE(i) ((i) & 0xff) /* UUCONF_ERROR_VALUE will return one of the following values. */ /* Function succeeded. */ #define UUCONF_SUCCESS (0) /* Named item not found. */ #define UUCONF_NOT_FOUND (1) /* A call to fopen failed. */ #define UUCONF_FOPEN_FAILED (2) /* A call to fseek failed. */ #define UUCONF_FSEEK_FAILED (3) /* A call to malloc or realloc failed. */ #define UUCONF_MALLOC_FAILED (4) /* Syntax error in file. */ #define UUCONF_SYNTAX_ERROR (5) /* Unknown command. */ #define UUCONF_UNKNOWN_COMMAND (6) #if UUCONF_ANSI_C /* For each type of configuration file (Taylor, V2, HDB), there are separate routines to read various sorts of information. There are also generic routines, which call on the appropriate type specific routines. The library can be compiled to read any desired combination of the configuration file types. This affects only the generic routines, as it determines which type specific routines they call. Thus, on a system which, for example, does not have any V2 configuration files, there is no need to include the overhead of the code to parse the files and the time to look for them. However, a program which specifically wants to be able to parse them can call the V2 specific routines. The uuconf functions all take as an argument a pointer to uuconf global information. This must be initialized by any the initialization routines (the generic one and the three file type specific ones) before any of the other uuconf functions may be called. */ /* Initialize the configuration file reading routines. The ppglobal argument should point to a generic pointer (a void *, or, on older compilers, a char *) which will be initialized and may then be passed to the other uuconf routines. The zprogram argument is the name of the program for which files should be read. A NULL is taken as "uucp", and reads the standard UUCP configuration files. The only other common argument is "cu", but any string is permitted. The zname argument is the name of the Taylor UUCP config file; if it is NULL, the default config file will be read. If not reading Taylor UUCP configuration information, the argument is ignored. This function must be called before any of the other uuconf functions. Note that if the zname argument is obtained from the user running the program, the program should be careful to revoke any special privileges it may have (e.g. on Unix call setuid (getuid ()) and setgid (getgid ())). Otherwise various sorts of spoofing become possible. */ extern int uuconf_init (void **uuconf_ppglobal, const char *uuconf_zprogram, const char *uuconf_zname); /* Adjust the configuration file global pointer for a new thread. The library is fully reentrant (with the exception of the function uuconf_error_string, which calls strerror, which on some systems is not reentrant), provided that each new thread that wishes to call the library calls this function and uses the new global pointer value. The ppglobal argument should be set to the address of the global pointer set by any of the init functions; it will be modified to become a new global pointer. */ extern int uuconf_init_thread (void **uuconf_ppglobal); /* Get the names of all known systems. This sets sets *ppzsystems to point to an array of system names. The list of names is NULL terminated. The array is allocated using malloc, as is each element of the array, and they may all be passed to free when they are no longer needed. If the falias argument is 0, the list will not include any aliases; otherwise, it will. */ extern int uuconf_system_names (void *uuconf_pglobal, char ***uuconf_ppzsystems, int uuconf_falias); /* Get the information for the system zsystem. This sets the fields in *qsys. This will work whether zsystem is the official name of the system or merely an alias. */ extern int uuconf_system_info (void *uuconf_pglobal, const char *uuconf_zsystem, struct uuconf_system *uuconf_qsys); /* Get information for an unknown (anonymous) system. The uuconf_zname field of the returned system information will be NULL. If no information is available for unknown systems, this will return UUCONF_NOT_FOUND. This does not run the HDB remote.unknown shell script. */ extern int uuconf_system_unknown (void *uuconf_pglobal, struct uuconf_system *uuconf_qsys); /* Get information for the local system. Normally the local system name should first be looked up using uuconf_system_info. If that returns UUCONF_NOT_FOUND, this function may be used to get an appropriate set of defaults. The uuconf_zname field of the returned system information may be NULL. */ extern int uuconf_system_local (void *uuconf_pglobal, struct uuconf_system *uuconf_qsys); /* Free the memory occupied by system information returned by uuconf_system_info, uuconf_system_unknown, uuconf_system_local, or any of the configuration file type specific routines described below. After this is called, the contents of the structure shall not be referred to. */ extern int uuconf_system_free (void *uuconf_pglobal, struct uuconf_system *uuconf_qsys); #ifdef __OPTIMIZE__ #define uuconf_system_free(qglob, q) \ (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) #endif /* Find a matching port. This will consider each port in turn. If the zname argument is not NULL, the port's uuconf_zname field must match it. If the ibaud argument is not zero and the ihighbaud argument is zero, the port's baud rate, if defined, must be the same (if the port has a range of baud rates, ibaud must be within the range). If ibaud and ihighbaud are both not zero, the port's baud rate, if defined, must be between ibaud and ihighbaud inclusive (if the port has a range of baud rates, the ranges must intersect). If the port has no baud rate, either because it is a type of port for which baud rate is not defined (e.g. a TCP port) or because the uuconf_ibaud field is 0, the ibaud and ihighbaud arguments are ignored. If the pifn argument is not NULL, the port is passed to pifn, along with the pinfo argument (which is otherwise ignored). If pifn returns UUCONF_SUCCESS, the port matches. If pifn returns UUCONF_NOT_FOUND, a new port is sought. Otherwise the return value of pifn is returned from uuconf_find_port. The pifn function may be used to further restrict the port, such as by modem class or device name. It may also be used to lock the port, if appropriate; in this case, if the lock fails, pifn may return UUCONF_NOT_FOUND to force uuconf_find_port to continue searching for a port. If the port matches, the information is set into uuconf_qport, and uuconf_find_port returns UUCONF_SUCCESS. */ extern int uuconf_find_port (void *uuconf_pglobal, const char *uuconf_zname, long uuconf_ibaud, long uuconf_ihighbaud, int (*uuconf_pifn) (struct uuconf_port *, void *uuconf_pinfo), void *uuconf_pinfo, struct uuconf_port *uuconf_qport); /* Free the memory occupied by system information returned by uuconf_find_port (or any of the configuration file specific routines described below). After this is called, the contents of the structure shall not be referred to. */ extern int uuconf_port_free (void *uuconf_pglobal, struct uuconf_port *uuconf_qport); #ifdef __OPTIMIZE__ #define uuconf_port_free(qglob, q) \ (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) #endif /* Get the names of all known dialers. This sets sets *ppzdialers to point to an array of dialer names. The list of names is NULL terminated. The array is allocated using malloc, as is each element of the array, and they may all be passed to free when they are no longer needed. */ extern int uuconf_dialer_names (void *uuconf_pglobal, char ***uuconf_ppzdialers); /* Get the information for the dialer zdialer. This sets the fields in *qdialer. */ extern int uuconf_dialer_info (void *uuconf_pglobal, const char *uuconf_zdialer, struct uuconf_dialer *uuconf_qdialer); /* Free the memory occupied by system information returned by uuconf_dialer_info (or any of the configuration file specific routines described below). After this is called, the contents of the structure shall not be referred to. */ extern int uuconf_dialer_free (void *uuconf_pglobal, struct uuconf_dialer *uuconf_qsys); #ifdef __OPTIMIZE__ #define uuconf_dialer_free(qglob, q) \ (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) #endif /* Get the configuration file names. The fields in the returned struct should not be freed. */ extern int uuconf_config_files (void *uuconf_pglobal, struct uuconf_config_file_names* uuconf_names); /* Get the local node name. If the node name is not specified (because no ``nodename'' command appeared in the config file) this will return UUCONF_NOT_FOUND, and some system dependent function must be used to determine the node name. Otherwise it will return a pointer to a constant string, which should not be freed. */ extern int uuconf_localname (void *uuconf_pglobal, const char **pzname); /* Get the local node name that should be used, given a login name. This function will check for any special local name that may be associated with the login name zlogin (as set by the ``myname'' command in a Taylor configuration file, or the MYNAME field in a Permissions entry). This will set *pzname to the node name. If no node name can be determined, *pzname will be set to NULL and the function will return UUCONF_NOT_FOUND; in this case some system dependent function must be used to determine the node name. If the function returns UUCONF_SUCCESS, *pzname will be point to an malloced buffer. */ extern int uuconf_login_localname (void *uuconf_pglobal, const char *uuconf_zlogin, char **pzname); /* Get the name of the UUCP spool directory. This will set *pzspool to a constant string, which should not be freed. */ extern int uuconf_spooldir (void *uuconf_pglobal, const char **uuconf_pzspool); /* Get the name of the default UUCP public directory. This will set *pzpub to a constant string, which should not be freed. Note that particular systems may use a different public directory. */ extern int uuconf_pubdir (void *uuconf_pglobal, const char **uuconf_pzpub); /* Get the name of the UUCP lock directory. This will set *pzlock to a constant string, which should not be freed. */ extern int uuconf_lockdir (void *uuconf_pglobal, const char **uuconf_pzlock); /* Get the name of the UUCP log file. This will set *pzlog to a constant string, which should not be freed. */ extern int uuconf_logfile (void *uuconf_pglobal, const char **uuconf_pzlog); /* Get the name of the UUCP statistics file. This will set *pzstats to a constant string, which should not be freed. */ extern int uuconf_statsfile (void *uuconf_pglobal, const char **uuconf_pzstats); /* Get the name of the UUCP debugging file. This will set *pzdebug to a constant string, which should not be freed. */ extern int uuconf_debugfile (void *uuconf_pglobal, const char **uuconf_pzdebug); /* Get the default debugging level to use. This basically gets the argument of the ``debug'' command from the Taylor UUCP config file. It will set *pzdebug to a constant string, which should not be freed. */ extern int uuconf_debuglevel (void *uuconf_pglobal, const char **uuconf_pzdebug); /* Get a combination of UUCONF_STRIP bits indicating what types of global information should be stripped on input. */ extern int uuconf_strip (void *uuconf_pglobal, int *uuconf_pistrip); /* Get the maximum number of simultaneous uuxqt executions. This will set *pcmaxuuxqt to the number. Zero indicates no maximum. */ extern int uuconf_maxuuxqts (void *uuconf_pglobal, int *uuconf_pcmaxuuxqt); /* Get the frequency with which to spawn a uuxqt process. This returns an integer. A positive number is the number of execution files that should be received between spawns. Other values are one of the UUCONF_RUNUUXQT constants listed above. */ extern int uuconf_runuuxqt (void *uuconf_pglobal, int *uuconf_pirunuuxqt); /* Check a login name and password. This checks the Taylor UUCP password file (not /etc/passwd). It will work even if uuconf_taylor_init was not called. All comparisons are done via a callback function. The first argument to the function will be zero when comparing login names, non-zero when comparing passwords. The second argument to the function will be the pinfo argument passed to uuconf_callin. The third argument will be the login name or password from the UUCP password file. The comparison function should return non-zero for a match, or zero for a non-match. If the login name is found and the password compares correctly, uuconf_callin will return UUCONF_SUCCESS. If the login is not found, or the password does not compare correctly, uuconf_callin will return UUCONF_NOT_FOUND. Other errors are also possible. */ extern int uuconf_callin (void *uuconf_pglobal, int (*uuconf_cmp) (int, void *, const char *), void *uuconf_pinfo); /* Get the callout login name and password for a system. This will set both *pzlog and *pzpass to a string allocated by malloc, or to NULL if the value is not found. If neither value is found, the function will return UUCONF_NOT_FOUND. */ extern int uuconf_callout (void *uuconf_pglobal, const struct uuconf_system *uuconf_qsys, char **uuconf_pzlog, char **uuconf_pzpass); /* See if a login name is permitted for a system. This will return UUCONF_SUCCESS if it is permitted or UUCONF_NOT_FOUND if it is invalid. This simply calls uuconf_taylor_validate or returns UUCONF_SUCCESS, depending on the value of HAVE_TAYLOR_CONFIG. */ extern int uuconf_validate (void *uuconf_pglobal, const struct uuconf_system *uuconf_qsys, const char *uuconf_zlogin); /* Get the name of the HDB remote.unknown shell script, if using HAVE_HDB_CONFIG. This does not actually run the shell script. If the function returns UUCONF_SUCCESS, the name will be in *pzname, which will point to an malloced buffer. If it returns UUCONF_NOT_FOUND, then there is no script to run. */ extern int uuconf_remote_unknown (void *uuconf_pglobal, char **pzname); /* Translate a dial code. This sets *pznum to an malloced string. This will look up the entire zdial string in the dialcode file, so for normal use the alphabetic prefix should be separated. */ extern int uuconf_dialcode (void *uuconf_pglobal, const char *uuconf_zdial, char **uuconf_pznum); /* Compare two grades, returning < 0 if b1 should be executed before b2, == 0 if they are the same, or > 0 if b1 should be executed after b2. This can not fail, and does not return a standard uuconf error code; it is normally called via the macro UUCONF_GRADE_CMP, defined above. */ extern int uuconf_grade_cmp (int uuconf_b1, int uuconf_b2); #else /* ! UUCONF_ANSI_C */ extern int uuconf_init (); extern int uuconf_init_thread (); extern int uuconf_system_names (); extern int uuconf_system_info (); extern int uuconf_system_unknown (); extern int uuconf_system_local (); extern int uuconf_system_free (); extern int uuconf_find_port (); extern int uuconf_port_free (); extern int uuconf_dialer_names (); extern int uuconf_dialer_info (); extern int uuconf_dialer_free (); extern int uuconf_config_files (); extern int uuconf_localname (); extern int uuconf_login_localname (); extern int uuconf_spooldir (); extern int uuconf_lockdir (); extern int uuconf_pubdir (); extern int uuconf_logfile (); extern int uuconf_statsfile (); extern int uuconf_debugfile (); extern int uuconf_debuglevel (); extern int uuconf_maxuuxqts (); extern int uuconf_runuuxqt (); extern int uuconf_callin (); extern int uuconf_callout (); extern int uuconf_remote_unknown (); extern int uuconf_validate (); extern int uuconf_grade_cmp (); #ifdef __OPTIMIZE__ #define uuconf_system_free(qglob, q) \ (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) #define uuconf_port_free(qglob, q) \ (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) #define uuconf_dialer_free(qglob, q) \ (uuconf_free_block ((q)->uuconf_palloc), UUCONF_SUCCESS) #endif #endif /* ! UUCONF_ANSI_C */ #if UUCONF_ANSI_C /* Initialize the Taylor UUCP configuration file reading routines. This must be called before calling any of the Taylor UUCP configuration file specific routines. The ppglobal argument should point to a generic pointer. Moreover, before calling this function the pointer either must be set to NULL, or must have been passed to one of the other uuconf init routines. The zprogram argument is the name of the program for which files should be read. If NULL, it is taken as "uucp", which means to read the standard UUCP files. The zname argument is the name of the config file. If it is NULL, the default config file will be used. Note that if the zname argument is obtained from the user running the program, the program should be careful to revoke any special privileges it may have (e.g. on Unix call setuid (getuid ()) and setgid (getgid ())). Otherwise various sorts of spoofing become possible. */ extern int uuconf_taylor_init (void **uuconf_pglobal, const char *uuconf_zprogram, const char *uuconf_zname); /* Get the names of all systems listed in the Taylor UUCP configuration files. This sets *ppzsystems to point to an array of system names. The list of names is NULL terminated. The array is allocated using malloc, as is each element of the array. If the falias argument is 0, the list will not include any aliases; otherwise, it will. */ extern int uuconf_taylor_system_names (void *uuconf_pglobal, char ***uuconf_ppzsystems, int uuconf_falias); /* Get the information for system zsystem from the Taylor UUCP configuration files. This will set *qsys. */ extern int uuconf_taylor_system_info (void *uuconf_pglobal, const char *uuconf_zsystem, struct uuconf_system *uuconf_qsys); /* Get information for an unknown (anonymous) system. This returns the values set by the ``unknown'' command in the main configuration file. If the ``unknown'' command was not used, this will return UUCONF_NOT_FOUND. */ extern int uuconf_taylor_system_unknown (void *uuconf_pglobal, struct uuconf_system *uuconf_qsys); /* Find a port from the Taylor UUCP configuration files. The arguments and return values are identical to those of uuconf_find_port. */ extern int uuconf_taylor_find_port (void *uuconf_pglobal, const char *uuconf_zname, long uuconf_ibaud, long uuconf_ihighbaud, int (*uuconf_pifn) (struct uuconf_port *, void *uuconf_pinfo), void *uuconf_pinfo, struct uuconf_port *uuconf_qport); /* Get the names of all dialers listed in the Taylor UUCP configuration files. This sets *ppzdialers to point to an array of dialer names. The list of names is NULL terminated. The array is allocated using malloc, as is each element of the array. */ extern int uuconf_taylor_dialer_names (void *uuconf_pglobal, char ***uuconf_ppzdialers); /* Get the information for the dialer zdialer from the Taylor UUCP configuration files. This sets the fields in *qdialer. */ extern int uuconf_taylor_dialer_info (void *uuconf_pglobal, const char *uuconf_zdialer, struct uuconf_dialer *uuconf_qdialer); /* Get the local node name that should be used, given a login name, considering only the ``myname'' command in the Taylor UUCP configuration files. If the function returns UUCONF_SUCCESS, *pzname will point to an malloced buffer. */ extern int uuconf_taylor_login_localname (void *uuconf_pglobal, const char *uuconf_zlogin, char **pzname); /* Get the callout login name and password for a system from the Taylor UUCP configuration files. This will set both *pzlog and *pzpass to a string allocated by malloc, or to NULL if the value is not found. If neither value is found, the function will return UUCONF_NOT_FOUND. */ extern int uuconf_taylor_callout (void *uuconf_pglobal, const struct uuconf_system *uuconf_qsys, char **uuconf_pzlog, char **uuconf_pzpass); /* See if a login name is permitted for a system. This will return UUCONF_SUCCESS if it is permitted or UUCONF_NOT_FOUND if it is invalid. This checks whether the login name appears in a called-login command with a list of system which does not include the system qsys. */ extern int uuconf_taylor_validate (void *uuconf_pglobal, const struct uuconf_system *uuconf_qsys, const char *uuconf_zlogin); #else /* ! UUCONF_ANSI_C */ extern int uuconf_taylor_init (); extern int uuconf_taylor_system_names (); extern int uuconf_taylor_system_info (); extern int uuconf_taylor_system_unknown (); extern int uuconf_taylor_find_port (); extern int uuconf_taylor_dialer_names (); extern int uuconf_taylor_dialer_info (); extern int uuconf_taylor_login_localname (); extern int uuconf_taylor_callout (); extern int uuconf_taylor_validate (); #endif /* ! UUCONF_ANSI_C */ #if UUCONF_ANSI_C /* Initialize the V2 configuration file reading routines. This must be called before any of the other V2 routines are called. The ppglobal argument should point to a generic pointer. Moreover, before calling this function the pointer either must be set to NULL, or must have been passed to one of the other uuconf init routines. */ extern int uuconf_v2_init (void **uuconf_ppglobal); /* Get the names of all systems listed in the V2 configuration files. This sets *ppzsystems to point to an array of system names. The list of names is NULL terminated. The array is allocated using malloc, as is each element of the array. If the falias argument is 0, the list will not include any aliases; otherwise, it will. */ extern int uuconf_v2_system_names (void *uuconf_pglobal, char ***uuconf_ppzsystems, int uuconf_falias); /* Get the information for system zsystem from the V2 configuration files. This will set *qsys. */ extern int uuconf_v2_system_info (void *uuconf_pglobal, const char *uuconf_zsystem, struct uuconf_system *uuconf_qsys); /* Find a port from the V2 configuration files. The arguments and return values are identical to those of uuconf_find_port. */ extern int uuconf_v2_find_port (void *uuconf_pglobal, const char *uuconf_zname, long uuconf_ibaud, long uuconf_ihighbaud, int (*uuconf_pifn) (struct uuconf_port *, void *uuconf_pinfo), void *uuconf_pinfo, struct uuconf_port *uuconf_qport); #else /* ! UUCONF_ANSI_C */ extern int uuconf_v2_init (); extern int uuconf_v2_system_names (); extern int uuconf_v2_system_info (); extern int uuconf_v2_find_port (); #endif /* ! UUCONF_ANSI_C */ #if UUCONF_ANSI_C /* Initialize the HDB configuration file reading routines. This should be called before any of the other HDB routines are called. The ppglobal argument should point to a generic pointer. Moreover, before calling this function the pointer either must be set to NULL, or must have been passed to one of the other uuconf init routines. The zprogram argument is used to match against a "services" string in Sysfiles. A NULL or "uucp" argument is taken as "uucico". */ extern int uuconf_hdb_init (void **uuconf_ppglobal, const char *uuconf_zprogram); /* Get the names of all systems listed in the HDB configuration files. This sets *ppzsystems to point to an array of system names. The list of names is NULL terminated. The array is allocated using malloc, as is each element of the array. If the falias argument is 0, the list will not include any aliases; otherwise, it will (an alias is created by using the ALIAS= keyword in the Permissions file). */ extern int uuconf_hdb_system_names (void *uuconf_pglobal, char ***uuconf_ppzsystems, int uuconf_falias); /* Get the information for system zsystem from the HDB configuration files. This will set *qsys. */ extern int uuconf_hdb_system_info (void *uuconf_pglobal, const char *uuconf_zsystem, struct uuconf_system *uuconf_qsys); /* Get information for an unknown (anonymous) system. If no information is available for unknown systems, this will return UUCONF_NOT_FOUND. This does not run the remote.unknown shell script. */ extern int uuconf_hdb_system_unknown (void *uuconf_pglobal, struct uuconf_system *uuconf_qsys); /* Find a port from the HDB configuration files. The arguments and return values are identical to those of uuconf_find_port. */ extern int uuconf_hdb_find_port (void *uuconf_pglobal, const char *uuconf_zname, long uuconf_ibaud, long uuconf_ihighbaud, int (*uuconf_pifn) (struct uuconf_port *, void *uuconf_pinfo), void *uuconf_pinfo, struct uuconf_port *uuconf_qport); /* Get the names of all dialers listed in the HDB configuration files. This sets *ppzdialers to point to an array of dialer names. The list of names is NULL terminated. The array is allocated using malloc, as is each element of the array. */ extern int uuconf_hdb_dialer_names (void *uuconf_pglobal, char ***uuconf_ppzdialers); /* Get the information for the dialer zdialer from the HDB configuration files. This sets the fields in *qdialer. */ extern int uuconf_hdb_dialer_info (void *uuconf_pglobal, const char *uuconf_zdialer, struct uuconf_dialer *uuconf_qdialer); /* Get the local node name that should be used, given a login name, considering only the MYNAME field in the HDB Permissions file. If the function returns UUCONF_SUCCESS, *pzname will point to an malloced buffer. */ extern int uuconf_hdb_login_localname (void *uuconf_pglobal, const char *uuconf_zlogin, char **pzname); /* Get the name of the HDB remote.unknown shell script. This does not actually run the shell script. If the function returns UUCONF_SUCCESS, the name will be in *pzname, which will point to an malloced buffer. */ extern int uuconf_hdb_remote_unknown (void *uuconf_pglobal, char **pzname); #else /* ! UUCONF_ANSI_C */ extern int uuconf_hdb_init (); extern int uuconf_hdb_system_names (); extern int uuconf_hdb_system_info (); extern int uuconf_hdb_system_unknown (); extern int uuconf_hdb_find_port (); extern int uuconf_hdb_dialer_names (); extern int uuconf_hdb_dialer_info (); extern int uuconf_hdb_localname (); extern int uuconf_hdb_remote_unknown (); #endif /* ! UUCONF_ANSI_C */ #if UUCONF_ANSI_C /* This function will set an appropriate error message into the buffer zbuf, given a uuconf error code. The buffer will always be null terminated, and will never be accessed beyond the length cbuf. This function will return the number of characters needed for the complete message, including the null byte. If this is less than the cbytes argument, the buffer holds a truncated string. */ extern int uuconf_error_string (void *uuconf_pglobal, int ierror, char *zbuf, UUCONF_SIZE_T cbuf); /* If UUCONF_ERROR_ERRNO is set in a return value, this function may be used to retrieve the errno value. This will be the value of errno as set by the system function which failed. However, some system functions, notably some stdio routines, may not set errno, in which case the value will be meaningless. This function does not return a uuconf error code, and it cannot fail. */ extern int uuconf_error_errno (void *uuconf_pglobal); /* If UUCONF_ERROR_FILENAME is set in a return value, this function may be used to retrieve the file name. This function does not return a uuconf error code, and it cannot fail. The string that it returns a pointer to is not guaranteed to remain allocated across the next call to a uuconf function (other than one of the three error retrieving functions). */ extern const char *uuconf_error_filename (void *uuconf_pglobal); /* If UUCONF_ERROR_LINENO is set in a return value, this function may be used to retrieve the line number. This function does not return a uuconf error code, and it cannot fail. */ extern int uuconf_error_lineno (void *uuconf_pglobal); #else /* ! UUCONF_ANSI_C */ extern int uuconf_error_string (); extern int uuconf_error_errno (); extern UUCONF_CONST char *uuconf_error_filename (); extern int uuconf_error_lineno (); #endif /* ! UUCONF_ANSI_C */ /* The uuconf package also provides a few functions which can accept commands and parcel them out according to a table. These are publically visible, partially in the hopes that they will be useful, but mostly because the rest of the Taylor UUCP package uses them. */ /* The types of entries allowed in a command table (struct uuconf_cmdtab). Each type defines how a particular command is interpreted. Each type will either assign a value to a variable or call a function. In all cases, a line of input is parsed into separate fields, separated by whitespace; comments beginning with '#' are discarded, except that a '#' preceeded by a backslash is retained. The first field is taken as the command to execute, and the remaining fields are its arguments. */ /* A boolean value. Used for a command which accepts a single argument, which must begin with 'y', 'Y', 't', or 'T' for true (1) or 'n', 'N', 'f', or 'F' for false (0). The corresponding variable must be an int. */ #define UUCONF_CMDTABTYPE_BOOLEAN (0x12) /* An integer value. Used for a command which accepts a single argument, which must be an integer. The corresponding variable must be an int. */ #define UUCONF_CMDTABTYPE_INT (0x22) /* A long value. Used for a command which accepts a single value, which must be an integer. The corresponding variable must be a long. */ #define UUCONF_CMDTABTYPE_LONG (0x32) /* A string value. Used for a command which accepts a string argument. If there is no argument, the variable will be set to point to a zero byte. Otherwise the variable will be set to point to the string. The corresponding variable must be a char *. The memory pointed to by the variable after it is set must not be modified. */ #define UUCONF_CMDTABTYPE_STRING (0x40) /* A full string value. Used for a command which accepts a series of string arguments separated by whitespace. The corresponding variable must be a char **. It will be set to an NULL terminated array of the arguments. The memory occupied by the array itself, and by the strings within it, must not be modified. */ #define UUCONF_CMDTABTYPE_FULLSTRING (0x50) /* A function. If this command is encountered, the command and its arguments are passed to the corresponding function. They are passed as an array of strings, in which the first string is the command itself, along with a count of strings. This value may be or'red with a specific number of required arguments; UUCONF_CMDTABTYPE_FN | 1 accepts no additional arguments besides the command itself, UUCONF_CMDTABTYPE_FN | 2 accepts 1 argument, etc. UUCONF_CMDTABTYPE_FN | 0, accepts any number of additional arguments. */ #define UUCONF_CMDTABTYPE_FN (0x60) /* A prefix function. The string in the table is a prefix; if a command is encountered with the same prefix, the corresponding function will be called as for UUCONF_CMDTABTYPE_FN. The number of arguments may be or'red in as with UUCONF_CMDTABTYPE_FN. */ #define UUCONF_CMDTABTYPE_PREFIX (0x70) /* This macro will return the particular type of a CMDTABTYPE. */ #define UUCONF_TTYPE_CMDTABTYPE(i) ((i) & 0x70) /* This macro will return the required number of arguments of a CMDTABTYPE. If it is zero, there is no restriction. */ #define UUCONF_CARGS_CMDTABTYPE(i) ((i) & 0x0f) /* When a function is called via UUCONF_CMDTABTYPE_FN or UUCONF_CMDTABTYPE_PREFIX, it may return any uuconf error code (see above). However, it will normally return one of the following: UUCONF_CMDTABRET_CONTINUE: Take no special action. In particular, the arguments passed to the function may be overwritten or freed. UUCONF_CMDTABRET_KEEP: The memory occupied by the arguments passed to the function must be preserved. Continue processing commands. UUCONF_CMDTABRET_EXIT: If reading commands from a file, stop processing. The arguments passed to the function may be overwritten or freed. UUCONF_CMDTABRET_KEEP_AND_EXIT: Stop processing any file. The memory occupied by the arguments passed to the function must be preserved. These values are interpreted by uuconf_cmd_file. The uuconf_cmd_line and uuconf_cmd_args functions may return UUCONF_CMDTABRET_KEEP. It they get an error, they will return an error code with UUCONF_CMDTABRET_EXIT set. Also, of course, they may return any value that is returned by one of the user functions in the uuconf_cmdtab table. */ /* UUCONF_CMDTABRET_KEEP and UUCONF_CMDTABRET_EXIT are defined above, with the error codes. */ #define UUCONF_CMDTABRET_CONTINUE UUCONF_SUCCESS #define UUCONF_CMDTABRET_KEEP_AND_EXIT \ (UUCONF_CMDTABRET_KEEP | UUCONF_CMDTABRET_EXIT) /* When a function is called via CMDTABTYPE_FN or CMDTABTYPE_PREFIX, it is passed five arguments. This is the type of a pointer to such a function. The uuconf global information structure is passed in for convenience in calling another uuconf function. The arguments to the command are passed in (the command itself is the first argument) along with a count and the value of the pvar field from the uuconf_cmdtab structure in which the function pointer was found. The pinfo argument to the function is taken from the argument to uuconf_cmd_*. */ #if UUCONF_ANSI_C typedef int (*uuconf_cmdtabfn) (void *uuconf_pglobal, int uuconf_argc, char **uuconf_argv, void *uuconf_pvar, void *uuconf_pinfo); #else typedef int (*uuconf_cmdtabfn) (); #endif /* A table of commands is an array of the following structures. The final element of the table should have uuconf_zcmd == NULL. */ struct uuconf_cmdtab { /* Command name. */ UUCONF_CONST char *uuconf_zcmd; /* Command type (one of CMDTABTYPE_*). */ int uuconf_itype; /* If not CMDTABTYPE_FN or CMDTABTYPE_PREFIX, the address of the associated variable. Otherwise, a pointer value to pass to the function pifn. */ UUCONF_POINTER uuconf_pvar; /* The function to call if CMDTABTYPE_FN or CMDTABTYPE_PREFIX. */ uuconf_cmdtabfn uuconf_pifn; }; /* Bit flags to pass to uuconf_processcmds. */ /* If set, case is significant when checking commands. Normally case is ignored. */ #define UUCONF_CMDTABFLAG_CASE (0x1) /* If set, a backslash at the end of a line may be used to include the next physical line in the logical line. */ #define UUCONF_CMDTABFLAG_BACKSLASH (0x2) /* If set, the comment character (#) is treated as a normal character, rather than as starting a comment. */ #define UUCONF_CMDTABFLAG_NOCOMMENTS (0x4) #if UUCONF_ANSI_C /* Read commands from a file, look them up in a table, and take the appropriate action. This continues reading lines from the file until EOF, or until a function returns with UUCONF_CMDTABRET_EXIT set, or until an error occurs. The qtab argument must point to a table of struct uuconf_cmdtab; the last element in the table should have uuconf_zcmd == NULL. When a UUCONF_CMDTABTYPE_FN or UUCONF_CMDTABTYPE_PREFIX command is found, the pinfo argument will be passed to the called function. If an a command is found that is not in the table, then if pfiunknownfn is NULL the unknown command is ignored; otherwise it is passed to pfiunknownfn, which should return a uuconf return code which is handled as for any other function (the pvar argument to pfiunknownfn will always be NULL). The iflags argument is any combination of the above UUCONF_CMDTABFLAG bits. The pblock argument may also be a memory block, as returned by uuconf_malloc_block (described below), in which case all memory preserved because of UUCONF_CMDTABRET_KEEP will be added to the block so that it may be freed later; it may also be NULL, in which case any such memory is permanently lost. This function initially sets the internal line number to 0, and then increments it as each line is read. It is permitted for any called function to use the uuconf_lineno function to obtain it. If this function is called when not at the start of a file, the value returned by uuconf_lineno (which is, in any case, only valid if an error code with UUCONF_ERROR_LINENO set is returned) must be adjusted by the caller. This returns a normal uuconf return value, as described above. */ extern int uuconf_cmd_file (void *uuconf_pglobal, FILE *uuconf_e, const struct uuconf_cmdtab *uuconf_qtab, void *uuconf_pinfo, uuconf_cmdtabfn uuconf_pfiunknownfn, int uuconf_iflags, void *pblock); /* This utility function is just like uuconf_cmd_file, except that it only operates on a single string. If a function is called via qtab, its return value will be the return value of this function. UUCONF_CMDTABFLAG_BACKSLASH is ignored in iflags. The string z is modified in place. The return value may include the UUCONF_CMDTABRET_KEEP and, on error, the UUCONF_CMDTABRET_EXIT bits, which should be honored by the calling code. */ extern int uuconf_cmd_line (void *uuconf_pglobal, char *uuconf_z, const struct uuconf_cmdtab *uuconf_qtab, void *uuconf_pinfo, uuconf_cmdtabfn uuconf_pfiunknownfn, int uuconf_iflags, void *pblock); /* This utility function is just like uuconf_cmd_line, except it is given a list of already parsed arguments. */ extern int uuconf_cmd_args (void *uuconf_pglobal, int uuconf_cargs, char **uuconf_pzargs, const struct uuconf_cmdtab *uuconf_qtab, void *uuconf_pinfo, uuconf_cmdtabfn uuconf_pfiunknownfn, int uuconf_iflags, void *pblock); #else /* ! UUCONF_ANSI_C */ extern int uuconf_cmd_file (); extern int uuconf_cmd_line (); extern int uuconf_cmd_args (); #endif /* ! UUCONF_ANSI_C */ #if UUCONF_ANSI_C /* The uuconf_cmd_file function may allocate memory permanently, as for setting a UUCONF_CMDTABTYPE_STRING value, in ways which are difficult to free up. A memory block may be used to record all allocated memory, so that it can all be freed up at once at some later time. These functions do not take a uuconf global pointer, and are independent of the rest of the uuconf library. */ /* Allocate a block of memory. If this returns NULL, then malloc returned NULL, and errno is whatever malloc set it to. */ extern void *uuconf_malloc_block (void); /* Allocate memory within a memory block. If this returns NULL, then malloc returned NULL, and errno is whatever malloc set it to. */ extern void *uuconf_malloc (void *uuconf_pblock, UUCONF_SIZE_T uuconf_cbytes); /* Add a block returned by the generic malloc routine to a memory block. This returns zero on success, non-zero on failure. If this fails (returns non-zero), then malloc returned NULL, and errno is whatever malloc set it to. */ extern int uuconf_add_block (void *uuconf_pblock, void *uuconf_padd); /* Free a value returned by uuconf_malloc from a memory block. In the current implementation, this will normally not do anything, but it doesn't hurt. No errors can occur. */ extern void uuconf_free (void *uuconf_pblock, void *uuconf_pfree); /* Free an entire memory block, including all values returned by uuconf_malloc from it and all values added to it with uuconf_add_block. No errors can occur. */ extern void uuconf_free_block (void *uuconf_pblock); #else /* ! UUCONF_ANSI_C */ extern UUCONF_POINTER uuconf_malloc_block (); extern UUCONF_POINTER uuconf_malloc (); extern int uuconf_add_block (); extern /* void */ uuconf_free (); extern /* void */ uuconf_free_block (); #endif /* ! UUCONF_ANSI_C */ #endif /* ! defined (UUCONF_H) */ uucp-1.07/policy.h0000664000076400007640000007426207665321755007613 /* policy.h Configuration file for policy decisions. To be edited on site. Copyright (C) 1991, 1992, 1993, 1994, 1995 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ /* This header file contains macro definitions which must be set by each site before compilation. The first few are system characteristics that can not be easily discovered by the configuration script. Most are configuration decisions that must be made by the local administrator. */ /* System characteristics. */ /* This code tries to use several ANSI C features, including prototypes, stdarg.h, the const qualifier and the types void (including void * pointers) and unsigned char. By default it will use these features if the compiler defines __STDC__. If your compiler supports these features but does not define __STDC__, you should set ANSI_C to 1. If your compiler does not support these features but defines __STDC__ (no compiler should do this, in my opinion), you should set ANSI_C to 0. In most cases (or if you're not sure) just leave the line below commented out. */ /* #define ANSI_C 1 */ /* Set USE_STDIO to 1 if data files should be read using the stdio routines (fopen, fread, etc.) rather than the UNIX unbuffered I/O calls (open, read, etc.). Unless you know your stdio is really rotten, you should leave this as 1. */ #define USE_STDIO 1 /* Exactly one of the following macros must be set to 1. Many modern systems support more than one of these choices through some form of compilation environment, in which case the setting will depend on the compilation environment you use. If you have a reasonable choice between options, I suspect that TERMIO or TERMIOS will be more efficient than TTY, but I have not done any head to head comparisons. If you don't set any of these macros, the code below will guess. It will doubtless be wrong on some systems. HAVE_BSD_TTY -- Use the 4.2BSD tty routines HAVE_SYSV_TERMIO -- Use the System V termio routines HAVE_POSIX_TERMIOS -- Use the POSIX termios routines */ #define HAVE_BSD_TTY 0 #define HAVE_SYSV_TERMIO 0 #define HAVE_POSIX_TERMIOS 0 /* This code tries to guess which terminal driver to use if you did not make a choice above. It is in this file to make it easy to figure out what's happening if something goes wrong. */ #if HAVE_BSD_TTY + HAVE_SYSV_TERMIO + HAVE_POSIX_TERMIOS == 0 #if HAVE_TERMIOS_H #undef HAVE_POSIX_TERMIOS #define HAVE_POSIX_TERMIOS 1 #else /* ! HAVE_TERMIOS_H */ #if HAVE_CBREAK #undef HAVE_BSD_TTY #define HAVE_BSD_TTY 1 #else /* ! HAVE_CBREAK */ #undef HAVE_SYSV_TERMIO #define HAVE_SYSV_TERMIO 1 #endif /* ! HAVE_CBREAK */ #endif /* ! HAVE_TERMIOS_H */ #endif /* HAVE_BSD_TTY + HAVE_SYSV_TERMIO + HAVE_POSIX_TERMIOS == 0 */ /* On some systems a write to a serial port will block even if the file descriptor has been set to not block. File transfer can be more efficient if the package knows that a write to the serial port will not block; however, if the write does block unexpectedly then data loss is possible at high speeds. If writes to a serial port always block even when requested not to, you should set HAVE_UNBLOCKED_WRITES to 0; otherwise you should set it to 1. In general on System V releases without STREAMS-based ttys (e.g., before SVR4) HAVE_UNBLOCKED_WRITES should be 0 and on modern systems it should be 1. If HAVE_UNBLOCKED_WRITES is set to 1 when it should be 0 you may see an unexpectedly large number of transmission errors, or, if you have hardware handshaking, transfer times may be lower than expected (but then, they always are). If HAVE_UNBLOCKED_WRITES is set to 0 when it should be 1, file transfer will use more CPU time than necessary. If you are unsure, setting HAVE_UNBLOCKED_WRITES to 0 should always be safe. */ #define HAVE_UNBLOCKED_WRITES 1 /* When the code does do a blocking write, it wants to write the largest amount of data which the kernel will accept as a single unit. On BSD this is typically the value of OBUFSIZ in , usually 100. On System V before SVR4 this is typically the size of a clist, CLSIZE in , which is usually 64. On SVR4, which uses STREAMS-based ttys, 2048 is reasonable. Define SINGLE_WRITE to the correct value for your system. If SINGLE_WRITE is too large, data loss may occur. If SINGLE_WRITE is too small, file transfer will use more CPU time than necessary. If you have no idea, 64 should work on most modern systems. */ #define SINGLE_WRITE 64 /* Some tty drivers, such as those from SCO and AT&T's Unix PC, have a bug in the implementation of ioctl() that causes CLOCAL to be ineffective until the port is opened a second time. If HAVE_CLOCAL_BUG is set to 1, code will be added to do this second open on the port. Set this if you are getting messages that say "Line disconnected" while in the dial chat script after only writing the first few characters to the port. This bug causes the resetting of CLOCAL to have no effect, so the "\m" (require carrier) escape sequence won't function properly in dialer chat scripts. */ #define HAVE_CLOCAL_BUG 0 /* On some systems, such as SCO Xenix, resetting DTR on a port apparently prevents getty from working on the port, and thus prevents anybody from dialing in. If HAVE_RESET_BUG is set to 1, DTR will not be reset when a serial port is closed. */ #define HAVE_RESET_BUG 0 /* The Sony NEWS reportedly handles no parity by clearing both the odd and even parity bits in the sgtty structure, unlike most BSD based systems in which no parity is indicated by setting both the odd and even parity bits. Setting HAVE_PARITY_BUG to 1 will handle this correctly. */ #define HAVE_PARITY_BUG 0 #if HAVE_BSD_TTY #ifdef sony #undef HAVE_PARITY_BUG #define HAVE_PARITY_BUG 1 #endif #endif /* On Ultrix 4.0, at least, setting CBREAK causes input characters to be stripped, regardless of the setting of LPASS8 and LLITOUT. This can be worked around by using the termio call to reset ISTRIP. This probably does not apply to any other operating system. Setting HAVE_STRIP_BUG to 1 will use this workaround. */ #define HAVE_STRIP_BUG 0 #if HAVE_BSD_TTY #ifdef __ultrix__ #ifndef ultrix #define ultrix #endif #endif #ifdef ultrix #undef HAVE_STRIP_BUG #define HAVE_STRIP_BUG 1 #endif #endif /* If your system implements full duplex pipes, set HAVE_FULLDUPLEX_PIPES to 1. Everything should work fine if you leave it set to 0, but setting it to 1 can be slightly more efficient. */ #define HAVE_FULLDUPLEX_PIPES 0 /* TIMES_TICK is the fraction of a second which times(2) returns (for example, if times returns 100ths of a second TIMES_TICK should be set to 100). On a true POSIX system (one which has the sysconf function and also has _SC_CLK_TCK defined in ) TIMES_TICK may simply be left as 0. On some systems the environment variable HZ is what you want for TIMES_TICK, but on some other systems HZ has the wrong value; check the man page. If you leave this set to 0, the code will try to guess; it will doubtless be wrong on some non-POSIX systems. If TIMES_TICK is wrong the code may report incorrect file transfer times in the statistics file, but on many systems times(2) will actually not be used and this value will not matter at all. */ #define TIMES_TICK 0 /* If your system does not support saved set user ID, set HAVE_SAVED_SETUID to 0. However, this is ignored if your system has the setreuid function. Most modern Unixes have one or the other. If your system has the setreuid function, don't worry about this define, or about the following discussion. If you set HAVE_SAVED_SETUID to 0, you will not be able to use uucp to transfer files that the uucp user can not read. Basically, you will only be able to use uucp on world-readable files. If you set HAVE_SAVED_SETUID to 1, but your system does not have saved set user ID, uucp will fail with an error message whenever anybody other than the uucp user uses it. */ #define HAVE_SAVED_SETUID 1 /* On some systems, such as 4.4BSD-Lite, NetBSD, the DG Aviion and, possibly, the RS/6000, the setreuid function is broken. It should be possible to use setreuid to swap the real and effective user ID's, but on some systems it will not change the real user ID (I believe this is due to a misreading of the POSIX standard). On such a system you must set HAVE_BROKEN_SETREUID to 1; if you do not, you will get error messages from setreuid. Systems on which setreuid exists but is broken pretty much always have saved setuid. */ #define HAVE_BROKEN_SETREUID 0 /* On a few systems, such as NextStep 3.3, the POSIX macro F_SETLKW is defined, but does not work. On such systems, you must set HAVE_BROKEN_SETLKW to 1. If you do not, uux will hang, or log peculiar error messages, every time it is run. */ #define HAVE_BROKEN_SETLKW 0 /* On the 3B2, and possibly other systems, nap takes an argument in hundredths of a second rather than milliseconds. I don't know of any way to test for this. Set HAVE_HUNDREDTHS_NAP to 1 if this is true on your system. This does not matter if your system does not have the nap function. */ #define HAVE_HUNDREDTHS_NAP 0 /* Set MAIL_PROGRAM to a program which can be used to send mail. It will be used for mail to both local and remote users. Set MAIL_PROGRAM_TO_BODY to 1 if the recipient should be specified as a To: line in the body of the message; otherwise, the recipient will be provided as an argument to MAIL_PROGRAM. Set MAIL_PROGRAM_SUBJECT_BODY if the subject should be specified as a Subject: line in the body of the message; otherwise, the subject will be provided using the -s option to MAIL_PROGRAM (if your mail program does not support the -s option, you must set MAIL_PROGRAM_SUBJECT_BODY to 1). If your system uses sendmail, use the sendmail choice below. Otherwise, select one of the other choices as appropriate. */ #if 1 #define MAIL_PROGRAM "/usr/lib/sendmail -t" /* #define MAIL_PROGRAM "/usr/sbin/sendmail -t" */ #define MAIL_PROGRAM_TO_BODY 1 #define MAIL_PROGRAM_SUBJECT_BODY 1 #endif #if 0 #define MAIL_PROGRAM "/usr/ucb/mail" #define MAIL_PROGRAM_TO_BODY 0 #define MAIL_PROGRAM_SUBJECT_BODY 0 #endif #if 0 #define MAIL_PROGRAM "/bin/mail" #define MAIL_PROGRAM_TO_BODY 0 #define MAIL_PROGRAM_SUBJECT_BODY 1 #endif /* Set PS_PROGRAM to the program to run to get a process status, including the arguments to pass it. This is used by ``uustat -p''. Set HAVE_PS_MULTIPLE to 1 if a comma separated list of process numbers may be appended (e.g. ``ps -flp1,10,100''). Otherwise ps will be invoked several times, with a single process number append each time. The default definitions should work on most systems, although some (such as the NeXT) will complain about the 'p' option; for those, use the second set of definitions. The third set of definitions are appropriate for System V. To use the second or third set of definitions, change the ``#if 1'' to ``#if 0'' and change the appropriate ``#if 0'' to ``#if 1''. */ #if 1 #define PS_PROGRAM "/bin/ps -lp" #define HAVE_PS_MULTIPLE 0 #endif #if 0 #define PS_PROGRAM "/bin/ps -l" #define HAVE_PS_MULTIPLE 0 #endif #if 0 #define PS_PROGRAM "/bin/ps -flp" #define HAVE_PS_MULTIPLE 1 #endif #ifdef __QNX__ /* Use this for QNX, along with HAVE_QNX_LOCKFILES. */ #undef PS_PROGRAM #undef HAVE_PS_MULTIPLE #define PS_PROGRAM "/bin/ps -l -n -p" #define HAVE_PS_MULTIPLE 0 #endif /* If you use other programs that also lock devices, such as cu or uugetty, the other programs and UUCP must agree on whether a device is locked. This is typically done by creating a lock file in a specific directory; the lock files are generally named LCK..something or LK.something. If the LOCKDIR macro is defined, these lock files will be placed in the named directory; otherwise they will be placed in the default spool directory. On some HDB systems the lock files are placed in /etc/locks. On some they are placed in /usr/spool/locks. On the NeXT they are placed in /usr/spool/uucp/LCK. */ /* #define LOCKDIR "/usr/spool/uucp" */ /* #define LOCKDIR "/etc/locks" */ /* #define LOCKDIR "/usr/spool/locks" */ /* #define LOCKDIR "/usr/spool/uucp/LCK" */ /* #define LOCKDIR "/var/spool/lock" */ /* #define LOCKDIR "/var/lock" */ /* You must also specify the format of the lock files by setting exactly one of the following macros to 1. Check an existing lock file to decide which of these choices is more appropriate. The HDB style is to write the locking process ID in ASCII, passed to ten characters, followed by a newline. The V2 style is to write the locking process ID as four binary bytes in the host byte order. Many BSD derived systems use this type of lock file, including the NeXT. SCO lock files are similar to HDB lock files, but always lock the lowercase version of the tty (i.e., LCK..tty2a is created if you are locking tty2A). They are appropriate if you are using Taylor UUCP on an SCO Unix, SCO Xenix, or SCO Open Desktop system. SVR4 lock files are also similar to HDB lock files, but they use a different naming convention. The filenames are LK.xxx.yyy.zzz, where xxx is the major device number of the device holding the special device file, yyy is the major device number of the port device itself, and zzz is the minor device number of the port device. Sequent DYNIX/ptx (but perhaps not Dynix 3.x) uses yet another naming convention. The lock file for /dev/ttyXA/XAAP is named LCK..ttyXAAP. Coherent use a completely different method of terminal locking. See unix/cohtty for details. For locks other than for terminals, HDB type lock files are used. QNX lock files are similar to HDB lock files except that the node ID must be stored in addition to the process ID and for serial devices the node ID must be included in the lock file name. QNX boxes are generally used in bunches, and all of them behave like one big machine to some extent. Thus, processes on different machines will be sharing the files in the spool directory. To detect if a process has died and a lock is thus stale, you need the node ID of the process as well as the process ID. The process ID is stored as a number written using ASCII digits padded to 10 characters, followed by a space, followed by the node ID written using ASCII digits padded to 10 characters, followed by a newline. The format for QNX lock files was made up just for Taylor UUCP. QNX doesn't come with a version of UUCP. */ #define HAVE_V2_LOCKFILES 0 #define HAVE_HDB_LOCKFILES 0 #define HAVE_SCO_LOCKFILES 0 #define HAVE_SVR4_LOCKFILES 0 #define HAVE_SEQUENT_LOCKFILES 0 #define HAVE_COHERENT_LOCKFILES 0 #define HAVE_QNX_LOCKFILES 0 /* This tries to pick a default based on preprocessor definitions. Ignore it if you have explicitly set one of the above values. */ #if HAVE_V2_LOCKFILES + HAVE_HDB_LOCKFILES + HAVE_SCO_LOCKFILES + HAVE_SVR4_LOCKFILES + HAVE_SEQUENT_LOCKFILES + HAVE_COHERENT_LOCKFILES + HAVE_QNX_LOCKFILES == 0 #ifdef __QNX__ #undef HAVE_QNX_LOCKFILES #define HAVE_QNX_LOCKFILES 1 #else /* ! defined (__QNX__) */ #ifdef __COHERENT__ #undef HAVE_COHERENT_LOCKFILES #define HAVE_COHERENT_LOCKFILES 1 #else /* ! defined (__COHERENT__) */ #ifdef _SEQUENT_ #undef HAVE_SEQUENT_LOCKFILES #define HAVE_SEQUENT_LOCKFILES 1 #else /* ! defined (_SEQUENT) */ #ifdef sco #undef HAVE_SCO_LOCKFILES #define HAVE_SCO_LOCKFILES 1 #else /* ! defined (sco) */ #ifdef __svr4__ #undef HAVE_SVR4_LOCKFILES #define HAVE_SVR4_LOCKFILES 1 #else /* ! defined (__svr4__) */ /* Final default is HDB. There's no way to tell V2 from HDB. */ #undef HAVE_HDB_LOCKFILES #define HAVE_HDB_LOCKFILES 1 #endif /* ! defined (__svr4__) */ #endif /* ! defined (sco) */ #endif /* ! defined (_SEQUENT) */ #endif /* ! defined (__COHERENT__) */ #endif /* ! defined (__QNX__) */ #endif /* no LOCKFILES define */ /* If your system supports Internet mail addresses (which look like user@host.domain rather than system!user), HAVE_INTERNET_MAIL should be set to 1. This is checked by uuxqt and uustat when sending notifications to the person who submitted the job. If your system does not understand addresses of the form user@host, you must set HAVE_INTERNET_MAIL to 0. If your system does not understand addresses of the form host!user, which is unlikely, you must set HAVE_INTERNET_MAIL to 1. If your system sends mail addressed to "A!B@C" to host C (i.e., it parses the address as "(A!B)@C"), you must set HAVE_INTERNET_MAIL to 1. If your system sends mail addressed to "A!B@C" to host A (i.e., it parses the address as "A!(B@C)"), you must set HAVE_INTERNET_MAIL to 0. Note that in general it is best to avoid addresses of the form "A!B@C" because of this ambiguity of precedence. UUCP will not intentionally generate addresses of this form, but it can occur in certain rather complex cases. */ #define HAVE_INTERNET_MAIL 1 /* Adminstrative decisions. */ /* Set USE_RCS_ID to 1 if you want the RCS ID strings compiled into the executable. Leaving them out will decrease the executable size. Leaving them in will make it easier to determine which version you are running. */ #define USE_RCS_ID 1 /* DEBUG controls how much debugging information is compiled into the code. If DEBUG is defined as 0, no sanity checks will be done and no debugging messages will be compiled in. If DEBUG is defined as 1 sanity checks will be done but there will still be no debugging messages. If DEBUG is 2 than debugging messages will be compiled in. When initially testing, DEBUG should be 2, and you should probably leave it at 2 unless a small reduction in the executable file size will be very helpful. */ #define DEBUG 2 /* Set HAVE_ENCRYPTED_PASSWORDS to 1 if you want login passwords to be encrypted before comparing them against the values in the file. This only applies when uucico is run with the -l or -e switches and is doing its own login prompting. Note that the passwords used are from the UUCP password file, not the system /etc/passwd file. See the documentation for further details. If you set this, you are responsible for encrypting the passwords in the UUCP password file. The function crypt will be used to do comparisons. */ #define HAVE_ENCRYPTED_PASSWORDS 0 /* Set the default grade to use for a uucp command if the -g option is not used. The grades, from highest to lowest, are 0 to 9, A to Z, a to z. */ #define BDEFAULT_UUCP_GRADE ('N') /* Set the default grade to use for a uux command if the -g option is not used. */ #define BDEFAULT_UUX_GRADE ('N') /* To compile in use of the new style of configuration files described in the documentation, set HAVE_TAYLOR_CONFIG to 1. */ #define HAVE_TAYLOR_CONFIG 1 /* To compile in use of V2 style configuration files (L.sys, L-devices and so on), set HAVE_V2_CONFIG to 1. To compile in use of HDB style configuration files (Systems, Devices and so on) set HAVE_HDB_CONFIG to 1. The files will be looked up in the oldconfigdir directory as defined in the Makefile. You may set any or all of HAVE_TAYLOR_CONFIG, HAVE_V2_CONFIG and HAVE_HDB_CONFIG to 1 (you must set at least one of the macros). When looking something up (a system, a port, etc.) the new style configuration files will be read first, followed by the V2 configuration files, followed by the HDB configuration files. */ #define HAVE_V2_CONFIG 0 #define HAVE_HDB_CONFIG 0 /* Exactly one of the following macros must be set to 1. The exact format of the spool directories is explained in unix/spool.c. SPOOLDIR_V2 -- Use a Version 2 (original UUCP) style spool directory SPOOLDIR_BSD42 -- Use a BSD 4.2 style spool directory SPOOLDIR_BSD43 -- Use a BSD 4.3 style spool directory SPOOLDIR_HDB -- Use a HDB (BNU) style spool directory SPOOLDIR_ULTRIX -- Use an Ultrix style spool directory SPOOLDIR_SVR4 -- Use a System V Release 4 spool directory SPOOLDIR_TAYLOR -- Use a new style spool directory If you are not worried about compatibility with a currently running UUCP, use SPOOLDIR_TAYLOR. */ #define SPOOLDIR_V2 0 #define SPOOLDIR_BSD42 0 #define SPOOLDIR_BSD43 0 #define SPOOLDIR_HDB 0 #define SPOOLDIR_ULTRIX 0 #define SPOOLDIR_SVR4 0 #define SPOOLDIR_TAYLOR 1 /* The status file generated by UUCP can use either the traditional HDB upper case comments or new easier to read lower case comments. This affects the display of uustat -m or uustat -q. Some third-party programs read these status files and expect them to be in a certain format. The default is to use the traditional comments when using an HDB or SVR4 spool directory, and to use lower case comments otherwise. */ #define USE_TRADITIONAL_STATUS (SPOOLDIR_HDB || SPOOLDIR_SVR4) /* You must select which type of logging you want by setting exactly one of the following to 1. These control output to the log file and to the statistics file. If you define HAVE_TAYLOR_LOGGING, each line in the log file will look something like this: uucico uunet uucp (1991-12-10 09:04:34.45 16390) Receiving uunet/D./D.uunetSwJ72 and each line in the statistics file will look something like this: uucp uunet (1991-12-10 09:04:40.20) received 2371 bytes in 5 seconds (474 bytes/sec) If you define HAVE_V2_LOGGING, each line in the log file will look something like this: uucico uunet uucp (12/10-09:04 16390) Receiving uunet/D./D.uunetSwJ72 and each line in the statistics file will look something like this: uucp uunet (12/10-09:04 16390) (692373862) received data 2371 bytes 5 seconds If you define HAVE_HDB_LOGGING, each program will by default use a separate log file. For uucico talking to uunet, for example, it will be /usr/spool/uucp/.Log/uucico/uunet. Each line will look something like this: uucp uunet (12/10-09:04:22,16390,1) Receiving uunet/D./D.uunetSwJ72 and each line in the statistics file will look something like this: uunet!uucp M (12/10-09:04:22) (C,16390,1) [ttyXX] <- 2371 / 5.000 secs, 474 bytes/sec The main reason to prefer one format over another is that you may have shell scripts which expect the files to have a particular format. If you have none, choose whichever format you find more appealing. */ #define HAVE_TAYLOR_LOGGING 1 #define HAVE_V2_LOGGING 0 #define HAVE_HDB_LOGGING 0 /* If QNX_LOG_NODE_ID is set to 1, log messages will include the QNX node ID just after the process ID. This is a policy decision because it changes the log file entry format, which can break other programs (e.g., some of the ones in the contrib directory) which expect to read the standard log file format. */ #ifdef __QNX__ #define QNX_LOG_NODE_ID 1 #else #define QNX_LOG_NODE_ID 0 #endif /* If LOG_DEVICE_PREFIX is 1, log messages will give the full pathname of a device rather than just the final component. This is important because on QNX //2/dev/ser2 refers to a different device than //4/dev/ser2. */ #ifdef __QNX__ #define LOG_DEVICE_PREFIX 1 #else #define LOG_DEVICE_PREFIX 0 #endif /* If you would like the log, debugging and statistics files to be closed after each message, set CLOSE_LOGFILES to 1. This will permit the log files to be easily moved. If a log file does not exist when a new message is written out, it will be created. Setting CLOSE_LOGFILES to 1 will obviously require slightly more processing time. */ #define CLOSE_LOGFILES 0 /* The name of the default spool directory. If HAVE_TAYLOR_CONFIG is set to 1, this may be overridden by the ``spool'' command in the configuration file. */ #define SPOOLDIR "/usr/spool/uucp" /* #define SPOOLDIR "/var/spool/uucp" */ /* The name of the default public directory. If HAVE_TAYLOR_CONFIG is set to 1, this may be overridden by the ``pubdir'' command in the configuration file. Also, a particular system may be given a specific public directory by using the ``pubdir'' command in the system file. */ #define PUBDIR "/usr/spool/uucppublic" /* #define PUBDIR "/var/spool/uucppublic" */ /* The default command path. This is a space separated list of directories. Remote command executions requested by uux are looked up using this path. If you are using HAVE_TAYLOR_CONFIG, the command path may be overridden for a particular system. For most systems, you should just make sure that the programs rmail and rnews can be found using this path. */ #define CMDPATH "/bin /usr/bin /usr/local/bin" /* The default amount of free space to require for systems that do not specify an amount with the ``free-space'' command. This is only used when talking to another instance of Taylor UUCP; if accepting a file would not leave at least this many bytes free on the disk, it will be refused. */ #define DEFAULT_FREE_SPACE (50000) /* While a file is being received, Taylor UUCP will periodically check to see if there is enough free space remaining on the disk. If there is not enough space available on the disk (as determined by DEFAULT_FREE_SPACE, above, or the ``free-space'' command for the system) the communication will be aborted. The disk will be checked each time FREE_SPACE_DELTA bytes are received. Lower values of FREE_SPACE_DELTA are less likely to fill up the disk, but will also waste more time checking the amount of free space. To avoid checking the disk while the file is being received, set FREE_SPACE_DELTA to 0. */ #define FREE_SPACE_DELTA (10240) /* It is possible for an execute job to request to be executed using sh(1), rather than execve(2). This is such a security risk, it is being disabled by default; to allow such jobs, set the following macro to 1. */ #define ALLOW_SH_EXECUTION 0 /* If a command executed on behalf of a remote system takes a filename as an argument, a security breach may be possible (note that on my system neither of the default commands, rmail and rnews, take filename arguments). If you set ALLOW_FILENAME_ARGUMENTS to 0, all arguments to a command will be checked; if any argument 1) starts with ../ 2) contains the string /../ 3) begins with a / but does not name a file that may be sent or received (according to the specified ``remote-send'' and ``remote-receive'') the command will be rejected. By default, any argument is permitted. */ #define ALLOW_FILENAME_ARGUMENTS 1 /* If you set FSYNC_ON_CLOSE to 1, all output files will be forced out to disk using the fsync system call when they are closed. This can be useful if you can not afford to lose people's mail if the system crashes. However, not all systems have the fsync call, and it is always less efficient to use it. Note that some versions of SCO Unix, and possibly other systems, make fsync a synonym for sync, which is extremely inefficient. */ #define FSYNC_ON_CLOSE 0 #if HAVE_TAYLOR_LOGGING /* The default log file when using HAVE_TAYLOR_LOGGING. When using HAVE_TAYLOR_CONFIG, this may be overridden by the ``logfile'' command in the configuration file. */ #define LOGFILE "/usr/spool/uucp/Log" /* #define LOGFILE "/var/spool/uucp/Log" */ /* #define LOGFILE "/var/log/uucp/Log" */ /* The default statistics file when using HAVE_TAYLOR_LOGGING. When using HAVE_TAYLOR_CONFIG, this may be overridden by the ``statfile'' command in the configuration file. */ #define STATFILE "/usr/spool/uucp/Stats" /* #define STATFILE "/var/spool/uucp/Stats" */ /* #define STATFILE "/var/log/uucp/Stats" */ /* The default debugging file when using HAVE_TAYLOR_LOGGING. When using HAVE_TAYLOR_CONFIG, this may be overridden by the ``debugfile'' command in the configuration file. */ #define DEBUGFILE "/usr/spool/uucp/Debug" /* #define DEBUGFILE "/var/spool/uucp/Debug" */ /* #define DEBUGFILE "/var/log/uucp/Debug" */ #endif /* HAVE_TAYLOR_LOGGING */ #if HAVE_V2_LOGGING /* The default log file when using HAVE_V2_LOGGING. When using HAVE_TAYLOR_CONFIG, this may be overridden by the ``logfile'' command in the configuration file. */ #define LOGFILE "/usr/spool/uucp/LOGFILE" /* The default statistics file when using HAVE_V2_LOGGING. When using HAVE_TAYLOR_CONFIG, this may be overridden by the ``statfile'' command in the configuration file. */ #define STATFILE "/usr/spool/uucp/SYSLOG" /* The default debugging file when using HAVE_V2_LOGGING. When using HAVE_TAYLOR_CONFIG, this may be overridden by the ``debugfile'' command in the configuration file. */ #define DEBUGFILE "/usr/spool/uucp/DEBUG" #endif /* HAVE_V2_LOGGING */ #if HAVE_HDB_LOGGING /* The default log file when using HAVE_HDB_LOGGING. When using HAVE_TAYLOR_CONFIG, this may be overridden by the ``logfile'' command in the configuration file. The first %s in the string will be replaced by the program name (e.g. uucico); the second %s will be replaced by the system name (if there is no appropriate system, "ANY" will be used). No other '%' character may appear in the string. */ #define LOGFILE "/usr/spool/uucp/.Log/%s/%s" /* The default statistics file when using HAVE_HDB_LOGGING. When using HAVE_TAYLOR_CONFIG, this may be overridden by the ``statfile'' command in the configuration file. */ #define STATFILE "/usr/spool/uucp/.Admin/xferstats" /* The default debugging file when using HAVE_HDB_LOGGING. When using HAVE_TAYLOR_CONFIG, this may be overridden by the ``debugfile'' command in the configuration file. */ #define DEBUGFILE "/usr/spool/uucp/.Admin/audit.local" #endif /* HAVE_HDB_LOGGING */ uucp-1.07/system.h0000664000076400007640000013761107665321756007637 /* system.h Header file for system dependent stuff in the Taylor UUCP package. This file is not itself system dependent. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #ifndef SYSTEM_H #define SYSTEM_H #if ANSI_C /* These structures are used in prototypes but are not defined in this header file. */ struct tm; struct uuconf_system; struct uuconf_port; struct sconnection; struct sstatus; struct scmd; #endif /* Any function which returns an error should also report an error message, unless otherwise indicated. Any function that returns a char *, rather than a const char *, is returning a pointer to a buffer allocated by zbufalc which must be freed using ubuffree, unless otherwise indicated. */ /* The maximum length of a remote system name. */ extern size_t cSysdep_max_name_len; /* Initialize. If something goes wrong, this routine should just exit. The flag argument is 0, or a combination of any of the following flags. */ /* This program needs to know the current working directory. This is used because on Unix it can be expensive to determine the current working directory (some versions of getcwd fork a process), but in most cases we don't need to know it. However, we are going to chdir to the spool directory (unless INIT_CHDIR is set), so we have to get the cwd now if we are ever going to get it. Both uucp and uux use the function fsysdep_needs_cwd to determine whether they will need the current working directory, and pass the argument to usysdep_initialize appropriately. There's probably a cleaner way to handle this, but this will suffice for now. */ #define INIT_GETCWD (01) /* This program should not chdir to the spool directory. This may only make sense on Unix. It is set by cu. */ #define INIT_NOCHDIR (02) /* This program needs special access to the spool directories. That means, on Unix, this program is normally installed setuid. */ #define INIT_SUID (04) /* Do not close all open descriptors. This is not used by the UUCP code, but it is used by other programs which share some of the system dependent libraries. */ #define INIT_NOCLOSE (010) extern void usysdep_initialize P((pointer puuconf, int iflags)); /* Exit the program. The fsuccess argument indicates whether to return an indication of success or failure to the outer environment. This routine should not return. */ extern void usysdep_exit P((boolean fsuccess)); /* Called when a non-standard configuration file is being used, to avoid handing out privileged access. If it returns FALSE, default configuration file will be used. This is called before the usysdep_initialize function is called. */ extern boolean fsysdep_other_config P((const char *)); /* Detach from the controlling terminal. This probably only makes sense on Unix. It is called by uucico to try to get the modem port as a controlling terminal. It is also called by uucico before it starts up uuxqt, so that uuxqt will be a complete daemon. */ extern void usysdep_detach P((void)); /* Get the local node name if it is not specified in the configuration files. Returns NULL on error; otherwise the return value should point to a static buffer. */ extern const char *zsysdep_localname P((void)); /* Get the login name. This is used when uucico is started up with no arguments in slave mode, which causes it to assume that somebody has logged in. It also used by uucp and uux for recording the user name. This may not return NULL. The return value should point to a static buffer. */ extern const char *zsysdep_login_name P((void)); /* Set a signal handler for a signal. If the signal occurs, the appropriate element of afSignal should be set to the signal number (see the declaration of afSignal in uucp.h). This routine might be able to just use signal, but Unix requires more complex handling. This is called before usysdep_initialize. */ extern void usysdep_signal P((int isig)); /* Catch a signal. This is actually defined as a macro in the system dependent header file, and the prototype here just indicates how it should be called. It is called before a routine which must exit if a signal occurs, and is expected to set do a setjmp (which is why it must be a macro). It is actually only called in one place in the system independent code, before the call to read stdin in uux. This is needed to handle 4.2 BSD restartable system calls, which require a longjmp. On systems which don't need to do setjmp/longjmp around system calls, this can be redefined in sysdep.h to TRUE. It should return TRUE if the routine should proceed, or FALSE if a signal occurred. After having this return TRUE, usysdep_start_catch should be used to start catching the signal; this basically tells the signal handler that it's OK to do the longjmp, if fsysdep_catch did not already do so. */ #ifndef fsysdep_catch extern boolean fsysdep_catch P((void)); #endif /* Start catching a signal. This is called after fsysdep_catch to tell the signal handler to go ahead and do the longjmp. This may be implemented as a macro in sysdep.h. */ #ifndef usysdep_start_catch extern void usysdep_start_catch P((void)); #endif /* Stop catching a signal. This is called when it is no longer necessary for fsysdep_catch to handle signals. This may be implemented as a macro in sysdep.h. */ #ifndef usysdep_end_catch extern void usysdep_end_catch P((void)); #endif /* Link two files. On Unix this should attempt the link. If it succeeds it should return TRUE with *pfworked set to TRUE. If the link fails because it must go across a device, it should return TRUE with *pfworked set to FALSE. If the link fails for some other reason, it should log an error message and return FALSE. On a system which does not support links to files, this should just return TRUE with *pfworked set to FALSE. */ extern boolean fsysdep_link P((const char *zfrom, const char *zto, boolean *pfworked)); /* Get the port name. This is used when uucico is started up in slave mode to figure out which port was used to call in so that it can determine any appropriate protocol parameters. This may return NULL if the port cannot be determined, which will just mean that no protocol parameters are applied. The name returned should be the sort of name that would appear in the port file. This should set *pftcp_port to TRUE if it can determine that the port is a TCP connection rather than a normal serial port. The return value (if not NULL) should point to a static buffer. */ extern const char *zsysdep_port_name P((boolean *pftcp_port)); /* Expand a file name on the local system. On Unix, if the zfile argument begins with ~user/ it goes in that users home directory, and if it begins with ~/ it goes in the public directory (the public directory is passed to this routine, since each system may have its own public directory). Similar conventions may be desirable on other systems. This should always return an absolute path name, probably in the public directory. It should return NULL on error; otherwise the return value should be allocated using zbufcpy or zbufalc. If pfbadname is not NULL, then if the function returns NULL *pfbadname should be set to TRUE if the error is just that the file name is badly specified; *pfbadname should be set to FALSE for some sort of internal error. */ extern char *zsysdep_local_file P((const char *zname, const char *zpubdir, boolean *pfbadname)); /* Return whether a file name is in a directory, and check for read or write access. This should check whether zfile is within zdir (or is zdir itself). If it is not, it should return FALSE. If zfile is in zdir, then fcheck indicates whether further checking should be done. If fcheck is FALSE, no further checking is done. Otherwise, if freadable is TRUE the user zuser should have search access to all directories from zdir down to zfile and should have read access on zfile itself (if zfile does not exist, or is not a regular file, this function may return FALSE but does not have to). If freadable is FALSE, the user zuser should have search access to all directories from zdir down to zfile and should have write access on zfile (which may be a directory, or may not actually exist, which is acceptable). The zuser argument may be NULL, in which case the check should be made for any user, not just zuser. There is no way for this function to return error. */ extern boolean fsysdep_in_directory P((const char *zfile, const char *zdir, boolean fcheck, boolean freadable, const char *zuser)); /* Return TRUE if a file exists, FALSE otherwise. There is no way to return error. */ extern boolean fsysdep_file_exists P((const char *zfile)); /* Start up a program. If the ffork argument is true, this should spawn a new process and return. If the ffork argument is false, this may either return or not. The three string arguments may be catenated together to form the program to execute; I did it this way to make it easy to call execl(2), and because I never needed more than two arguments. The program will always be "uucico" or "uuxqt". The return value should be TRUE on success, FALSE on error. */ extern boolean fsysdep_run P((boolean ffork, const char *zprogram, const char *zarg1, const char *zarg2)); /* Send a mail message. This function will be passed an array of strings. All necessary newlines are already included; the strings should simply be concatenated together to form the mail message. It should return FALSE on error, although the return value is often ignored. */ extern boolean fsysdep_mail P((const char *zto, const char *zsubject, int cstrs, const char **paz)); /* Get the time in seconds since some epoch. The actual epoch is unimportant, so long as the time values are consistent across program executions and the value is never negative. If the pimicros argument is not NULL, it should be set to the number of microseconds (if this is not available, *pimicros should be set to zero). */ extern long ixsysdep_time P((long *pimicros)); /* Get the time in seconds and microseconds (millionths of a second) since some epoch. The actual epoch is not important, and it may change in between program invocations; this is provided because on Unix the times function may be used. If microseconds can not be determined, *pimicros can just be set to zero. */ extern long ixsysdep_process_time P((long *pimicros)); /* Parse the value returned by ixsysdep_time into a struct tm. I assume that this structure is defined in . This is basically just localtime, except that the ANSI function takes a time_t which may not be what is returned by ixsysdep_time. */ extern void usysdep_localtime P((long itime, struct tm *q)); /* Sleep for a number of seconds. */ extern void usysdep_sleep P((int cseconds)); /* Pause for half a second, or 1 second if subsecond sleeps are not possible. */ extern void usysdep_pause P((void)); /* Lock a remote system. This should return FALSE if the system is already locked (no error should be reported). */ extern boolean fsysdep_lock_system P((const struct uuconf_system *qsys)); /* Unlock a remote system. This should return FALSE on error (although the return value is generally ignored). */ extern boolean fsysdep_unlock_system P((const struct uuconf_system *qsys)); /* Get the conversation sequence number for a remote system, and increment it for next time. This should return -1 on error. */ extern long ixsysdep_get_sequence P((const struct uuconf_system *qsys)); /* Get the status of a remote system. This should return FALSE on error. Otherwise it should set *qret to the status. If no status information is available, this should set *qret to sensible values and return TRUE. If pfnone is not NULL, then it should be set to TRUE if no status information was available or FALSE otherwise. */ extern boolean fsysdep_get_status P((const struct uuconf_system *qsys, struct sstatus *qret, boolean *pfnone)); /* Set the status of a remote system. This should return FALSE on error. The system will be locked before this call is made. */ extern boolean fsysdep_set_status P((const struct uuconf_system *qsys, const struct sstatus *qset)); /* See whether a remote system is permitted to log in. This is just to support the remote.unknown shell script for HDB. The zscript argument is the script name, as return by uuconf_remote_unknown. The zsystem argument is the name given by the remote system. If the system is not permitted to log in, this function should log an error and return FALSE. */ extern boolean fsysdep_unknown_caller P((const char *zscript, const char *zsystem)); /* Check whether there is work for a remote system. It should return TRUE if there is work, FALSE otherwise; there is no way to indicate an error. */ extern boolean fsysdep_has_work P((const struct uuconf_system *qsys)); /* Initialize the work scan. This will be called before fsysdep_get_work. The bgrade argument is the minimum grade of execution files that should be considered (e.g. a bgrade of 'd' will allow all grades from 'A' to 'Z' and 'a' to 'd'). The cmax argument is the maximum number of items to return in calls to fsysdep_get_work; a value of 0 means there is no limit. This function should return FALSE on error. */ extern boolean fsysdep_get_work_init P((const struct uuconf_system *qsys, int bgrade, unsigned int cmax)); /* Get the next command to be executed for a remote system. The bgrade and cmax arguments will be the same as for fsysdep_get_work_init; probably only one of these functions will use them, namely the function for which it is more convenient. This should return FALSE on error. The structure pointed to by qcmd should be filled in. The strings may point into a static buffer; they will be copied out if necessary. If there is no more work, this should set qcmd->bcmd to 'H' and return TRUE. This should set qcmd->pseq to something which can be passed to fsysdep_did_work to remove the job from the queue when it has been completed. This may set qcmd->bcmd to 'P' to represent a poll file; the main code will just pass the pseq element of such a structure to fsysdep_did_work if the system is called. */ extern boolean fsysdep_get_work P((const struct uuconf_system *qsys, int bgrade, unsigned int cmax, struct scmd *qcmd)); /* Remove a job from the work queue. This must also remove the temporary file used for a send command, if there is one. It should return FALSE on error. */ extern boolean fsysdep_did_work P((pointer pseq)); /* Save the temporary file for a send command. This function should return a string that will be put into a mail message. On success this string should say something like ``The file has been saved as ...''. On failure it could say something like ``The file could not be saved because ...''. If there is no temporary file, or for some reason it's not appropriate to include a message, this function should just return NULL. This function is used when a file send fails for some reason, to make sure that we don't completely lost the file. */ extern const char *zsysdep_save_temp_file P((pointer pseq)); /* Save a file in a location used to hold corrupt files. This is called if a bad execution file is found by uuxqt. This should return the new name of the file (allocated by zbufalc), or NULL if the move failed (in which the original file should remain). */ extern char *zsysdep_save_corrupt_file P((const char *zfile)); /* Save a file in a location used to hold failed execution files. This is called if a uuxqt execution fails. This should return the new name of the file (allocated by zbufalc), or NULL if the move failed (in which case the original file should remain). */ extern char *zsysdep_save_failed_file P((const char *zfile)); /* Cleanup anything left over by fsysdep_get_work_init and fsysdep_get_work. This may be called even though fsysdep_get_work_init has not been. */ extern void usysdep_get_work_free P((const struct uuconf_system *qsys)); /* Add a base name to a file if it is a directory. If zfile names a directory, then return a string naming a file within the directory with the base file name of zname. This should return NULL on error. */ extern char *zsysdep_add_base P((const char *zfile, const char *zname)); /* Get a file name from the spool directory. This should return NULL on error. The pseq argument is TRUE if the file was found from searching the work directory; this is, unfortunately, needed to support SVR4 spool directories. */ extern char *zsysdep_spool_file_name P((const struct uuconf_system *qsys, const char *zfile, pointer pseq)); /* Make necessary directories. This should create all non-existent directories for a file. If the fpublic argument is TRUE, anybody should be permitted to create and remove files in the directory; otherwise anybody can list the directory, but only the UUCP system can create and remove files. It should return FALSE on error. */ extern boolean fsysdep_make_dirs P((const char *zfile, boolean fpublic)); /* Create a stdio file, setting appropriate protection. If the fpublic argument is TRUE, the file is made publically accessible; otherwise it is treated as a private data file. If the fappend argument is TRUE, the file is opened in append mode; otherwise any previously existing file of the same name is removed. If the fmkdirs argument is TRUE, then any necessary directories should also be created. On a system in which file protections are unimportant and the necessary directories exist, this may be implemented as fopen (zfile, fappend ? "a" : "w"); */ extern FILE *esysdep_fopen P((const char *zfile, boolean fpublic, boolean fappend, boolean fmkdirs)); /* Open a file, using the access permission of the user who invoked the program. The frd argument is TRUE if the file should be opened for reading, and the fbinary argument is TRUE if the file should be opened as a binary file (this is ignored on Unix, since there all files are binary files). This returns an openfile_t, not a FILE *. This is supposed to be able to open a file even if it can not be read by the uucp user. This is not possible on some older Unix systems. */ extern openfile_t esysdep_user_fopen P((const char *zfile, boolean frd, boolean fbinary)); /* Open a file to send to another system; the qsys argument is the system the file is being sent to. If fcheck is TRUE, it should make sure that the file is readable by zuser (if zuser is NULL the file must be readable by anybody). This is to eliminate a window between fsysdep_in_directory and esysdep_open_send. If an error occurs, it should return EFILECLOSED. */ extern openfile_t esysdep_open_send P((const struct uuconf_system *qsys, const char *zname, boolean fcheck, const char *zuser)); /* Return a temporary file name to receive into. This file will be opened by esysdep_open_receive. The qsys argument is the system the file is coming from, the zto argument is the name the file will have after it has been fully received, the ztemp argument, if it is not NULL, is from the command sent by the remote system, and the frestart argument is TRUE if the protocol and remote system permit file transfers to be restarted. The return value must be freed using ubuffree. The function should return NULL on error. */ extern char *zsysdep_receive_temp P((const struct uuconf_system *qsys, const char *zfile, const char *ztemp, boolean frestart)); /* Open a file to receive from another system. The zreceive argument is the return value of zsysdep_receive_temp with the same qsys, zfile and ztemp arguments. If the function can determine that this file has already been partially received, it should set *pcrestart to the number of bytes that have been received. If the file has not been partially received, *pcrestart should be set to -1. pcrestart will be passed in as NULL if file restart is not supported by the protocol or the remote system. The function should return EFILECLOSED on error. After the file is written, fsysdep_move_file will be called to move the file to its final destination, and to set the correct file mode. */ extern openfile_t esysdep_open_receive P((const struct uuconf_system *qsys, const char *zto, const char *ztemp, const char *zreceive, long *pcrestart)); /* Move a file. This is used to move a received file to its final location. The zto argument is the file to create. The zorig argument is the name of the file to move. If fmkdirs is TRUE, then any necessary directories are created; fpublic indicates whether they should be publically writeable or not. If fcheck is TRUE, this should make sure the directory is writeable by the user zuser (if zuser is NULL, then it must be writeable by any user); this is to avoid a window of vulnerability between fsysdep_in_directory and fsysdep_move_file. This function should return FALSE on error, in which case the zorig file should still exist. */ extern boolean fsysdep_move_file P((const char *zorig, const char *zto, boolean fmkdirs, boolean fpublic, boolean fcheck, const char *zuser)); /* Change the mode of a file. The imode argument is a Unix mode. This should return FALSE on error. */ extern boolean fsysdep_change_mode P((const char *zfile, unsigned int imode)); /* Truncate a file which we are receiving into. This may be done by closing the original file, removing it and reopening it. This should return FALSE on error. */ extern openfile_t esysdep_truncate P((openfile_t e, const char *zname)); /* Sync a file to disk. If this fails it should log an error using the zmsg parameter, and return FALSE. This is controlled by the FSYNC_ON_CLOSE macro in policy.h. */ extern boolean fsysdep_sync P((openfile_t e, const char *zmsg)); /* It is possible for the acknowledgement of a received file to be lost. The sending system will then now know that the file was correctly received, and will send it again. This can be a problem particularly with protocols which support channels, since they may send several small files in a single window, all of which may be received correctly although the sending system never sees the acknowledgement. If these files involve an execution, the execution will happen twice, which will be bad. This function is called when a file is completely received. It is supposed to try and remember the reception, in case the connection is lost. It is passed the system, the file name to receive to, and the temporary file name from the sending system. It should return FALSE on error. */ extern boolean fsysdep_remember_reception P((const struct uuconf_system *qsys, const char *zto, const char *ztemp)); /* This function is called to see if a file has already been received successfully. It gets the same arguments as fsysdep_remember_reception. It should return TRUE if the file was already received, FALSE otherwise. There is no way to report error. */ extern boolean fsysdep_already_received P((const struct uuconf_system *qsys, const char *zto, const char *ztemp)); /* This function is called when it is no longer necessary to remember that a file has been received. This will be called when the protocol knows that the receive message has been acknowledged. It gets the same arguments as fsysdep_remember_reception. it should return FALSE on error. */ extern boolean fsysdep_forget_reception P((const struct uuconf_system *qsys, const char *zto, const char *ztemp)); /* Start expanding a wildcarded file name. This should return FALSE on error; otherwise subsequent calls to zsysdep_wildcard should return file names. */ extern boolean fsysdep_wildcard_start P((const char *zfile)); /* Get the next wildcard name. This should return NULL when there are no more names to return. The return value should be freed using ubuffree. The argument should be the same as that to fsysdep_wildcard_start. There is no way to return error. */ extern char *zsysdep_wildcard P((const char *zfile)); /* Finish getting wildcard names. This may be called before or after zsysdep_wildcard has returned NULL. It should return FALSE on error. */ extern boolean fsysdep_wildcard_end P((void)); /* Prepare to execute a bunch of file transfer requests. This should make an entry in the spool directory so that the next time uucico is started up it will transfer these files. The bgrade argument specifies the grade of the commands. The commands themselves are in the pascmds array, which has ccmds entries. The function should return NULL on error, or the jobid on success. The jobid is a string that may be printed or passed to fsysdep_kill_job and related functions, but is otherwise uninterpreted. If pftemp is not NULL, then on an error return, *pftemp will be TRUE for a temporary error, FALSE for a permanent error. */ extern char *zsysdep_spool_commands P((const struct uuconf_system *qsys, int bgrade, int ccmds, const struct scmd *pascmds, boolean *pftemp)); /* Get a file name to use for a data file to be copied to another system. The ztname, zdname and zxname arguments will all either be NULL or point to an array of CFILE_NAME_LEN characters in length. The ztname array should be set to a temporary file name that could be passed to zsysdep_spool_file_name to retrieve the return value of this function; this will be appropriate for the temporary name in a send request. The zdname array should be set to a data file name that is appropriate for the spool directory of the other system; this will be appropriate for the name of the destination file in a send request of a data file for an execution of some sort. The zxname array should be set to an execute file name that is appropriate for the other system. The zlocalname argument is the name of the local system as seen by the remote system, the bgrade argument is the grade, and fxqt is TRUE if this file is going to become an execution file. This should return NULL on error. */ #define CFILE_NAME_LEN (15) extern char *zsysdep_data_file_name P((const struct uuconf_system *qsys, const char *zlocalname, int bgrade, boolean fxqt, char *ztname, char *zdname, char *zxname)); /* Get a name for a local execute file. This is used by uux for a local command with remote files. Returns NULL on error. */ extern char *zsysdep_xqt_file_name P((void)); /* Beginning getting execute files. To get a list of execute files, first fsysdep_get_xqt_init is called, then zsysdep_get_xqt is called several times until it returns NULL, then finally usysdep_get_xqt_free is called. If the zsystem argument is not NULL, it is the name of a system for which execution files are desired. */ extern boolean fsysdep_get_xqt_init P((const char *zsystem)); /* Get the next execute file. This should return NULL when finished (with *pferr set to FALSE). The zsystem argument should be the same string as that passed to fsysdep_get_xqt_init. On an error this should return NULL with *pferr set to TRUE. This should set *pzsystem to the name of the system for which the execute file was created; this is not guaranteed to match the zsystem argument--that must be double checked by the caller. Both the return value and *pzsystem should be freed using ubuffree. */ extern char *zsysdep_get_xqt P((const char *zsystem, char **pzsystem, boolean *pferr)); /* Clean up after getting execute files. The zsystem argument should be the same string as that passed to fsysdep_get_xqt_init. */ extern void usysdep_get_xqt_free P((const char *zsystem)); /* Get the absolute pathname of a command to execute. This is given the legal list of commands (which may be the special case "ALL") and the path. It must return an absolute pathname to the command. If it gets an error it should set *pferr to TRUE and return NULL; if the command is not found it should set *pferr to FALSE and return NULL. */ extern char *zsysdep_find_command P((const char *zcmd, char **pzcmds, char **pzpath, boolean *pferr)); /* Expand file names for uuxqt. This exists because uuxqt on Unix has to expand file names which begin with a ~. It does not want to expand any other type of file name, and it turns a double ~ into a single one without expanding. If this returns NULL, the file does not need to be changed; otherwise it returns a zbufalc'ed string. There is no way to report error. */ extern char *zsysdep_xqt_local_file P((const struct uuconf_system *qsys, const char *zfile)); #if ! ALLOW_FILENAME_ARGUMENTS /* Check an argument to an execution command to make sure that it doesn't refer to a file name that may not be accessed. This should check the argument to see if it is a filename. If it is, it should either reject it out of hand or it should call fin_directory_list on the file with both qsys->zremote_receive and qsys->zremote_send. If the file is rejected, it should log an error and return FALSE. Otherwise it should return TRUE. */ extern boolean fsysdep_xqt_check_file P((const struct uuconf_system *qsys, const char *zfile)); #endif /* ! ALLOW_FILENAME_ARGUMENTS */ /* Run an execute file. The arguments are: qsys -- system for which execute file was created zuser -- user who requested execution pazargs -- list of arguments to command (element 0 is command) zfullcmd -- command and arguments stuck together in one string zinput -- file name for standard input (may be NULL) zoutput -- file name for standard output (may be NULL) fshell -- if TRUE, use /bin/sh to execute file ilock -- return value of ixsysdep_lock_uuxqt pzerror -- set to name of standard error file pftemp -- set to TRUE if error is temporary, FALSE otherwise If fshell is TRUE, the command should be executed with /bin/sh (obviously, this can only really be done on Unix systems). If an error occurs this should return FALSE and set *pftemp appropriately. *pzerror should be freed using ubuffree. */ extern boolean fsysdep_execute P((const struct uuconf_system *qsys, const char *zuser, const char **pazargs, const char *zfullcmd, const char *zinput, const char *zoutput, boolean fshell, int ilock, char **pzerror, boolean *pftemp)); /* Lock for uuxqt execution. If the cmaxuuxqts argument is not zero, this should make sure that no more than cmaxuuxqts uuxqt processes are running at once. Also, only one uuxqt may execute a particular command (specified by the -c option) at a time. If zcmd is not NULL, it is a command that must be locked. This should return a nonnegative number which will be passed to other routines, including fsysdep_unlock_uuxqt, or -1 on error. */ extern int ixsysdep_lock_uuxqt P((const char *zcmd, int cmaxuuxqts)); /* Unlock a uuxqt process. This is passed the return value of ixsysdep_lock_uuxqt, as well as the arguments passed to ixsysdep_lock_uuxqt. It may return FALSE on error, but at present the return value is ignored. */ extern boolean fsysdep_unlock_uuxqt P((int iseq, const char *zcmd, int cmaxuuxqts)); /* See whether a particular uuxqt command is locked. This should return TRUE if the command is locked (because ixsysdep_lock_uuxqt was called with it as an argument), FALSE otherwise. There is no way to return error. */ extern boolean fsysdep_uuxqt_locked P((const char *zcmd)); /* Lock an execute file in order to execute it. This should return FALSE if the execute file is already locked. There is no way to return error. */ extern boolean fsysdep_lock_uuxqt_file P((const char *zfile)); /* Unlock an execute file. This should return FALSE on error. */ extern boolean fsysdep_unlock_uuxqt_file P((const char *zfile)); /* Lock the execution directory. The ilock argument is the return value of ixsysdep_lock_uuxqt. This should return FALSE if the directory is already locked. There is no way to return error. */ extern boolean fsysdep_lock_uuxqt_dir P((int ilock)); /* Remove all files in the execution directory, and unlock it. This should return FALSE on error. */ extern boolean fsysdep_unlock_uuxqt_dir P((int ilock)); /* Copy files into the execution directory indicated by ilock. The code will already have checked that all the files exist. The copying may be done using the link system call. The elements in the pzfrom array will be complete filenames. The elements in the pzto array will be either NULL (in which case the file should not be copied) or simple base names. If pzinput and *pzinput are not NULL, then *pzinput is the name of the standard input file; if it is the same as any element of pzfrom, then *pzinput will be set to the zbufcpy of the full path for the corresponding pzto value, if any. */ extern boolean fsysdep_copy_uuxqt_files P((int cfiles, const char *const *pzfrom, const char *const *pzto, int ilock, char **pzinput)); /* Expand a file name on the local system, defaulting to the current directory. This is just like zsysdep_local_file, except that relative files are placed in the working directory the program started in rather than in the public directory. This should return NULL on error. */ extern char *zsysdep_local_file_cwd P((const char *zname, const char *zpubdir, boolean *pfbadname)); /* Add the working directory to a file name. The named file is actually on a remote system. If the file already has a directory, it should not be changed. This should return NULL on error. */ extern char *zsysdep_add_cwd P((const char *zfile)); /* See whether a file name will need the current working directory when zsysdep_local_file_cwd or zsysdep_add_cwd is called on it. This will be called before usysdep_initialize. It should just check whether the argument is an absolute path. See the comment above usysdep_initialize in this file for an explanation of why things are done this way. */ extern boolean fsysdep_needs_cwd P((const char *zfile)); /* Get the base name of a file. The file will be a local file name, and this function should return the base file name, ideally in a form which will make sense on most systems; it will be used if the destination of a uucp is a directory. */ extern char *zsysdep_base_name P((const char *zfile)); /* Return a filename within a directory. */ extern char *zsysdep_in_dir P((const char *zdir, const char *zfile)); /* Get the mode of a file. This should return a Unix style file mode. It should return 0 on error. */ extern unsigned int ixsysdep_file_mode P((const char *zfile)); /* Get the mode of a file using the permissions of the user who invoked the program. This should return a Unix style file mode. It should return 0 on error. */ extern unsigned int ixsysdep_user_file_mode P((const char *zfile)); /* See whether the user has access to a file. This is called by uucp and uux to prevent copying of a file which uucp can read but the user cannot. If access is denied, this should log an error message and return FALSE. */ extern boolean fsysdep_access P((const char *zfile)); /* See whether the daemon has access to a file. This is called by uucp and uux when a file is queued up for transfer without being copied into the spool directory. It is merely an early error check, as the daemon would of course discover the error itself when it tried the transfer. If access would be denied, this should log an error message and return FALSE. */ extern boolean fsysdep_daemon_access P((const char *zfile)); /* Translate a destination from system!user to a place in the public directory where uupick will get the file. On Unix this produces system!~/receive/user/localname, and that's probably what it has to produce on any other system as well. Returns NULL on a usage error, or otherwise returns string allocated by zbufcpy. */ extern char *zsysdep_uuto P((const char *zdest, const char *zlocalname)); /* Return TRUE if a pathname exists and is a directory. */ extern boolean fsysdep_directory P((const char *zpath)); /* Walk a directory tree. The zdir argument is the directory to walk. The pufn argument is a function to call on each regular file in the tree. The first argument to pufn should be the full filename; the second argument to pufn should be the filename relative to zdir; the third argument to pufn should be the pinfo argument to usysdep_walk_tree. The usysdep_walk_tree function should return FALSE on error. */ extern boolean usysdep_walk_tree P((const char *zdir, void (*pufn) P((const char *zfull, const char *zrelative, pointer pinfo)), pointer pinfo)); /* Return the jobid of a work file, given the sequence value. On error this should log an error and return NULL. The jobid is a string which may be printed out and read in and passed to fsysdep_kill_job, etc., but is not otherwise interpreted. */ extern char *zsysdep_jobid P((const struct uuconf_system *qsys, pointer pseq)); /* See whether the current user is privileged. Privileged users are permitted to kill jobs submitted by another user, and they are permitted to use the -u argument to uucico; other uses of this call may be added later. This should return TRUE if permission is granted, FALSE otherwise. */ extern boolean fsysdep_privileged P((void)); /* Kill a job, given the jobid. This should remove all associated files and in general eliminate the job completely. On error it should log an error message and return FALSE. */ extern boolean fsysdep_kill_job P((pointer puuconf, const char *zjobid)); /* Rejuvenate a job, given the jobid. If possible, this should update the time associated with the job such that it will not be eliminated by uustat -K or similar programs that check the creation time. This should affect the return value of ixsysdep_work_time. On error it should log an error message and return FALSE. */ extern boolean fsysdep_rejuvenate_job P((pointer puuconf, const char *zjobid)); /* Get the time a job was queued, given the sequence number. There is no way to indicate error. The return value must use the same epoch as ixsysdep_time. */ extern long ixsysdep_work_time P((const struct uuconf_system *qsys, pointer pseq)); /* Get the time a file was created. This is called by uustat on execution files. There is no way to indicate error. The return value must use the same epoch as ixsysdep_time. */ extern long ixsysdep_file_time P((const char *zfile)); /* Touch a file to make it appear as though it was created at the current time. This is called by uustat on execution files. On error this should log an error message and return FALSE. */ extern boolean fsysdep_touch_file P((const char *zfile)); /* Get the size in bytes of a file. If this file does not exist, this should not give an error message, but should return -1. If some other error occurs, this should return -2. */ extern long csysdep_size P((const char *zfile)); /* Return the amount of free space on the containing the given file name (the file may or may not exist). If the amount of free space cannot be determined, the function should return -1. */ extern long csysdep_bytes_free P((const char *zfile)); /* Start getting status information for all systems with available status information. There may be status information for unknown systems, which is why this series of functions is used. The phold argument is used to pass information around, to possibly avoid the use of static variables. On error this should log an error and return FALSE. */ extern boolean fsysdep_all_status_init P((pointer *phold)); /* Get status information for the next system. This should return the system name and fill in the qstat argument. The phold argument will be that set by fsysdep_all_status_init. On error this should log an error, set *pferr to TRUE, and return NULL. */ extern char *zsysdep_all_status P((pointer phold, boolean *pferr, struct sstatus *qstat)); /* Free up anything allocated by fsysdep_all_status_init and zsysdep_all_status. The phold argument is that set by fsysdep_all_status_init. */ extern void usysdep_all_status_free P((pointer phold)); /* Display the process status of all processes holding lock files. This is uustat -p. The return value is passed to usysdep_exit. */ extern boolean fsysdep_lock_status P((void)); /* Return TRUE if the user has legitimate access to the port. This is used by cu to control whether the user can open a port directly, rather than merely being able to dial out on it. Opening a port directly allows the modem to be reprogrammed. */ extern boolean fsysdep_port_access P((struct uuconf_port *qport)); /* Return whether the given port could be named by the given line. On Unix, the line argument would be something like "ttyd0", and this function should return TRUE if the named port is "/dev/ttyd0". */ extern boolean fsysdep_port_is_line P((struct uuconf_port *qport, const char *zline)); /* Set the terminal into raw mode. In this mode no input characters should be treated specially, and characters should be made available as they are typed. The original terminal mode should be saved, so that it can be restored by fsysdep_terminal_restore. If flocalecho is TRUE, then local echoing should still be done; otherwise echoing should be disabled. This function returns FALSE on error. */ extern boolean fsysdep_terminal_raw P((boolean flocalecho)); /* Restore the terminal back to the original setting, before fsysdep_terminal_raw was called. Returns FALSE on error. */ extern boolean fsysdep_terminal_restore P((void)); /* Read a line from the terminal. The fsysdep_terminal_raw function will have been called. This should print the zprompt argument (unless it is NULL) and return the line, allocated by zbufcpy, or NULL on error. */ extern char *zsysdep_terminal_line P((const char *zprompt)); /* Write a line to the terminal, ending with a newline. This is basically just puts (zline, stdout), except that the terminal will be in raw mode, so on ASCII Unix systems the line needs to end with \r\n. */ extern boolean fsysdep_terminal_puts P((const char *zline)); /* If faccept is TRUE, permit the user to generate signals from the terminal. If faccept is FALSE, turn signals off again. After fsysdep_terminal_raw is called, signals should be off. Return FALSE on error. */ extern boolean fsysdep_terminal_signals P((boolean faccept)); /* The cu program expects the system dependent code to handle the details of copying data from the communications port to the terminal. This should be set up by fsysdep_cu_init, and done while fsysdep_cu is called. It is permissible to do it on a continual basis (on Unix a subprocess handles it) so long as the copying can be stopped by the fsysdep_cu_copy function. The fsysdep_cu_init function does any system dependent initialization needed for this. */ extern boolean fsysdep_cu_init P((struct sconnection *qconn)); /* Copy all data from the communications port to the terminal, and all data from the terminal to the communications port. Keep this up until the escape character *zCuvar_escape is seen. Set *pbcmd to the character following the escape character; after the escape character, zlocalname should be printed, possibly after a delay. If two escape characters are entered in sequence, this function should send a single escape character to the port, and not return. Returns FALSE on error. */ extern boolean fsysdep_cu P((struct sconnection *qconn, char *pbcmd, const char *zlocalname)); /* If fcopy is TRUE, start copying data from the communications port to the terminal. If fcopy is FALSE, stop copying data. This function may be called several times during a cu session. It should return FALSE on error. */ extern boolean fsysdep_cu_copy P((boolean fcopy)); /* Stop copying data from the communications port to the terminal, and generally clean up after fsysdep_cu_init and fsysdep_cu. Returns FALSE on error. */ extern boolean fsysdep_cu_finish P((void)); /* Run a shell command. If zcmd is NULL, or *zcmd == '\0', just start up a shell. The second argument is one of the following values. This should return FALSE on error. */ enum tshell_cmd { /* Attach stdin and stdout to the terminal. */ SHELL_NORMAL, /* Attach stdout to the communications port, stdin to the terminal. */ SHELL_STDOUT_TO_PORT, /* Attach stdin to the communications port, stdout to the terminal. */ SHELL_STDIN_FROM_PORT, /* Attach both stdin and stdout to the communications port. */ SHELL_STDIO_ON_PORT }; extern boolean fsysdep_shell P((struct sconnection *qconn, const char *zcmd, enum tshell_cmd tcmd)); /* Change directory. If zdir is NULL, or *zdir == '\0', change to the user's home directory. Return FALSE on error. */ extern boolean fsysdep_chdir P((const char *zdir)); /* Suspend the current process. This is only expected to work on Unix versions that support SIGTSTP. In general, people can just shell out. */ extern boolean fsysdep_suspend P((void)); /* Start getting files for uupick. The zsystem argument may be NULL to get files from all systems, or it may specify a particular system. The zpubdir argument is the public directory to use. This returns FALSE on error. */ extern boolean fsysdep_uupick_init P((const char *zsystem, const char *zpubdir)); /* Get the next file for uupick. This returns the basic file name. It sets *pzfull to the full name, and *pzfrom to the name of the system which sent this file over; both should be freed using ubuffree. *pzfull should be passed to ubuffree after it is no longer needed. The zsystem and zpubdir arguments should be the same as the arguments to fsysdep_uupick_init. This returns NULL when all files been returned. */ extern char *zsysdep_uupick P((const char *zsystem, const char *zpubdir, char **pzfrom, char **pzfull)); /* Clean up after getting files for uupick. */ extern boolean fsysdep_uupick_free P((const char *zsystem, const char *zpubdir)); /* Translate a local file name for uupick. On Unix this is just like zsysdep_local_file_cwd except that a file beginning with ~/ is placed in the user's home directory rather than in the public directory. */ extern char *zsysdep_uupick_local_file P((const char *zfile, boolean *pfbadname)); /* Remove a directory and all the files in it. */ extern boolean fsysdep_rmdir P((const char *zdir)); #endif /* ! defined (SYSTEM_H) */ uucp-1.07/sysdep.h0000664000076400007640000004000107665321756007604 /* sysdep.h -*- C -*- The header file for the UNIX system dependent routines. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #ifndef SYSDEP_H #define SYSDEP_H #if ANSI_C /* These structures are used in prototypes but are not defined in this header file. */ struct uuconf_system; struct sconnection; #endif /* SCO, SVR4 and Sequent lockfiles are basically just like HDB lockfiles. */ #if HAVE_SCO_LOCKFILES || HAVE_SVR4_LOCKFILES || HAVE_SEQUENT_LOCKFILES #undef HAVE_HDB_LOCKFILES #define HAVE_HDB_LOCKFILES 1 #endif #if HAVE_BSD_TTY + HAVE_SYSV_TERMIO + HAVE_POSIX_TERMIOS != 1 #error Terminal driver define not set or duplicated #endif #if SPOOLDIR_V2 + SPOOLDIR_BSD42 + SPOOLDIR_BSD43 + SPOOLDIR_HDB + SPOOLDIR_ULTRIX + SPOOLDIR_SVR4 + SPOOLDIR_TAYLOR != 1 #error Spool directory define not set or duplicated #endif /* If setreuid is broken, don't use it. */ #if HAVE_BROKEN_SETREUID #undef HAVE_SETREUID #define HAVE_SETREUID 0 #endif /* Get some standard types from the configuration header file. */ #ifdef PID_T typedef PID_T pid_t; #endif #ifdef UID_T typedef UID_T uid_t; #endif #ifdef GID_T typedef GID_T gid_t; #endif #ifdef OFF_T typedef OFF_T off_t; #endif /* On Unix, binary files are the same as text files. */ #define BINREAD "r" #define BINWRITE "w" /* If we have sigaction, we can force system calls to not be restarted. */ #if HAVE_SIGACTION #undef HAVE_RESTARTABLE_SYSCALLS #define HAVE_RESTARTABLE_SYSCALLS 0 #endif /* If we have sigvec, and we have HAVE_SIGVEC_SV_FLAGS, and SV_INTERRUPT is defined, we can force system calls to not be restarted (signal.h is included by uucp.h before this point, so SV_INTERRUPT will be defined by now if it it ever is). */ #if HAVE_SIGVEC && HAVE_SIGVEC_SV_FLAGS #ifdef SV_INTERRUPT #undef HAVE_RESTARTABLE_SYSCALLS #define HAVE_RESTARTABLE_SYSCALLS 0 #endif #endif /* If we were cross-configured, we will have a value of -1 for HAVE_RESTARTABLE_SYSCALLS. In this case, we try to guess what the correct value should be. Yuck. If we have sigvec, but neither of the above cases applied (which we know because they would have changed HAVE_RESTARTABLE_SYSCALLS) then we are probably on 4.2BSD and system calls are automatically restarted. Otherwise, assume that they are not. */ #if HAVE_RESTARTABLE_SYSCALLS == -1 #undef HAVE_RESTARTABLE_SYSCALLS #if HAVE_SIGVEC #define HAVE_RESTARTABLE_SYSCALLS 1 #else #define HAVE_RESTARTABLE_SYSCALLS 0 #endif #endif /* HAVE_RESTARTABLE_SYSCALLS == -1 */ /* We don't handle sigset in combination with restartable system calls, so we check for it although this combination will never happen. */ #if ! HAVE_SIGACTION && ! HAVE_SIGVEC && HAVE_SIGSET #if HAVE_RESTARTABLE_SYSCALLS #undef HAVE_SIGSET #define HAVE_SIGSET 0 #endif #endif /* If we don't have restartable system calls, we can ignore fsysdep_catch, usysdep_start_catch and usysdep_end_catch. Otherwise fsysdep_catch has to do a setjmp. */ #if ! HAVE_RESTARTABLE_SYSCALLS #define fsysdep_catch() (TRUE) #define usysdep_start_catch() #define usysdep_end_catch() #define CATCH_PROTECT #else /* HAVE_RESTARTABLE_SYSCALLS */ #if HAVE_SETRET && ! HAVE_SIGSETJMP #include #define setjmp setret #define longjmp longret #define jmp_buf ret_buf #else /* ! HAVE_SETRET || HAVE_SIGSETJMP */ #include #if HAVE_SIGSETJMP #undef setjmp #undef longjmp #undef jmp_buf #define setjmp(s) sigsetjmp ((s), TRUE) #define longjmp siglongjmp #define jmp_buf sigjmp_buf #endif /* HAVE_SIGSETJMP */ #endif /* ! HAVE_SETRET || HAVE_SIGSETJMP */ extern volatile sig_atomic_t fSjmp; extern volatile jmp_buf sSjmp_buf; #define fsysdep_catch() (setjmp (sSjmp_buf) == 0) #define usysdep_start_catch() (fSjmp = TRUE) #define usysdep_end_catch() (fSjmp = FALSE) #define CATCH_PROTECT volatile #endif /* HAVE_RESTARTABLE_SYSCALLS */ /* Get definitions for the terminal driver. */ #if HAVE_BSD_TTY #include struct sbsd_terminal { struct sgttyb stty; struct tchars stchars; struct ltchars sltchars; }; typedef struct sbsd_terminal sterminal; #define fgetterminfo(o, q) \ (ioctl ((o), TIOCGETP, &(q)->stty) == 0 \ && ioctl ((o), TIOCGETC, &(q)->stchars) == 0 \ && ioctl ((o), TIOCGLTC, &(q)->sltchars) == 0) #define fsetterminfo(o, q) \ (ioctl ((o), TIOCSETN, &(q)->stty) == 0 \ && ioctl ((o), TIOCSETC, &(q)->stchars) == 0 \ && ioctl ((o), TIOCSLTC, &(q)->sltchars) == 0) #define fsetterminfodrain(o, q) \ (ioctl ((o), TIOCSETP, &(q)->stty) == 0 \ && ioctl ((o), TIOCSETC, &(q)->stchars) == 0 \ && ioctl ((o), TIOCSLTC, &(q)->sltchars) == 0) #endif /* HAVE_BSD_TTY */ #if HAVE_SYSV_TERMIO #include typedef struct termio sterminal; #define fgetterminfo(o, q) (ioctl ((o), TCGETA, (q)) == 0) #define fsetterminfo(o, q) (ioctl ((o), TCSETA, (q)) == 0) #define fsetterminfodrain(o, q) (ioctl ((o), TCSETAW, (q)) == 0) #endif /* HAVE_SYSV_TERMIO */ #if HAVE_POSIX_TERMIOS #include typedef struct termios sterminal; #define fgetterminfo(o, q) (tcgetattr ((o), (q)) == 0) #define fsetterminfo(o, q) (tcsetattr ((o), TCSANOW, (q)) == 0) #define fsetterminfodrain(o, q) (tcsetattr ((o), TCSADRAIN, (q)) == 0) /* On some systems it is not possible to include both and in the same source files; I don't really know why. On such systems, we pretend that we don't have . */ #if ! HAVE_TERMIOS_AND_SYS_IOCTL_H #undef HAVE_SYS_IOCTL_H #define HAVE_SYS_IOCTL_H 0 #endif #endif /* HAVE_POSIX_TERMIOS */ /* The root directory (this is needed by the system independent stuff as the default for local-send). */ #define ZROOTDIR "/" /* The name of the execution directory within the spool directory (this is need by the system independent uuxqt.c). */ #define XQTDIR ".Xqtdir" /* The name of the directory in which we preserve file transfers that failed. */ #define PRESERVEDIR ".Preserve" /* The name of the directory to which we move corrupt files. */ #define CORRUPTDIR ".Corrupt" /* The name of the directory to which we move failed execution files. */ #define FAILEDDIR ".Failed" /* The length of the sequence number used in a file name. */ #define CSEQLEN (4) /* Get some standard definitions. Avoid including the files more than once--some might have been included by uucp.h. */ #if USE_STDIO && HAVE_UNISTD_H #include #endif #if ! USE_TYPES_H #include #endif #include /* Get definitions for the file permission bits. */ #ifndef S_IRWXU #define S_IRWXU 0700 #endif #ifndef S_IRUSR #define S_IRUSR 0400 #endif #ifndef S_IWUSR #define S_IWUSR 0200 #endif #ifndef S_IXUSR #define S_IXUSR 0100 #endif #ifndef S_IRWXG #define S_IRWXG 0070 #endif #ifndef S_IRGRP #define S_IRGRP 0040 #endif #ifndef S_IWGRP #define S_IWGRP 0020 #endif #ifndef S_IXGRP #define S_IXGRP 0010 #endif #ifndef S_IRWXO #define S_IRWXO 0007 #endif #ifndef S_IROTH #define S_IROTH 0004 #endif #ifndef S_IWOTH #define S_IWOTH 0002 #endif #ifndef S_IXOTH #define S_IXOTH 0001 #endif #if STAT_MACROS_BROKEN #undef S_ISDIR #endif #ifndef S_ISDIR #ifdef S_IFDIR #define S_ISDIR(i) (((i) & S_IFMT) == S_IFDIR) #else /* ! defined (S_IFDIR) */ #define S_ISDIR(i) (((i) & 0170000) == 040000) #endif /* ! defined (S_IFDIR) */ #endif /* ! defined (S_ISDIR) */ /* We need the access macros. */ #ifndef R_OK #define R_OK 4 #define W_OK 2 #define X_OK 1 #define F_OK 0 #endif /* ! defined (R_OK) */ /* We create files with these modes (should this be configurable?). */ #define IPRIVATE_FILE_MODE (S_IRUSR | S_IWUSR) #define IPUBLIC_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) /* We create directories with this mode (should this be configurable?). */ #define IDIRECTORY_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) #define IPUBLIC_DIRECTORY_MODE (S_IRWXU | S_IRWXG | S_IRWXO) #if ! HAVE_OPENDIR /* Define some structures to use if we don't have opendir, etc. These will only work if we have the old Unix filesystem, with a 2 byte inode and a 14 byte filename. */ #include struct dirent { char d_name[DIRSIZ + 1]; }; typedef struct { int o; struct dirent s; } DIR; extern DIR *opendir P((const char *zdir)); extern struct dirent *readdir P((DIR *)); extern int closedir P((DIR *)); #endif /* ! HAVE_OPENDIR */ #if ! HAVE_FTW_H /* If there is no , define the ftw constants. */ #define FTW_F (0) #define FTW_D (1) #define FTW_DNR (2) #define FTW_NS (3) #endif /* ! HAVE_FTW_H */ /* This structure holds the system dependent information we keep for a connection. This is used by the TCP and TLI code. */ struct ssysdep_conn { /* File descriptor. */ int o; /* File descriptor to read from (used by stdin and pipe port types). */ int ord; /* File descriptor to write to (used by stdin and pipe port types). */ int owr; /* Device name. */ char *zdevice; /* File status flags. */ int iflags; /* File status flags for write descriptor (-1 if not used). */ int iwr_flags; /* Hold the real descriptor when using a dialer device. */ int ohold; /* TRUE if this is a terminal and the remaining fields are valid. */ boolean fterminal; /* TRUE if this is a TLI descriptor. */ boolean ftli; /* Baud rate. */ long ibaud; /* Original terminal settings. */ sterminal sorig; /* Current terminal settings. */ sterminal snew; /* Process ID of currently executing pipe command, or parent process of forked TCP or TLI server, or -1. */ pid_t ipid; #if HAVE_COHERENT_LOCKFILES /* On Coherent we need to hold on to the real port name which will be used to enable the port. Ick. */ char *zenable; #endif }; /* These functions do I/O and chat scripts to a port. They are called by the TCP and TLI routines. */ extern boolean fsysdep_conn_read P((struct sconnection *qconn, char *zbuf, size_t *pclen, size_t cmin, int ctimeout, boolean freport)); extern boolean fsysdep_conn_write P((struct sconnection *qconn, const char *zbuf, size_t clen)); extern boolean fsysdep_conn_io P((struct sconnection *qconn, const char *zwrite, size_t *pcwrite, char *zread, size_t *pcread)); extern boolean fsysdep_conn_chat P((struct sconnection *qconn, char **pzprog)); /* Set a signal handler. */ extern void usset_signal P((int isig, RETSIGTYPE (*pfn) P((int)), boolean fforce, boolean *pfignored)); /* Default signal handler. This sets the appropriate element of the afSignal array. If system calls are automatically restarted, it may do a longjmp to an fsysdep_catch. */ extern RETSIGTYPE ussignal P((int isig)); /* Try to fork, repeating several times. */ extern pid_t ixsfork P((void)); /* Spawn a job. Returns the process ID of the spawned job or -1 on error. The following macros may be passed in aidescs. */ /* Set descriptor to /dev/null. */ #define SPAWN_NULL (-1) /* Set element of aidescs to a pipe for caller to read from. */ #define SPAWN_READ_PIPE (-2) /* Set element of aidescs to a pipe for caller to write to. */ #define SPAWN_WRITE_PIPE (-3) extern pid_t ixsspawn P((const char **pazargs, int *aidescs, boolean fkeepuid, boolean fkeepenv, const char *zchdir, boolean fnosigs, boolean fshell, const char *zpath, const char *zuu_machine, const char *zuu_user)); /* Do a form of popen using ixsspawn. */ extern FILE *espopen P((const char **pazargs, boolean frd, pid_t *pipid)); /* Wait for a particular process to finish, returning the exit status. The process ID should be pid_t, but we can't put that in a prototype. */ extern int ixswait P((unsigned long ipid, const char *zreport)); /* Read from a connection using two file descriptors. */ extern boolean fsdouble_read P((struct sconnection *qconn, char *zbuf, size_t *pclen, size_t cmin, int ctimeout, boolean freport)); /* Write to a connection using two file descriptors. */ extern boolean fsdouble_write P((struct sconnection *qconn, const char *zbuf, size_t clen)); /* Run a chat program on a connection using two file descriptors. */ extern boolean fsdouble_chat P((struct sconnection *qconn, char **pzprog)); /* Find a spool file in the spool directory. For a local file, the bgrade argument is the grade of the file. This is needed for SPOOLDIR_SVR4. */ extern char *zsfind_file P((const char *zsimple, const char *zsystem, int bgrade)); /* Return the grade given a sequence number. */ extern int bsgrade P((pointer pseq)); /* Lock a string. */ extern boolean fsdo_lock P((const char *, boolean fspooldir, boolean *pferr)); /* Unlock a string. */ extern boolean fsdo_unlock P((const char *, boolean fspooldir)); /* Check access for a particular user name, or NULL to check access for any user. */ extern boolean fsuser_access P((const struct stat *, int imode, const char *zuser)); /* Switch to the permissions of the invoking user. This sets the arguments to values to pass to fsuucp_perms. */ extern boolean fsuser_perms P((uid_t *, gid_t *)); /* Switch back to the permissions of the UUCP user ID. This should be passed the values returned by fsuser_perms in its arguments. */ extern boolean fsuucp_perms P((long, long)); /* Stick two directories and a file name together. */ extern char *zsappend3 P((const char *zdir1, const char *zdir2, const char *zfile)); /* Stick three directories and a file name together. */ extern char *zsappend4 P((const char *zdir1, const char *zdir2, const char *zdir3, const char *zfile)); /* Get a temporary file name. */ extern char *zstemp_file P((const struct uuconf_system *qsys)); /* Get a command file name. */ extern char *zscmd_file P((const struct uuconf_system *qsys, int bgrade)); /* Get a jobid from a system, a file name, and a grade. */ extern char *zsfile_to_jobid P((const struct uuconf_system *qsys, const char *zfile, int bgrade)); /* Get a file name from a jobid. This also returns the associated system in *pzsystem and the grade in *pbgrade. */ extern char *zsjobid_to_file P((const char *zid, char **pzsystem, char *pbgrade)); /* See whether there is a spool directory for a system when using SPOOLDIR_ULTRIX. */ extern boolean fsultrix_has_spool P((const char *zsystem)); #if HAVE_COHERENT_LOCKFILES /* Lock a coherent tty. */ extern boolean lockttyexist P((const char *z)); extern boolean fscoherent_disable_tty P((const char *zdevice, char **pzenable)); #endif /* Some replacements for standard Unix functions. */ #if ! HAVE_DUP2 extern int dup2 P((int oold, int onew)); #endif #if ! HAVE_FTW extern int ftw P((const char *zdir, int (*pfn) P((const char *zfile, const struct stat *qstat, int iflag)), int cdescriptors)); #endif #if ! HAVE_GETCWD && ! HAVE_GETWD extern char *getcwd P((char *zbuf, size_t cbuf)); #endif #if ! HAVE_MKDIR extern int mkdir P((const char *zdir, int imode)); #endif #if ! HAVE_RENAME extern int rename P((const char *zold, const char *znew)); #endif #if ! HAVE_RMDIR extern int rmdir P((const char *zdir)); #endif /* The working directory from which the program was run (this is set by usysdep_initialize if called with INIT_GETCWD). */ extern char *zScwd; /* The spool directory name. */ extern const char *zSspooldir; /* The lock directory name. */ extern const char *zSlockdir; /* The local UUCP name (needed for some spool directory stuff). */ extern const char *zSlocalname; #endif /* ! defined (SYSDEP_H) */ uucp-1.07/getopt.h0000664000076400007640000001060507665321755007605 /* Declarations for getopt. Copyright (C) 1989, 1990, 1991, 1992 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. This file was modified slightly by Ian Lance Taylor, November 1992, for Taylor UUCP, and again in June, 1995. */ #ifndef _GETOPT_H #define _GETOPT_H 1 #ifdef __cplusplus extern "C" { #endif /* Ian Lance Taylor added the following defines for Taylor UUCP. This avoids reported conflicts with system getopt definitions. */ #define getopt gnu_getopt #define optarg gnu_optarg #define optind gnu_optind #define opterr gnu_opterr /* 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 EOF, 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; /* 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'. */ enum _argtype { no_argument, required_argument, optional_argument }; #ifndef P /* On some systems, includes getopt.h before P is defined by uucp.h, and the -I arguments cause this version of getopt.h to be included. Work around that here. */ #define P(x) () #define UNDEFINE_P #endif extern int getopt P((int argc, char *const *argv, const char *shortopts)); extern int getopt_long P((int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind)); extern int getopt_long_only P((int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind)); /* Internal only. Users should not call this directly. */ extern int _getopt_internal P((int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind, int long_only)); #ifdef UNDEFINE_P #undef P #undef UNDEFINE_P #endif #ifdef __cplusplus } #endif #endif /* _GETOPT_H */ uucp-1.07/tstuu.c0000664000076400007640000010177307665321756007472 /* tstuu.c Test the uucp package on a UNIX system. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char tstuu_rcsid[] = "$Id: tstuu.c,v 1.89 2002/03/05 19:10:41 ian Rel $"; #endif #include "sysdep.h" #include "system.h" #include "getopt.h" #include #include #include #if HAVE_SYS_TIMES_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_SELECT #if HAVE_SYS_TIME_H #include #endif #if HAVE_SYS_SELECT_H #include #endif #endif #if HAVE_POLL #if HAVE_STROPTS_H #include #endif #if HAVE_POLL_H #include #endif #endif #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #if HAVE_TIME_H #if ! HAVE_SYS_TIME_H || ! HAVE_SELECT || TIME_WITH_SYS_TIME #include #endif #endif #if HAVE_SYS_WAIT_H #include #endif #if HAVE_UNION_WAIT typedef union wait wait_status; #else typedef int wait_status; #endif #if HAVE_STREAMS_PTYS #include extern char *ptsname (); #endif /* Get definitions for both O_NONBLOCK and O_NDELAY. */ #ifndef O_NDELAY #ifdef FNDELAY #define O_NDELAY FNDELAY #else /* ! defined (FNDELAY) */ #define O_NDELAY 0 #endif /* ! defined (FNDELAY) */ #endif /* ! defined (O_NDELAY) */ #ifndef O_NONBLOCK #ifdef FNBLOCK #define O_NONBLOCK FNBLOCK #else /* ! defined (FNBLOCK) */ #define O_NONBLOCK 0 #endif /* ! defined (FNBLOCK) */ #endif /* ! defined (O_NONBLOCK) */ #if O_NDELAY == 0 && O_NONBLOCK == 0 #error No way to do nonblocking I/O #endif /* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */ #ifndef EAGAIN #ifndef EWOULDBLOCK #define EAGAIN (-1) #define EWOULDBLOCK (-1) #else /* defined (EWOULDBLOCK) */ #define EAGAIN EWOULDBLOCK #endif /* defined (EWOULDBLOCK) */ #else /* defined (EAGAIN) */ #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif /* ! defined (EWOULDBLOCK) */ #endif /* defined (EAGAIN) */ #ifndef ENODATA #define ENODATA EAGAIN #endif /* Make sure we have a CLK_TCK definition, even if it makes no sense. This is in case TIMES_TICK is defined as CLK_TCK. */ #ifndef CLK_TCK #define CLK_TCK (60) #endif /* Don't try too hard to get a TIMES_TICK value; it doesn't matter that much. */ #if TIMES_TICK == 0 #undef TIMES_TICK #define TIMES_TICK CLK_TCK #endif #if TIMES_DECLARATION_OK extern long times (); #endif #ifndef SIGCHLD #define SIGCHLD SIGCLD #endif #if 1 #define ZUUCICO_CMD "login uucp" #define UUCICO_EXECL "/bin/login", "login", "uucp" #else #define ZUUCICO_CMD "su - nuucp" #define UUCICO_EXECL "/bin/su", "su", "-", "nuucp" #endif #if ! HAVE_SELECT && ! HAVE_POLL #error You need select or poll #endif #if ! HAVE_REMOVE #undef remove #define remove unlink #endif /* Buffer chain to hold data read from a uucico. */ #define BUFCHARS (512) struct sbuf { struct sbuf *qnext; int cstart; int cend; char ab[BUFCHARS]; }; /* Local functions. */ static void umake_file P((const char *zfile, int cextra)); static void uprepare_test P((boolean fmake, int itest, boolean fcall_uucico, const char *zsys)); static void ucheck_file P((const char *zfile, const char *zerr, int cextra)); static void ucheck_test P((int itest, boolean fcall_uucico)); static RETSIGTYPE uchild P((int isig)); static int cpshow P((char *z, int bchar)); static void uchoose P((int *po1, int *po2)); static long cread P((int o, struct sbuf **)); static boolean fsend P((int o, int oslave, struct sbuf **)); static boolean fwritable P((int o)); static void xsystem P((const char *zcmd)); static FILE *xfopen P((const char *zname, const char *zmode)); static char *zDebug; static int iTest; static boolean fCall_uucico; static int iPercent; static pid_t iPid1, iPid2; static int cFrom1, cFrom2; static char abLogout1[sizeof "tstout /dev/ptyp0"]; static char abLogout2[sizeof "tstout /dev/ptyp0"]; static char *zProtocols; int main (argc, argv) int argc; char **argv; { int iopt; const char *zcmd1, *zcmd2; const char *zsys; boolean fmake = TRUE; int omaster1, oslave1, omaster2, oslave2; char abpty1[sizeof "/dev/ptyp0"]; char abpty2[sizeof "/dev/ptyp0"]; struct sbuf *qbuf1, *qbuf2; #if ! HAVE_TAYLOR_CONFIG fprintf (stderr, "%s: only works when compiled with HAVE_TAYLOR_CONFIG\n", argv[0]); exit (1); #endif zcmd1 = NULL; zcmd2 = NULL; zsys = "test2"; while ((iopt = getopt (argc, argv, "c:np:s:t:ux:1:2:")) != EOF) { switch (iopt) { case 'c': zProtocols = optarg; break; case 'n': fmake = FALSE; break; case 'p': iPercent = (int) strtol (optarg, (char **) NULL, 10); srand ((unsigned int) ixsysdep_time ((long *) NULL)); break; case 's': zsys = optarg; break; case 't': iTest = (int) strtol (optarg, (char **) NULL, 10); break; case 'u': fCall_uucico = TRUE; break; case 'x': zDebug = optarg; break; case '1': zcmd1 = optarg; break; case '2': zcmd2 = optarg; break; default: fprintf (stderr, "Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995 Ian Lance Taylor\n", VERSION); fprintf (stderr, "Usage: tstuu [-xn] [-t #] [-u] [-1 cmd] [-2 cmd]\n"); exit (EXIT_FAILURE); } } if (fCall_uucico && zcmd2 == NULL) zcmd2 = ZUUCICO_CMD; uprepare_test (fmake, iTest, fCall_uucico, zsys); (void) remove ("/usr/tmp/tstuu/spool1/core"); (void) remove ("/usr/tmp/tstuu/spool2/core"); omaster1 = -1; oslave1 = -1; omaster2 = -1; oslave2 = -1; #if ! HAVE_STREAMS_PTYS { char *zptyname; const char *zpty; zptyname = abpty1; for (zpty = "pqrs"; *zpty != '\0'; ++zpty) { int ipty; for (ipty = 0; ipty < 16; ipty++) { int om, os; FILE *e; sprintf (zptyname, "/dev/pty%c%c", *zpty, "0123456789abcdef"[ipty]); om = open (zptyname, O_RDWR); if (om < 0) continue; zptyname[5] = 't'; os = open (zptyname, O_RDWR); if (os < 0) { (void) close (om); continue; } if (omaster1 == -1) { omaster1 = om; oslave1 = os; e = fopen ("/usr/tmp/tstuu/pty1", "w"); if (e == NULL) { perror ("fopen"); exit (EXIT_FAILURE); } fprintf (e, "%s", zptyname + 5); if (fclose (e) != 0) { perror ("fclose"); exit (EXIT_FAILURE); } zptyname = abpty2; } else { omaster2 = om; oslave2 = os; e = fopen ("/usr/tmp/tstuu/pty2", "w"); if (e == NULL) { perror ("fopen"); exit (EXIT_FAILURE); } fprintf (e, "%s", zptyname + 5); if (fclose (e) != 0) { perror ("fclose"); exit (EXIT_FAILURE); } break; } } if (omaster1 != -1 && omaster2 != -1) break; } } #else /* HAVE_STREAMS_PTYS */ { int ipty; for (ipty = 0; ipty < 2; ipty++) { int om, os; FILE *e; char *znam; struct termio stio; om = open ((char *) "/dev/ptmx", O_RDWR); if (om < 0) break; znam = ptsname (om); if (znam == NULL) break; if (unlockpt (om) != 0 || grantpt (om) != 0) break; os = open (znam, O_RDWR); if (os < 0) { (void) close (om); om = -1; break; } if (ioctl (os, I_PUSH, "ptem") < 0 || ioctl(os, I_PUSH, "ldterm") < 0) { perror ("ioctl"); exit (EXIT_FAILURE); } /* Can this really be right? */ memset (&stio, 0, sizeof (stio)); stio.c_cflag = B9600 | CS8 | CREAD | HUPCL; if (ioctl(os, TCSETA, &stio) < 0) { perror ("TCSETA"); exit (EXIT_FAILURE); } if (omaster1 == -1) { strcpy (abpty1, znam); omaster1 = om; oslave1 = os; e = fopen ("/usr/tmp/tstuu/pty1", "w"); if (e == NULL) { perror ("fopen"); exit (EXIT_FAILURE); } fprintf (e, "%s", znam + 5); if (fclose (e) != 0) { perror ("fclose"); exit (EXIT_FAILURE); } } else { strcpy (abpty2, znam); omaster2 = om; oslave2 = os; e = fopen ("/usr/tmp/tstuu/pty2", "w"); if (e == NULL) { perror ("fopen"); exit (EXIT_FAILURE); } fprintf (e, "%s", znam + 5); if (fclose (e) != 0) { perror ("fclose"); exit (EXIT_FAILURE); } } } } #endif /* HAVE_STREAMS_PTYS */ if (omaster2 == -1) { fprintf (stderr, "No pseudo-terminals available\n"); exit (EXIT_FAILURE); } /* Make sure we can or these into an int for the select call. Most systems could use 31 instead of 15, but it should never be a problem. */ if (omaster1 > 15 || omaster2 > 15) { fprintf (stderr, "File descriptors are too large\n"); exit (EXIT_FAILURE); } /* Prepare to log out the command if it is a login command. On Ultrix 4.0 uucico can only be run from login for some reason. */ if (zcmd1 == NULL || strncmp (zcmd1, "login", sizeof "login" - 1) != 0) abLogout1[0] = '\0'; else sprintf (abLogout1, "tstout %s", abpty1); if (zcmd2 == NULL || strncmp (zcmd2, "login", sizeof "login" - 1) != 0) abLogout2[0] = '\0'; else sprintf (abLogout2, "tstout %s", abpty2); iPid1 = fork (); if (iPid1 < 0) { perror ("fork"); exit (EXIT_FAILURE); } else if (iPid1 == 0) { if (close (0) < 0 || close (1) < 0 || close (omaster1) < 0 || close (omaster2) < 0 || close (oslave2) < 0) perror ("close"); if (dup2 (oslave1, 0) < 0 || dup2 (oslave1, 1) < 0) perror ("dup2"); if (close (oslave1) < 0) perror ("close"); /* This is said to improve the tests on Linux. */ sleep (3); if (zDebug != NULL) fprintf (stderr, "About to exec first process\n"); if (zcmd1 != NULL) exit (system ((char *) zcmd1)); else { (void) execl ("uucico", "uucico", "-I", "/usr/tmp/tstuu/Config1", "-q", "-S", zsys, "-pstdin", (const char *) NULL); perror ("execl failed"); exit (EXIT_FAILURE); } } iPid2 = fork (); if (iPid2 < 0) { perror ("fork"); kill (iPid1, SIGTERM); exit (EXIT_FAILURE); } else if (iPid2 == 0) { if (close (0) < 0 || close (1) < 0 || close (omaster1) < 0 || close (oslave1) < 0 || close (omaster2) < 0) perror ("close"); if (dup2 (oslave2, 0) < 0 || dup2 (oslave2, 1) < 0) perror ("dup2"); if (close (oslave2) < 0) perror ("close"); /* This is said to improve the tests on Linux. */ sleep (5); if (zDebug != NULL) fprintf (stderr, "About to exec second process\n"); if (fCall_uucico) { (void) execl (UUCICO_EXECL, (const char *) NULL); perror ("execl failed"); exit (EXIT_FAILURE); } else if (zcmd2 != NULL) exit (system ((char *) zcmd2)); else { (void) execl ("uucico", "uucico", "-I", "/usr/tmp/tstuu/Config2", "-lq", (const char *)NULL); perror ("execl failed"); exit (EXIT_FAILURE); } } signal (SIGCHLD, uchild); if (fcntl (omaster1, F_SETFL, O_NDELAY | O_NONBLOCK) < 0 && errno == EINVAL) (void) fcntl (omaster1, F_SETFL, O_NONBLOCK); if (fcntl (omaster2, F_SETFL, O_NDELAY | O_NONBLOCK) < 0 && errno == EINVAL) (void) fcntl (omaster2, F_SETFL, O_NONBLOCK); qbuf1 = NULL; qbuf2 = NULL; while (TRUE) { int o1, o2; boolean fcont; o1 = omaster1; o2 = omaster2; uchoose (&o1, &o2); if (o1 == -1 && o2 == -1) { if (zDebug != NULL) fprintf (stderr, "Five second pause\n"); continue; } if (o1 != -1) cFrom1 += cread (omaster1, &qbuf1); if (o2 != -1) cFrom2 += cread (omaster2, &qbuf2); do { fcont = FALSE; if (qbuf1 != NULL && fwritable (omaster2) && fsend (omaster2, oslave2, &qbuf1)) fcont = TRUE; if (qbuf2 != NULL && fwritable (omaster1) && fsend (omaster1, oslave1, &qbuf2)) fcont = TRUE; if (! fcont && (qbuf1 != NULL || qbuf2 != NULL)) { long cgot1, cgot2; cgot1 = cread (omaster1, &qbuf1); cFrom1 += cgot1; cgot2 = cread (omaster2, &qbuf2); cFrom2 += cgot2; fcont = TRUE; } } while (fcont); } /*NOTREACHED*/ } /* When a child dies, kill them both. */ static RETSIGTYPE uchild (isig) int isig ATTRIBUTE_UNUSED; { struct tms sbase, s1, s2; signal (SIGCHLD, SIG_DFL); /* Give the processes a chance to die on their own. */ sleep (2); (void) kill (iPid1, SIGTERM); (void) kill (iPid2, SIGTERM); (void) times (&sbase); #if HAVE_WAITPID (void) waitpid (iPid1, (pointer) NULL, 0); #else /* ! HAVE_WAITPID */ #if HAVE_WAIT4 (void) wait4 (iPid1, (pointer) NULL, 0, (struct rusage *) NULL); #else /* ! HAVE_WAIT4 */ (void) wait ((wait_status *) NULL); #endif /* ! HAVE_WAIT4 */ #endif /* ! HAVE_WAITPID */ (void) times (&s1); #if HAVE_WAITPID (void) waitpid (iPid2, (pointer) NULL, 0); #else /* ! HAVE_WAITPID */ #if HAVE_WAIT4 (void) wait4 (iPid2, (wait_status *) NULL, 0, (struct rusage *) NULL); #else /* ! HAVE_WAIT4 */ (void) wait ((wait_status *) NULL); #endif /* ! HAVE_WAIT4 */ #endif /* ! HAVE_WAITPID */ (void) times (&s2); fprintf (stderr, " First child: user: %g; system: %g\n", (double) (s1.tms_cutime - sbase.tms_cutime) / (double) TIMES_TICK, (double) (s1.tms_cstime - sbase.tms_cstime) / (double) TIMES_TICK); fprintf (stderr, "Second child: user: %g; system: %g\n", (double) (s2.tms_cutime - s1.tms_cutime) / (double) TIMES_TICK, (double) (s2.tms_cstime - s1.tms_cstime) / (double) TIMES_TICK); ucheck_test (iTest, fCall_uucico); if (abLogout1[0] != '\0') { if (zDebug != NULL) fprintf (stderr, "Executing %s\n", abLogout1); (void) system (abLogout1); } if (abLogout2[0] != '\0') { if (zDebug != NULL) fprintf (stderr, "Executing %s\n", abLogout2); (void) system (abLogout2); } fprintf (stderr, "Wrote %d bytes from 1 to 2\n", cFrom1); fprintf (stderr, "Wrote %d bytes from 2 to 1\n", cFrom2); if (access ("/usr/tmp/tstuu/spool1/core", R_OK) == 0) fprintf (stderr, "core file 1 exists\n"); if (access ("/usr/tmp/tstuu/spool2/core", R_OK) == 0) fprintf (stderr, "core file 2 exists\n"); exit (EXIT_SUCCESS); } /* Open a file without error. */ static FILE * xfopen (zname, zmode) const char *zname; const char *zmode; { FILE *eret; eret = fopen (zname, zmode); if (eret == NULL) { perror (zname); exit (EXIT_FAILURE); } return eret; } /* Close a file without error. */ static void xfclose P((FILE *e)); static void xfclose (e) FILE *e; { if (fclose (e) != 0) { perror ("fclose"); exit (EXIT_FAILURE); } } /* Create a test file. */ static void umake_file (z, c) const char *z; int c; { int i; FILE *e; e = xfopen (z, "w"); for (i = 0; i < 256; i++) { int i2; for (i2 = 0; i2 < 256; i2++) putc (i, e); } for (i = 0; i < c; i++) putc (i, e); xfclose (e); } /* Check a test file. */ static void ucheck_file (z, zerr, c) const char *z; const char *zerr; int c; { int i; FILE *e; e = xfopen (z, "r"); for (i = 0; i < 256; i++) { int i2; for (i2 = 0; i2 < 256; i2++) { int bread; bread = getc (e); if (bread == EOF) { fprintf (stderr, "%s: Unexpected EOF at position %d,%d\n", zerr, i, i2); xfclose (e); return; } if (bread != i) fprintf (stderr, "%s: At position %d,%d got %d expected %d\n", zerr, i, i2, bread, i); } } for (i = 0; i < c; i++) { int bread; bread = getc (e); if (bread == EOF) { fprintf (stderr, "%s: Unexpected EOF at extra %d\n", zerr, i); xfclose (e); return; } if (bread != i) fprintf (stderr, "%s: At extra %d got %d expected %d\n", zerr, i, bread, i); } if (getc (e) != EOF) fprintf (stderr, "%s: File is too long", zerr); xfclose (e); } /* Prepare all the configuration files for testing. */ static void uprepare_test (fmake, itest, fcall_uucico, zsys) boolean fmake; int itest; boolean fcall_uucico; const char *zsys; { FILE *e; const char *zuucp1, *zuucp2; const char *zuux1, *zuux2; char ab[1000]; const char *zfrom; const char *zto; /* We must make /usr/tmp/tstuu world writeable or we won't be able to receive files into it. */ (void) umask (0); #ifndef S_IWOTH #define S_IWOTH 02 #endif if (mkdir ((char *) "/usr/tmp/tstuu", IPUBLIC_DIRECTORY_MODE | S_IWOTH) != 0 && errno != EEXIST) { perror ("mkdir"); exit (EXIT_FAILURE); } if (mkdir ((char *) "/usr/tmp/tstuu/spool1", IPUBLIC_DIRECTORY_MODE) != 0 && errno != EEXIST) { perror ("mkdir"); exit (EXIT_FAILURE); } if (mkdir ((char *) "/usr/tmp/tstuu/spool2", IPUBLIC_DIRECTORY_MODE) != 0 && errno != EEXIST) { perror ("mkdir"); exit (EXIT_FAILURE); } if (fmake) { e = xfopen ("/usr/tmp/tstuu/Config1", "w"); fprintf (e, "# First test configuration file\n"); fprintf (e, "nodename test1\n"); fprintf (e, "spool /usr/tmp/tstuu/spool1\n"); fprintf (e, "lockdir /usr/tmp/tstuu/spool1\n"); fprintf (e, "sysfile /usr/tmp/tstuu/System1\n"); fprintf (e, "sysfile /usr/tmp/tstuu/System1.2\n"); fprintf (e, "portfile /usr/tmp/tstuu/Port1\n"); (void) remove ("/usr/tmp/tstuu/Log1"); #if ! HAVE_HDB_LOGGING fprintf (e, "logfile /usr/tmp/tstuu/Log1\n"); #else fprintf (e, "%s\n", "logfile /usr/tmp/tstuu/Log1/%s/%s"); #endif fprintf (e, "statfile /usr/tmp/tstuu/Stats1\n"); fprintf (e, "debugfile /usr/tmp/tstuu/Debug1\n"); fprintf (e, "callfile /usr/tmp/tstuu/Call1\n"); fprintf (e, "pubdir /usr/tmp/tstuu\n"); #if HAVE_V2_CONFIG fprintf (e, "v2-files no\n"); #endif #if HAVE_HDB_CONFIG fprintf (e, "hdb-files no\n"); #endif if (zDebug != NULL) fprintf (e, "debug %s\n", zDebug); xfclose (e); e = xfopen ("/usr/tmp/tstuu/System1", "w"); fprintf (e, "# This file is ignored, to test multiple system files\n"); fprintf (e, "time never\n"); xfclose (e); e = xfopen ("/usr/tmp/tstuu/System1.2", "w"); fprintf (e, "# First test system file\n"); fprintf (e, "time any\n"); fprintf (e, "port stdin\n"); fprintf (e, "# That was the defaults\n"); fprintf (e, "system %s\n", zsys); if (! fcall_uucico) { FILE *eprog; eprog = xfopen ("/usr/tmp/tstuu/Chat1", "w"); /* Wait for the other side to open the port and flush input. */ fprintf (eprog, "sleep 2\n"); fprintf (eprog, "echo password $1 speed $2 1>&2\n"); fprintf (eprog, "echo test1\n"); fprintf (eprog, "exit 0\n"); xfclose (eprog); if (chmod ("/usr/tmp/tstuu/Chat1", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) { perror ("chmod (/usr/tmp/tstuu/Chat1)"); exit (EXIT_FAILURE); } fprintf (e, "chat-program /usr/tmp/tstuu/Chat1 \\P \\S\n"); fprintf (e, "chat word: \\P\n"); fprintf (e, "chat-fail login;\n"); fprintf (e, "call-login *\n"); fprintf (e, "call-password *\n"); } else fprintf (e, "chat \"\"\n"); fprintf (e, "call-transfer yes\n"); fprintf (e, "commands cat\n"); if (! fcall_uucico && iPercent == 0) { fprintf (e, "protocol-parameter g window 7\n"); fprintf (e, "protocol-parameter g packet-size 4096\n"); fprintf (e, "protocol-parameter j avoid \\377\n"); } if (zProtocols != NULL) fprintf (e, "protocol %s\n", zProtocols); xfclose (e); e = xfopen ("/usr/tmp/tstuu/Port1", "w"); fprintf (e, "port stdin\n"); fprintf (e, "type stdin\n"); xfclose (e); e = xfopen ("/usr/tmp/tstuu/Call1", "w"); fprintf (e, "Call out password file\n"); fprintf (e, "%s test1 pass\\s1\n", zsys); xfclose (e); if (! fcall_uucico) { FILE *eprog; e = xfopen ("/usr/tmp/tstuu/Config2", "w"); fprintf (e, "# Second test configuration file\n"); fprintf (e, "nodename test2\n"); fprintf (e, "spool /usr/tmp/tstuu/spool2\n"); fprintf (e, "lockdir /usr/tmp/tstuu/spool2\n"); fprintf (e, "sysfile /usr/tmp/tstuu/System2\n"); (void) remove ("/usr/tmp/tstuu/Log2"); #if ! HAVE_HDB_LOGGING fprintf (e, "logfile /usr/tmp/tstuu/Log2\n"); #else fprintf (e, "%s\n", "logfile /usr/tmp/tstuu/Log2/%s/%s"); #endif fprintf (e, "statfile /usr/tmp/tstuu/Stats2\n"); fprintf (e, "debugfile /usr/tmp/tstuu/Debug2\n"); fprintf (e, "passwdfile /usr/tmp/tstuu/Pass2\n"); fprintf (e, "pubdir /usr/tmp/tstuu\n"); #if HAVE_V2_CONFIG fprintf (e, "v2-files no\n"); #endif #if HAVE_HDB_CONFIG fprintf (e, "hdb-files no\n"); #endif if (zDebug != NULL) fprintf (e, "debug %s\n", zDebug); xfclose (e); e = xfopen ("/usr/tmp/tstuu/System2", "w"); fprintf (e, "# Second test system file\n"); fprintf (e, "system test1\n"); fprintf (e, "called-login test1\n"); fprintf (e, "request true\n"); fprintf (e, "commands cat\n"); if (zProtocols != NULL) fprintf (e, "protocol %s\n", zProtocols); eprog = xfopen ("/usr/tmp/tstuu/Chat2", "w"); fprintf (eprog, "echo port $1 1>&2\n"); fprintf (eprog, "exit 0\n"); xfclose (eprog); if (chmod ("/usr/tmp/tstuu/Chat2", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) { perror ("chmod (/usr/tmp/tstuu/Chat2"); exit (EXIT_FAILURE); } fprintf (e, "called-chat-program /bin/sh /usr/tmp/tstuu/Chat2 \\Y\n"); fprintf (e, "time any\n"); xfclose (e); e = xfopen ("/usr/tmp/tstuu/Pass2", "w"); fprintf (e, "# Call in password file\n"); fprintf (e, "test1 pass\\s1\n"); xfclose (e); } } zuucp1 = "./uucp -I /usr/tmp/tstuu/Config1 -r"; zuux1 = "./uux -I /usr/tmp/tstuu/Config1 -r"; if (fcall_uucico) { zuucp2 = "/usr/bin/uucp -r"; zuux2 = "/usr/bin/uux -r"; } else { zuucp2 = "./uucp -I /usr/tmp/tstuu/Config2 -r"; zuux2 = "./uux -I /usr/tmp/tstuu/Config2 -r"; } /* Test transferring a file from the first system to the second. */ if (itest == 0 || itest == 1) { zfrom = "/usr/tmp/tstuu/from1"; if (fcall_uucico) zto = "/usr/spool/uucppublic/to1"; else zto = "/usr/tmp/tstuu/to1"; (void) remove (zto); umake_file (zfrom, 0); sprintf (ab, "%s %s %s!%s", zuucp1, zfrom, zsys, zto); xsystem (ab); } /* Test having the first system request a file from the second. */ if (itest == 0 || itest == 2) { if (fcall_uucico) zfrom = "/usr/spool/uucppublic/from2"; else zfrom = "/usr/tmp/tstuu/from2"; zto = "/usr/tmp/tstuu/to2"; (void) remove (zto); umake_file (zfrom, 3); sprintf (ab, "%s %s!%s %s", zuucp1, zsys, zfrom, zto); xsystem (ab); } /* Test having the second system send a file to the first. */ if (itest == 0 || itest == 3) { if (fcall_uucico) zfrom = "/usr/spool/uucppublic/from3"; else zfrom = "/usr/tmp/tstuu/from3"; zto = "/usr/tmp/tstuu/to3"; (void) remove (zto); umake_file (zfrom, 5); sprintf (ab, "%s -c \\~/from3 test1!~/to3", zuucp2); xsystem (ab); } /* Test having the second system request a file from the first. */ if (itest == 0 || itest == 4) { zfrom = "/usr/tmp/tstuu/from4"; if (fcall_uucico) zto = "/usr/spool/uucppublic/to4"; else zto = "/usr/tmp/tstuu/to4"; (void) remove (zto); umake_file (zfrom, 7); sprintf (ab, "%s test1!%s %s", zuucp2, zfrom, zto); xsystem (ab); } /* Test having the second system make an execution request. */ if (itest == 0 || itest == 5) { zfrom = "/usr/tmp/tstuu/from5"; if (fcall_uucico) zto = "/usr/spool/uucppublic/to5"; else zto = "/usr/tmp/tstuu/to5"; (void) remove (zto); umake_file (zfrom, 11); sprintf (ab, "%s test1!cat '<%s' '>%s'", zuux2, zfrom, zto); xsystem (ab); } /* Test having the first system request a wildcard. */ if (itest == 0 || itest == 6) { const char *zfrom1, *zfrom2; if (fcall_uucico) { zfrom = "/usr/spool/uucppublic/to6\\*"; zfrom1 = "/usr/spool/uucppublic/to6.1"; zfrom2 = "/usr/spool/uucppublic/to6.2"; } else { zfrom = "/usr/tmp/tstuu/spool2/to6\\*"; zfrom1 = "/usr/tmp/tstuu/spool2/to6.1"; zfrom2 = "/usr/tmp/tstuu/spool2/to6.2"; } umake_file (zfrom1, 100); umake_file (zfrom2, 101); (void) remove ("/usr/tmp/tstuu/to6.1"); (void) remove ("/usr/tmp/tstuu/to6.2"); sprintf (ab, "%s %s!%s /usr/tmp/tstuu", zuucp1, zsys, zfrom); xsystem (ab); } /* Test having the second system request a wildcard. */ if (itest == 0 || itest == 7) { const char *zto1, *zto2; if (fcall_uucico) { zto = "/usr/spool/uucppublic"; zto1 = "/usr/spool/uucppublic/to7.1"; zto2 = "/usr/spool/uucppublic/to7.2"; } else { zto = "/usr/tmp/tstuu"; zto1 = "/usr/tmp/tstuu/to7.1"; zto2 = "/usr/tmp/tstuu/to7.2"; } umake_file ("/usr/tmp/tstuu/spool1/to7.1", 150); umake_file ("/usr/tmp/tstuu/spool1/to7.2", 155); (void) remove (zto1); (void) remove (zto2); sprintf (ab, "%s test1!/usr/tmp/tstuu/spool1/to7.\\* %s", zuucp2, zto); xsystem (ab); } /* Test an E command. This runs cat, discarding the output. */ if ((itest == 0 || itest == 8) && ! fcall_uucico) { umake_file ("/usr/tmp/tstuu/from8", 30); sprintf (ab, "%s - test2!cat < /usr/tmp/tstuu/from8", zuux1); xsystem (ab); } } /* Try to make sure the file transfers were successful. */ static void ucheck_test (itest, fcall_uucico) int itest; boolean fcall_uucico; { if (itest == 0 || itest == 1) { if (fcall_uucico) ucheck_file ("/usr/spool/uucppublic/to1", "test 1", 0); else ucheck_file ("/usr/tmp/tstuu/to1", "test 1", 0); } if (itest == 0 || itest == 2) ucheck_file ("/usr/tmp/tstuu/to2", "test 2", 3); if (itest == 0 || itest == 3) ucheck_file ("/usr/tmp/tstuu/to3", "test 3", 5); if (itest == 0 || itest == 4) { if (fcall_uucico) ucheck_file ("/usr/spool/uucppublic/to4", "test 4", 7); else ucheck_file ("/usr/tmp/tstuu/to4", "test 4", 7); } if (itest == 0 || itest == 6) { ucheck_file ("/usr/tmp/tstuu/to6.1", "test 6.1", 100); ucheck_file ("/usr/tmp/tstuu/to6.2", "test 6.2", 101); } if (itest == 0 || itest == 7) { const char *zto1, *zto2; if (fcall_uucico) { zto1 = "/usr/spool/uucppublic/to7.1"; zto2 = "/usr/spool/uucppublic/to7.2"; } else { zto1 = "/usr/tmp/tstuu/to7.1"; zto2 = "/usr/tmp/tstuu/to7.2"; } ucheck_file (zto1, "test 7.1", 150); ucheck_file (zto2, "test 7.2", 155); } } /* A debugging routine used when displaying buffers. */ static int cpshow (z, ichar) char *z; int ichar; { if (isprint (BUCHAR (ichar)) && ichar != '\"') { *z = (char) ichar; return 1; } *z++ = '\\'; switch (ichar) { case '\n': *z = 'n'; return 2; case '\r': *z = 'r'; return 2; case '\"': *z = '\"'; return 2; default: sprintf (z, "%03o", (unsigned int)(ichar & 0xff)); return strlen (z) + 1; } } /* Pick one of two file descriptors which is ready for reading, or return in five seconds. If the argument is ready for reading, leave it alone; otherwise set it to -1. */ static void uchoose (po1, po2) int *po1; int *po2; { #if HAVE_SELECT int iread; struct timeval stime; iread = (1 << *po1) | (1 << *po2); stime.tv_sec = 5; stime.tv_usec = 0; if (select ((*po1 > *po2 ? *po1 : *po2) + 1, (pointer) &iread, (pointer) NULL, (pointer) NULL, &stime) < 0) { perror ("select"); uchild (SIGCHLD); } if ((iread & (1 << *po1)) == 0) *po1 = -1; if ((iread & (1 << *po2)) == 0) *po2 = -1; #else /* ! HAVE_SELECT */ #if HAVE_POLL struct pollfd as[2]; as[0].fd = *po1; as[0].events = POLLIN; as[1].fd = *po2; as[1].events = POLLIN; if (poll (as, 2, 5 * 1000) < 0) { perror ("poll"); uchild (SIGCHLD); } if ((as[0].revents & POLLIN) == 0) *po1 = -1; if ((as[1].revents & POLLIN) == 0) *po2 = -1; #endif /* HAVE_POLL */ #endif /* ! HAVE_SELECT */ } /* Read some data from a file descriptor. This keeps reading until one of the reads gets no data. */ static long cread (o, pqbuf) int o; struct sbuf **pqbuf; { long ctotal; while (*pqbuf != NULL && (*pqbuf)->qnext != NULL) pqbuf = &(*pqbuf)->qnext; ctotal = 0; while (TRUE) { int cgot; if (*pqbuf != NULL && (size_t) (*pqbuf)->cend >= sizeof (*pqbuf)->ab) pqbuf = &(*pqbuf)->qnext; if (*pqbuf == NULL) { *pqbuf = (struct sbuf *) malloc (sizeof (struct sbuf)); if (*pqbuf == NULL) { fprintf (stderr, "Out of memory\n"); uchild (SIGCHLD); } (*pqbuf)->qnext = NULL; (*pqbuf)->cstart = 0; (*pqbuf)->cend = 0; } cgot = read (o, (*pqbuf)->ab + (*pqbuf)->cend, (sizeof (*pqbuf)->ab) - (*pqbuf)->cend); if (cgot < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA) cgot = 0; else { perror ("read"); uchild (SIGCHLD); } } if (cgot == 0) return ctotal; ctotal += cgot; if (zDebug != NULL) { char abshow[325]; char *zfrom; char *zshow; int i; zfrom = (*pqbuf)->ab + (*pqbuf)->cend; zshow = abshow; for (i = 0; i < cgot && i < 80; i++, zfrom++) zshow += cpshow (zshow, *zfrom); if (i < cgot) { *zshow++ = '.'; *zshow++ = '.'; *zshow++ = '.'; } *zshow = '\0'; fprintf (stderr, "Read from %d: %d \"%s\"\n", o, cgot, abshow); fflush (stderr); } if (iPercent > 0) { int i; int c; c = 0; for (i = 0; i < cgot; i++) { if (rand () % 1000 < iPercent) { ++(*pqbuf)->ab[(*pqbuf)->cend + i]; ++c; } } if (zDebug != NULL && c > 0) fprintf (stderr, "Clobbered %d bytes\n", c); } (*pqbuf)->cend += cgot; if (ctotal > 256) return ctotal; } } /* Write data to a file descriptor until one of the writes gets no data. */ static boolean fsend (o, oslave, pqbuf) int o; int oslave; struct sbuf **pqbuf; { long ctotal; ctotal = 0; while (*pqbuf != NULL) { int cwrite, cwrote; if ((*pqbuf)->cstart >= (*pqbuf)->cend) { struct sbuf *qfree; qfree = *pqbuf; *pqbuf = (*pqbuf)->qnext; free ((pointer) qfree); continue; } #ifdef FIONREAD { long cunread; if (ioctl (oslave, FIONREAD, &cunread) < 0) { perror ("FIONREAD"); uchild (SIGCHLD); } if (zDebug != NULL) fprintf (stderr, "%ld unread\n", cunread); cwrite = 256 - cunread; if (cwrite <= 0) break; } #else /* ! FIONREAD */ if (! fwritable (o)) break; cwrite = 1; #endif /* ! FIONREAD */ if (cwrite > (*pqbuf)->cend - (*pqbuf)->cstart) cwrite = (*pqbuf)->cend - (*pqbuf)->cstart; cwrote = write (o, (*pqbuf)->ab + (*pqbuf)->cstart, cwrite); if (cwrote < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA) cwrote = 0; else { perror ("write"); uchild (SIGCHLD); } } if (cwrote == 0) break; ctotal += cwrote; (*pqbuf)->cstart += cwrote; } if (zDebug != NULL && ctotal > 0) fprintf (stderr, "Wrote %ld to %d\n", ctotal, o); return ctotal > 0; } /* Check whether a file descriptor can be written to. */ static boolean fwritable (o) int o; { #if HAVE_SELECT int iwrite; struct timeval stime; int cfds; iwrite = 1 << o; stime.tv_sec = 0; stime.tv_usec = 0; cfds = select (o + 1, (pointer) NULL, (pointer) &iwrite, (pointer) NULL, &stime); if (cfds < 0) { perror ("select"); uchild (SIGCHLD); } return cfds > 0; #else /* ! HAVE_SELECT */ #if HAVE_POLL struct pollfd s; int cfds; s.fd = o; s.events = POLLOUT; cfds = poll (&s, 1, 0); if (cfds < 0) { perror ("poll"); uchild (SIGCHLD); } return cfds > 0; #endif /* HAVE_POLL */ #endif /* ! HAVE_SELECT */ } /* A version of the system command that checks for errors. */ static void xsystem (zcmd) const char *zcmd; { int istat; istat = system ((char *) zcmd); if (istat != 0) { fprintf (stderr, "Command failed with status %d\n", istat); fprintf (stderr, "%s\n", zcmd); exit (EXIT_FAILURE); } } uucp-1.07/uuchk.c0000664000076400007640000010000307665321756007406 /* uuchk.c Display what we think the permissions of systems are. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char uuchk_rcsid[] = "$Id: uuchk.c,v 1.71 2002/03/05 19:10:42 ian Rel $"; #endif #include "getopt.h" #include "uuconf.h" /* Local functions. */ static void ukusage P((void)); static void ukhelp P((void)); static void ukshow_names P((const char *zheader, const char * const *pznames)); static void ukshow P((const struct uuconf_system *qsys, pointer puuconf)); static int ikshow_port P((struct uuconf_port *qport, pointer pinfo)); static void ukshow_dialer P((struct uuconf_dialer *qdial)); static void ukshow_chat P((const struct uuconf_chat *qchat, const char *zhdr)); static void ukshow_size P((struct uuconf_timespan *q, boolean fcall, boolean flocal)); static void ukshow_reliable P ((int i, const char *zhdr)); static void ukshow_proto_params P((struct uuconf_proto_param *pas, int cindent)); static void ukshow_time P((const struct uuconf_timespan *)); static struct uuconf_timespan *qcompress_span P((struct uuconf_timespan *)); static void ukuuconf_error P((pointer puuconf, int iret)); /* Structure used to pass uuconf pointer into ikshow_port and also let it record whether any ports were found. */ struct sinfo { /* The uuconf global pointer. */ pointer puuconf; /* The system. */ const struct uuconf_system *qsys; /* Whether any ports were seen. */ boolean fgot; }; /* Program name. */ static const char *zKprogram; /* Long getopt options. */ static const struct option asKlongopts[] = { { "system", required_argument, NULL,'s' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; int main (argc, argv) int argc; char **argv; { int iopt; const char *zsystem = NULL; const char *zconfig = NULL; int iret; pointer puuconf; zKprogram = argv[0]; while ((iopt = getopt_long (argc, argv, "I:s:vx:", asKlongopts, (int *) NULL)) != EOF) { switch (iopt) { case 's': /* Examine specific system. */ zsystem = optarg; break; case 'I': /* Set the configuration file name. */ zconfig = optarg; break; case 'x': /* Set the debugging level. There is actually no debugging information for this program. */ break; case 'v': /* Print version and exit. */ printf ("uuchk (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 1: /* --help. */ ukhelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found and flag set. */ break; default: ukusage (); /*NOTREACHED*/ } } if (optind != argc) { fprintf (stderr, "%s: too many arguments", zKprogram); ukusage (); } iret = uuconf_init (&puuconf, (const char *) NULL, zconfig); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); if (zsystem != NULL) { struct uuconf_system ssys; iret = uuconf_system_info (puuconf, zsystem, &ssys); if (iret == UUCONF_NOT_FOUND) { fprintf (stderr, "%s: system not found\n", zsystem); exit (EXIT_FAILURE); } else if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); ukshow (&ssys, puuconf); (void) uuconf_system_free (puuconf, &ssys); } else { struct uuconf_config_file_names snames; const char *zstr; int iint; char **pzsystems; iret = uuconf_config_files (puuconf, &snames); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); if (snames.uuconf_ztaylor_config != NULL) printf ("config file: %s\n", snames.uuconf_ztaylor_config); ukshow_names("sys file", snames.uuconf_pztaylor_sys); ukshow_names("port file", snames.uuconf_pztaylor_port); ukshow_names("dial file", snames.uuconf_pztaylor_dial); ukshow_names("dialcode file", snames.uuconf_pzdialcode); ukshow_names("passwd file", snames.uuconf_pztaylor_pwd); ukshow_names("call file", snames.uuconf_pztaylor_call); if (snames.uuconf_zv2_systems != NULL) printf ("V2 L.sys file: %s\n", snames.uuconf_zv2_systems); if (snames.uuconf_zv2_device != NULL) printf ("V2 L-devices file: %s\n", snames.uuconf_zv2_device); if (snames.uuconf_zv2_userfile != NULL) printf ("V2 USERFILE file: %s\n", snames.uuconf_zv2_userfile); if (snames.uuconf_zv2_cmds != NULL) printf ("V2 L.cmds file: %s\n", snames.uuconf_zv2_cmds); ukshow_names("HDB Systems file", snames.uuconf_pzhdb_systems); ukshow_names("HDB Devices file", snames.uuconf_pzhdb_devices); ukshow_names("HDB Dialers file", snames.uuconf_pzhdb_dialers); if (snames.uuconf_zhdb_permissions != NULL) printf ("HDB Permissions file: %s\n", snames.uuconf_zhdb_permissions); /* FIXME: This doesn't dump the following HDB file names: Sysfiles, Maxuuxqts, remote.unknown. */ iret = uuconf_localname (puuconf, &zstr); if (iret == UUCONF_SUCCESS) printf ("Local node name %s\n", zstr); else if (iret != UUCONF_NOT_FOUND) ukuuconf_error (puuconf, iret); iret = uuconf_spooldir (puuconf, &zstr); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); printf ("Spool directory %s\n", zstr); iret = uuconf_pubdir (puuconf, &zstr); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); printf ("Public directory %s\n", zstr); iret = uuconf_lockdir (puuconf, &zstr); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); printf ("Lock directory %s\n", zstr); iret = uuconf_logfile (puuconf, &zstr); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); printf ("Log file %s\n", zstr); iret = uuconf_statsfile (puuconf, &zstr); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); printf ("Statistics file %s\n", zstr); iret = uuconf_debugfile (puuconf, &zstr); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); printf ("Debug file %s\n", zstr); iret = uuconf_debuglevel (puuconf, &zstr); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); if (zstr != NULL) printf ("Global debugging level %s\n", zstr); iret = uuconf_strip (puuconf, &iint); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); printf ("uucico -l will %sstrip login names and passwords\n", (iint & UUCONF_STRIP_LOGIN) != 0 ? "" : "not "); printf ("uucico will %sstrip UUCP protocol commands\n", (iint & UUCONF_STRIP_PROTO) != 0 ? "" : "not "); iret = uuconf_maxuuxqts (puuconf, &iint); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); if (iint != 0) printf ("Maximum number of uuxqt processes permitted %d\n", iint); iret = uuconf_runuuxqt (puuconf, &iint); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); if (iint > 0) printf ("Start uuxqt every %d jobs\n", iint); else { switch (iint) { case UUCONF_RUNUUXQT_NEVER: printf ("Never start uuxqt\n"); break; case UUCONF_RUNUUXQT_ONCE: printf ("Start uuxqt once per uucico invocation\n"); break; case UUCONF_RUNUUXQT_PERCALL: printf ("Start uuxqt once per call\n"); break; default: fprintf (stderr, "Illegal value from uuconf_runuuxqt\n"); exit (EXIT_FAILURE); break; } } iret = uuconf_system_names (puuconf, &pzsystems, FALSE); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); if (*pzsystems == NULL) { fprintf (stderr, "%s: no systems found\n", zKprogram); exit (EXIT_FAILURE); } while (*pzsystems != NULL) { struct uuconf_system ssys; printf ("\n"); iret = uuconf_system_info (puuconf, *pzsystems, &ssys); if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); else ukshow (&ssys, puuconf); (void) uuconf_system_free (puuconf, &ssys); ++pzsystems; } } exit (EXIT_SUCCESS); /* Avoid errors about not returning a value. */ return 0; } /* Print a usage message and die. */ static void ukusage () { fprintf (stderr, "Usage: %s [-s system] [-I file]\n", zKprogram); fprintf (stderr, "Use %s --help for help\n", zKprogram); exit (EXIT_FAILURE); } /* Print a help message. */ static void ukhelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); printf ("Usage: %s [-s system] [-I file] [-v]\n", zKprogram); printf (" -s,--system system: Only print configuration for named system\n"); printf (" -I,--config file: Set configuration file to use\n"); printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } /* Print a list of configuration file names. */ static void ukshow_names (zheader, pznames) const char *zheader; const char * const *pznames; { if (pznames == NULL) return; if (pznames[1] == NULL) printf ("%s: %s\n", zheader, pznames[0]); else { const char * const *pz; printf ("%s:\n", zheader); for (pz = pznames; *pz != NULL; ++pz) printf (" %s\n", *pz); } } /* Dump out the information for a system. */ static void ukshow (qsys, puuconf) const struct uuconf_system *qsys; pointer puuconf; { char **pz; int i; int iret; boolean fanycall; printf ("System: %s", qsys->uuconf_zname); if (qsys->uuconf_pzalias != NULL) { printf (" ("); for (pz = qsys->uuconf_pzalias; *pz != NULL; pz++) { printf ("%s", *pz); if (pz[1] != NULL) printf (" "); } printf (")"); } printf ("\n"); fanycall = FALSE; for (i = 0; qsys != NULL; qsys = qsys->uuconf_qalternate, i++) { boolean fcall, fcalled; struct uuconf_timespan *qtime, *qspan; if (i != 0 || qsys->uuconf_qalternate != NULL) { printf ("Alternate %d", i); if (qsys->uuconf_zalternate != NULL) printf (" (%s)", qsys->uuconf_zalternate); printf ("\n"); } /* See if this alternate could be used when calling out. */ fcall = qsys->uuconf_fcall; if (qsys->uuconf_qtimegrade == NULL) fcall = FALSE; /* See if this alternate could be used when calling in. */ fcalled = qsys->uuconf_fcalled; if (! fcall && ! fcalled) { printf (" This alternate is never used\n"); continue; } if (fcall) fanycall = TRUE; if (fcalled) { if (qsys->uuconf_zcalled_login != NULL && strcmp (qsys->uuconf_zcalled_login, "ANY") != 0) { if (i == 0 && qsys->uuconf_qalternate == NULL) printf (" Caller must log in as %s\n", qsys->uuconf_zcalled_login); else printf (" When called using login name %s\n", qsys->uuconf_zcalled_login); } else printf (" When called using any login name\n"); if (qsys->uuconf_zlocalname != NULL) printf (" Will use %s as name of local system\n", qsys->uuconf_zlocalname); } if (fcalled && qsys->uuconf_fcallback) { printf (" If called, will call back\n"); fcalled = FALSE; } if (fcall) { struct sinfo si; if (i == 0 && qsys->uuconf_qalternate == NULL) printf (" Call out"); else printf (" This alternate applies when calling"); if (qsys->uuconf_zport != NULL || qsys->uuconf_qport != NULL) { printf (" using "); if (qsys->uuconf_zport != NULL) printf ("port %s", qsys->uuconf_zport); else printf ("a specially defined port"); if (qsys->uuconf_ibaud != 0) { printf (" at speed %ld", qsys->uuconf_ibaud); if (qsys->uuconf_ihighbaud != 0) printf (" to %ld", qsys->uuconf_ihighbaud); } printf ("\n"); } else if (qsys->uuconf_ibaud != 0) { printf (" at speed %ld", qsys->uuconf_ibaud); if (qsys->uuconf_ihighbaud != 0) printf (" to %ld", qsys->uuconf_ihighbaud); printf ("\n"); } else printf (" using any port\n"); si.puuconf = puuconf; si.qsys = qsys; si.fgot = FALSE; if (qsys->uuconf_qport != NULL) { printf (" The port is defined as:\n"); (void) ikshow_port (qsys->uuconf_qport, (pointer) &si); } else { struct uuconf_port sdummy; printf (" The possible ports are:\n"); iret = uuconf_find_port (puuconf, qsys->uuconf_zport, qsys->uuconf_ibaud, qsys->uuconf_ihighbaud, ikshow_port, (pointer) &si, &sdummy); if (iret != UUCONF_NOT_FOUND) ukuuconf_error (puuconf, iret); if (! si.fgot) printf (" *** There are no matching ports\n"); } if (qsys->uuconf_zphone != NULL) { if ((qsys->uuconf_zport != NULL && strcmp (qsys->uuconf_zport, "TCP") == 0) || (qsys->uuconf_qport != NULL && (qsys->uuconf_qport->uuconf_ttype == UUCONF_PORTTYPE_TCP || qsys->uuconf_qport->uuconf_ttype == UUCONF_PORTTYPE_TLI))) printf (" Remote address %s\n", qsys->uuconf_zphone); else printf (" Phone number %s\n", qsys->uuconf_zphone); } ukshow_chat (&qsys->uuconf_schat, " Chat"); if (qsys->uuconf_zcall_login != NULL || qsys->uuconf_zcall_password != NULL) { char *zlogin, *zpass; iret = uuconf_callout (puuconf, qsys, &zlogin, &zpass); if (iret == UUCONF_NOT_FOUND) printf (" Can not determine login name or password\n"); else if (UUCONF_ERROR_VALUE (iret) == UUCONF_FOPEN_FAILED) printf (" Can not read call out file\n"); else if (iret != UUCONF_SUCCESS) ukuuconf_error (puuconf, iret); else { if (zlogin != NULL) { printf (" Login name %s\n", zlogin); free ((pointer) zlogin); } if (zpass != NULL) { printf (" Password %s\n", zpass); free ((pointer) zpass); } } } qtime = qcompress_span (qsys->uuconf_qtimegrade); for (qspan = qtime; qspan != NULL; qspan = qspan->uuconf_qnext) { printf (" "); ukshow_time (qspan); printf (" may call if "); if ((char) qspan->uuconf_ival == UUCONF_GRADE_LOW) printf ("any work"); else printf ("work grade %c or higher", (char) qspan->uuconf_ival); if (qspan->uuconf_cretry != 0) printf (" (retry %d)", qspan->uuconf_cretry); printf ("\n"); } if (qsys->uuconf_cmax_retries > 0) printf (" May retry the call up to %d times\n", qsys->uuconf_cmax_retries); if (qsys->uuconf_qcalltimegrade != NULL) { boolean fprint, fother; qtime = qcompress_span (qsys->uuconf_qcalltimegrade); fprint = FALSE; fother = FALSE; if (qtime->uuconf_istart != 0) fother = TRUE; for (qspan = qtime; qspan != NULL; qspan = qspan->uuconf_qnext) { if ((char) qspan->uuconf_ival == UUCONF_GRADE_LOW) { fother = TRUE; continue; } fprint = TRUE; printf (" "); ukshow_time (qspan); printf (" may accept work grade %c or higher\n", (char) qspan->uuconf_ival); if (qspan->uuconf_qnext == NULL) { if (qspan->uuconf_iend != 7 * 24 * 60) fother = TRUE; } else { if (qspan->uuconf_iend != qspan->uuconf_qnext->uuconf_istart) fother = TRUE; } } if (fprint && fother) printf (" (At other times may accept any work)\n"); } } if (fcalled) { if (qsys->uuconf_qcalledtimegrade != NULL) { boolean fprint, fother; qtime = qcompress_span (qsys->uuconf_qcalledtimegrade); fprint = FALSE; fother = FALSE; if (qtime->uuconf_istart != 0) fother = TRUE; for (qspan = qtime; qspan != NULL; qspan = qspan->uuconf_qnext) { if ((char) qspan->uuconf_ival == UUCONF_GRADE_LOW) { fother = TRUE; continue; } fprint = TRUE; printf (" "); ukshow_time (qspan); printf (" will send work grade %c or higher\n", (char) qspan->uuconf_ival); if (qspan->uuconf_qnext == NULL) { if (qspan->uuconf_iend != 7 * 24 * 60) fother = TRUE; } else { if (qspan->uuconf_iend != qspan->uuconf_qnext->uuconf_istart) fother = TRUE; } } if (fprint && fother) printf (" (At other times will send any work)\n"); } } if (fcall && qsys->uuconf_csuccess_wait != 0) printf (" Will wait %d seconds after a successful call\n", qsys->uuconf_csuccess_wait); if (qsys->uuconf_fsequence) printf (" Sequence numbers are used\n"); if (fcalled) ukshow_chat (&qsys->uuconf_scalled_chat, " When called, chat"); if (qsys->uuconf_zdebug != NULL) printf (" Debugging level %s\n", qsys->uuconf_zdebug); if (qsys->uuconf_zmax_remote_debug != NULL) printf (" Max remote debugging level %s\n", qsys->uuconf_zmax_remote_debug); if (fcall) { ukshow_size (qsys->uuconf_qcall_local_size, TRUE, TRUE); ukshow_size (qsys->uuconf_qcall_remote_size, TRUE, FALSE); } if (fcalled) { ukshow_size (qsys->uuconf_qcalled_local_size, FALSE, TRUE); ukshow_size (qsys->uuconf_qcalled_remote_size, FALSE, FALSE); } if (fcall) printf (" May %smake local requests when calling\n", qsys->uuconf_fcall_transfer ? "" : "not "); if (fcalled) printf (" May %smake local requests when called\n", qsys->uuconf_fcalled_transfer ? "" : "not "); if (qsys->uuconf_fcall_transfer || qsys->uuconf_fcalled_transfer) { printf (" May send by local request:"); for (pz = qsys->uuconf_pzlocal_send; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } if (! qsys->uuconf_fsend_request) printf (" May not send files by remote request\n"); else { printf (" May send by remote request:"); for (pz = qsys->uuconf_pzremote_send; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } if (qsys->uuconf_fcall_transfer || qsys->uuconf_fcalled_transfer) { printf (" May accept by local request:"); for (pz = qsys->uuconf_pzlocal_receive; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } if (! qsys->uuconf_frec_request) printf (" May not receive files by remote request\n"); else { printf (" May receive by remote request:"); for (pz = qsys->uuconf_pzremote_receive; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } printf (" May execute"); for (pz = qsys->uuconf_pzcmds; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); printf (" Execution path"); for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++) printf (" %s" , *pz); printf ("\n"); if (qsys->uuconf_cfree_space != 0) printf (" Will leave %ld bytes available\n", qsys->uuconf_cfree_space); if (qsys->uuconf_zpubdir != NULL) printf (" Public directory is %s\n", qsys->uuconf_zpubdir); if (qsys->uuconf_pzforward_from != NULL) { printf (" May forward from"); for (pz = qsys->uuconf_pzforward_from; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } if (qsys->uuconf_pzforward_to != NULL) { printf (" May forward to"); for (pz = qsys->uuconf_pzforward_to; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } if (qsys->uuconf_cmax_file_time > 0) printf (" Maximum file send time: %ld\n", qsys->uuconf_cmax_file_time); if (qsys->uuconf_zprotocols != NULL) printf (" Will use protocols %s\n", qsys->uuconf_zprotocols); else printf (" Will use any known protocol\n"); if (qsys->uuconf_qproto_params != NULL) ukshow_proto_params (qsys->uuconf_qproto_params, 1); } if (! fanycall) printf (" Calls will never be placed to this system\n"); } /* Show information about a port. */ /*ARGSUSED*/ static int ikshow_port (qport, pinfo) struct uuconf_port *qport; pointer pinfo; { struct sinfo *qi = (struct sinfo *) pinfo; char **pz; struct uuconf_modem_port *qmodem; struct uuconf_tcp_port *qtcp; struct uuconf_tli_port *qtli; struct uuconf_pipe_port *qpipe; qi->fgot = TRUE; printf (" Port name %s\n", qport->uuconf_zname); switch (qport->uuconf_ttype) { case UUCONF_PORTTYPE_STDIN: printf (" Port type stdin\n"); break; case UUCONF_PORTTYPE_DIRECT: printf (" Port type direct\n"); if (qport->uuconf_u.uuconf_sdirect.uuconf_zdevice != NULL) printf (" Device %s\n", qport->uuconf_u.uuconf_sdirect.uuconf_zdevice); else printf (" Using port name as device name\n"); printf (" Speed %ld\n", qport->uuconf_u.uuconf_sdirect.uuconf_ibaud); printf (" Carrier %savailable\n", qport->uuconf_u.uuconf_sdirect.uuconf_fcarrier ? "" : "not "); printf (" Hardware flow control %savailable\n", qport->uuconf_u.uuconf_sdirect.uuconf_fhardflow ? "" : "not "); break; case UUCONF_PORTTYPE_MODEM: qmodem = &qport->uuconf_u.uuconf_smodem; printf (" Port type modem\n"); if (qmodem->uuconf_zdevice != NULL) printf (" Device %s\n", qmodem->uuconf_zdevice); else printf (" Using port name as device name\n"); if (qmodem->uuconf_zdial_device != NULL) printf (" Dial device %s\n", qmodem->uuconf_zdial_device); printf (" Speed %ld\n", qmodem->uuconf_ibaud); if (qmodem->uuconf_ilowbaud != qmodem->uuconf_ihighbaud) printf (" Speed range %ld to %ld\n", qmodem->uuconf_ilowbaud, qmodem->uuconf_ihighbaud); printf (" Carrier %savailable\n", qmodem->uuconf_fcarrier ? "" : "not "); printf (" Hardware flow control %savailable\n", qmodem->uuconf_fhardflow ? "" : "not "); if (qmodem->uuconf_qdialer != NULL) { printf (" Specially defined dialer\n"); ukshow_dialer (qmodem->uuconf_qdialer); } else if (qmodem->uuconf_pzdialer != NULL && qmodem->uuconf_pzdialer[0] != NULL) { struct uuconf_dialer sdial; int iret; /* This might be a single dialer name, or it might be a sequence of dialer/token pairs. */ if (qmodem->uuconf_pzdialer[1] == NULL || qmodem->uuconf_pzdialer[2] == NULL) { iret = uuconf_dialer_info (qi->puuconf, qmodem->uuconf_pzdialer[0], &sdial); if (iret == UUCONF_NOT_FOUND) printf (" *** No dialer %s\n", qmodem->uuconf_pzdialer[0]); else if (iret != UUCONF_SUCCESS) ukuuconf_error (qi->puuconf, iret); else { printf (" Dialer %s\n", qmodem->uuconf_pzdialer[0]); ukshow_dialer (&sdial); if (qmodem->uuconf_pzdialer[1] != NULL) printf (" Token %s\n", qmodem->uuconf_pzdialer[1]); } } else { pz = qmodem->uuconf_pzdialer; while (*pz != NULL) { iret = uuconf_dialer_info (qi->puuconf, *pz, &sdial); if (iret == UUCONF_NOT_FOUND) printf (" *** No dialer %s\n", *pz); else if (iret != UUCONF_SUCCESS) ukuuconf_error (qi->puuconf, iret); else { printf (" Dialer %s\n", *pz); ukshow_dialer (&sdial); } ++pz; if (*pz != NULL) { printf (" Token %s\n", *pz); ++pz; } } } } else printf (" *** No dialer information\n"); break; case UUCONF_PORTTYPE_TCP: qtcp = &qport->uuconf_u.uuconf_stcp; printf (" Port type tcp\n"); printf (" TCP service %s\n", qtcp->uuconf_zport); if (qtcp->uuconf_iversion != 0) printf (" IP version %d\n", qtcp->uuconf_iversion); if (qtcp->uuconf_pzdialer != NULL && qtcp->uuconf_pzdialer[0] != NULL) { printf (" Dialer sequence"); for (pz = qtcp->uuconf_pzdialer; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } break; case UUCONF_PORTTYPE_TLI: qtli = &qport->uuconf_u.uuconf_stli; printf (" Port type TLI%s\n", qtli->uuconf_fstream ? "S" : ""); if (qtli->uuconf_zdevice != NULL) printf (" Device %s\n", qtli->uuconf_zdevice); else printf (" Using port name as device name\n"); if (qtli->uuconf_pzpush != NULL) { printf (" Push"); for (pz = qtli->uuconf_pzpush; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } if (qtli->uuconf_pzdialer != NULL && qtli->uuconf_pzdialer[0] != NULL) { printf (" Dialer sequence"); for (pz = qtli->uuconf_pzdialer; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } if (qtli->uuconf_zservaddr != NULL) printf (" Server address %s\n", qtli->uuconf_zservaddr); break; case UUCONF_PORTTYPE_PIPE: qpipe = &qport->uuconf_u.uuconf_spipe; printf (" Port type pipe\n"); if (qpipe->uuconf_pzcmd != NULL) { printf (" Command"); for (pz = qpipe->uuconf_pzcmd; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } break; default: fprintf (stderr, " CAN'T HAPPEN\n"); break; } if (qport->uuconf_zprotocols != NULL) printf (" Will use protocols %s\n", qport->uuconf_zprotocols); if (qport->uuconf_zlockname != NULL) printf (" Will use lockname %s\n", qport->uuconf_zlockname); if ((qport->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) ukshow_reliable (qport->uuconf_ireliable, " "); if (qport->uuconf_qproto_params != NULL) ukshow_proto_params (qport->uuconf_qproto_params, 3); /* Return NOT_FOUND to force find_port to continue searching. */ return UUCONF_NOT_FOUND; } /* Show information about a dialer. */ static void ukshow_dialer (q) struct uuconf_dialer *q; { ukshow_chat (&q->uuconf_schat, " Chat"); printf (" Wait for dialtone %s\n", q->uuconf_zdialtone); printf (" Pause while dialing %s\n", q->uuconf_zpause); printf (" Carrier %savailable\n", q->uuconf_fcarrier ? "" : "not "); if (q->uuconf_fcarrier) printf (" Wait %d seconds for carrier\n", q->uuconf_ccarrier_wait); if (q->uuconf_fdtr_toggle) { printf (" Toggle DTR"); if (q->uuconf_fdtr_toggle_wait) printf (" and wait"); printf ("\n"); } ukshow_chat (&q->uuconf_scomplete, " When complete chat"); ukshow_chat (&q->uuconf_sabort, " When aborting chat"); if ((q->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) ukshow_reliable (q->uuconf_ireliable, " "); if (q->uuconf_qproto_params != NULL) ukshow_proto_params (q->uuconf_qproto_params, 4); } /* Show a chat script. */ static void ukshow_chat (qchat, zhdr) const struct uuconf_chat *qchat; const char *zhdr; { char **pz; if (qchat->uuconf_pzprogram != NULL) { printf ("%s program", zhdr); for (pz = qchat->uuconf_pzprogram; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } if (qchat->uuconf_pzchat != NULL) { printf ("%s script", zhdr); for (pz = qchat->uuconf_pzchat; *pz != NULL; pz++) { if ((*pz)[0] != '-' || pz == qchat->uuconf_pzchat) printf (" "); printf ("%s", *pz); } printf ("\n"); printf ("%s script timeout %d\n", zhdr, qchat->uuconf_ctimeout); if (qchat->uuconf_pzfail != NULL) { printf ("%s failure strings", zhdr); for (pz = qchat->uuconf_pzfail; *pz != NULL; pz++) printf (" %s", *pz); printf ("\n"); } if (qchat->uuconf_fstrip) printf ("%s script incoming bytes stripped to seven bits\n", zhdr); } } /* Show a size/time restriction. */ static void ukshow_size (qspan, fcall, flocal) struct uuconf_timespan *qspan; boolean fcall; boolean flocal; { struct uuconf_timespan *q; boolean fother; qspan = qcompress_span (qspan); if (qspan == NULL) return; printf (" If call%s the following applies to a %s request:\n", fcall ? "ing" : "ed", flocal ? "local" : "remote"); fother = FALSE; if (qspan->uuconf_istart >= 60) fother = TRUE; for (q = qspan; q != NULL; q = q->uuconf_qnext) { printf (" "); ukshow_time (q); printf (" may transfer files %ld bytes or smaller\n", q->uuconf_ival); if (q->uuconf_qnext == NULL) { if (q->uuconf_iend <= 6 * 24 * 60 + 23 * 60) fother = TRUE; } else { if (q->uuconf_iend + 60 <= q->uuconf_qnext->uuconf_istart) fother = TRUE; } } if (fother) printf (" (At other times may send files of any size)\n"); } /* Show reliability information. */ static void ukshow_reliable (i, zhdr) int i; const char *zhdr; { printf ("%sCharacteristics:", zhdr); if ((i & UUCONF_RELIABLE_EIGHT) != 0) printf (" eight-bit-clean"); else printf (" not-eight-bit-clean"); if ((i & UUCONF_RELIABLE_RELIABLE) != 0) printf (" reliable"); if ((i & UUCONF_RELIABLE_ENDTOEND) != 0) printf (" end-to-end"); if ((i & UUCONF_RELIABLE_FULLDUPLEX) != 0) printf (" fullduplex"); else printf (" halfduplex"); printf ("\n"); } /* Show protocol parameters. */ static void ukshow_proto_params (pas, cindent) struct uuconf_proto_param *pas; int cindent; { struct uuconf_proto_param *q; for (q = pas; q->uuconf_bproto != '\0'; q++) { int i; struct uuconf_proto_param_entry *qe; for (i = 0; i < cindent; i++) printf (" "); printf ("For protocol %c will use the following parameters\n", q->uuconf_bproto); for (qe = q->uuconf_qentries; qe->uuconf_cargs > 0; qe++) { int ia; for (i = 0; i < cindent; i++) printf (" "); for (ia = 0; ia < qe->uuconf_cargs; ia++) printf (" %s", qe->uuconf_pzargs[ia]); printf ("\n"); } } } /* Display a time span. */ static void ukshow_time (q) const struct uuconf_timespan *q; { int idaystart, idayend; int ihourstart, ihourend; int iminutestart, iminuteend; const char * const zdays = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat\0Sun"; if (q->uuconf_istart == 0 && q->uuconf_iend == 7 * 24 * 60) { printf ("At any time"); return; } idaystart = q->uuconf_istart / (24 * 60); ihourstart = (q->uuconf_istart % (24 * 60)) / 60; iminutestart = q->uuconf_istart % 60; idayend = q->uuconf_iend / (24 * 60); ihourend = (q->uuconf_iend % (24 * 60)) / 60; iminuteend = q->uuconf_iend % 60; if (idaystart == idayend) printf ("%s from %02d:%02d to %02d:%02d", zdays + idaystart * 4, ihourstart, iminutestart, ihourend, iminuteend); else printf ("From %s %02d:%02d to %s %02d:%02d", zdays + idaystart * 4, ihourstart, iminutestart, zdays + idayend * 4, ihourend, iminuteend); } /* Compress a time span by merging any two adjacent spans with identical values. This isn't necessary for uucico, but it looks nicer when printed out. */ static struct uuconf_timespan * qcompress_span (qlist) struct uuconf_timespan *qlist; { struct uuconf_timespan **pq; pq = &qlist; while (*pq != NULL) { if ((*pq)->uuconf_qnext != NULL && (*pq)->uuconf_iend == (*pq)->uuconf_qnext->uuconf_istart && (*pq)->uuconf_ival == (*pq)->uuconf_qnext->uuconf_ival) { struct uuconf_timespan *qnext; qnext = (*pq)->uuconf_qnext; (*pq)->uuconf_qnext = qnext->uuconf_qnext; (*pq)->uuconf_iend = qnext->uuconf_iend; } else pq = &(*pq)->uuconf_qnext; } return qlist; } /* Display a uuconf error and exit. */ static void ukuuconf_error (puuconf, iret) pointer puuconf; int iret; { char ab[512]; (void) uuconf_error_string (puuconf, iret, ab, sizeof ab); if ((iret & UUCONF_ERROR_FILENAME) == 0) fprintf (stderr, "%s: %s\n", zKprogram, ab); else fprintf (stderr, "%s:%s\n", zKprogram, ab); exit (EXIT_FAILURE); } uucp-1.07/uucico.c0000664000076400007640000024007707665321756007576 /* uucico.c This is the main UUCP communication program. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002, 2003 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char uucico_rcsid[] = "$Id: uucico.c,v 1.204 2003/05/29 06:00:49 ian Rel $"; #endif #include #if HAVE_LIMITS_H #include #else #define LONG_MAX 2147483647L #endif #include "getopt.h" #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "prot.h" #include "trans.h" #include "system.h" #if HAVE_ENCRYPTED_PASSWORDS #ifndef crypt extern char *crypt (); #endif #endif /* Coherent already had a different meaning for the -c option. What a pain. */ #ifdef __COHERENT__ #define COHERENT_C_OPTION 1 #else #define COHERENT_C_OPTION 0 #endif /* Define the known protocols. */ #define TCP_PROTO \ (UUCONF_RELIABLE_ENDTOEND \ | UUCONF_RELIABLE_RELIABLE \ | UUCONF_RELIABLE_EIGHT) static const struct sprotocol asProtocols[] = { { 't', TCP_PROTO, 1, TRUE, asTproto_params, ftstart, ftshutdown, ftsendcmd, ztgetspace, ftsenddata, ftwait, ftfile }, { 'e', TCP_PROTO, 1, TRUE, asEproto_params, festart, feshutdown, fesendcmd, zegetspace, fesenddata, fewait, fefile }, { 'i', UUCONF_RELIABLE_EIGHT, 7, TRUE, asIproto_params, fistart, fishutdown, fisendcmd, zigetspace, fisenddata, fiwait, NULL }, { 'a', UUCONF_RELIABLE_EIGHT, 1, TRUE, asZproto_params, fzstart, fzshutdown, fzsendcmd, zzgetspace, fzsenddata, fzwait, fzfile }, { 'g', UUCONF_RELIABLE_EIGHT, 1, TRUE, asGproto_params, fgstart, fgshutdown, fgsendcmd, zggetspace, fgsenddata, fgwait, NULL }, { 'G', UUCONF_RELIABLE_EIGHT, 1, TRUE, asGproto_params, fbiggstart, fgshutdown, fgsendcmd, zggetspace, fgsenddata, fgwait, NULL }, { 'j', UUCONF_RELIABLE_EIGHT, 7, TRUE, asIproto_params, fjstart, fjshutdown, fisendcmd, zigetspace, fisenddata, fiwait, NULL }, { 'f', UUCONF_RELIABLE_RELIABLE, 1, FALSE, asFproto_params, ffstart, ffshutdown, ffsendcmd, zfgetspace, ffsenddata, ffwait, fffile }, { 'v', UUCONF_RELIABLE_EIGHT, 1, TRUE, asGproto_params, fvstart, fgshutdown, fgsendcmd, zggetspace, fgsenddata, fgwait, NULL }, { 'y', UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT, 1, TRUE, asYproto_params, fystart, fyshutdown, fysendcmd, zygetspace, fysenddata, fywait, fyfile } }; #define CPROTOCOLS (sizeof asProtocols / sizeof asProtocols[0]) /* Locked system. */ static boolean fLocked_system; static struct uuconf_system sLocked_system; /* Daemon structure holding information about the remote system (must be global so the error handler can see it. */ static struct sdaemon sDaemon; /* Open connection. */ static struct sconnection *qConn; /* uuconf global pointer; need to close the connection after a fatal error. */ static pointer pUuconf; /* This structure is passed to iuport_lock via uuconf_find_port. */ struct spass { boolean fmatched; boolean flocked; struct sconnection *qconn; }; /* Local functions. */ static void uusage P((void)); static void uhelp P((void)); static void uabort P((void)); static boolean fcall P((pointer puuconf, const char *zconfig, boolean fuuxqt, const struct uuconf_system *qsys, struct uuconf_port *qport, boolean fifwork, boolean fforce, boolean fdetach, boolean fquiet, boolean ftrynext)); static boolean fconn_call P((struct sdaemon *qdaemon, struct uuconf_port *qport, struct sstatus *qstat, int cretry, boolean *pfcalled)); static boolean fdo_call P((struct sdaemon *qdaemon, struct sstatus *qstat, const struct uuconf_dialer *qdialer, boolean *pfcalled, enum tstatus_type *pterr)); static int iuport_lock P((struct uuconf_port *qport, pointer pinfo)); static boolean flogin_prompt P((pointer puuconf, const char *zconfig, boolean fuuxqt, struct sconnection *qconn, const char *zlogin, const char **pzsystem)); static int icallin_cmp P((int iwhich, pointer pinfo, const char *zfile)); static boolean faccept_call P((pointer puuconf, const char *zconfig, boolean fuuxqt, const char *zlogin, struct sconnection *qconn, const char **pzsystem)); static void uaccept_call_cleanup P((pointer puuconf, struct uuconf_system *qfreesys, struct uuconf_port *qport, struct uuconf_port *qfreeport, char *zloc)); static void uapply_proto_params P((pointer puuconf, int bproto, struct uuconf_cmdtab *qcmds, struct uuconf_proto_param *pas)); static boolean fsend_uucp_cmd P((struct sconnection *qconn, const char *z)); static char *zget_uucp_cmd P((struct sconnection *qconn, boolean frequired, boolean fstrip)); static char *zget_typed_line P((struct sconnection *qconn, boolean fstrip)); /* Long getopt options. */ static const struct option asLongopts[] = { { "quiet", no_argument, NULL, 2 }, { "ifwork", no_argument, NULL, 'C' }, { "nodetach", no_argument, NULL, 'D' }, { "loop", no_argument, NULL, 'e' }, { "force", no_argument, NULL, 'f'}, { "stdin", required_argument, NULL, 'i' }, { "prompt", no_argument, NULL, 'l' }, { "port", required_argument, NULL, 'p' }, { "nouuxqt", no_argument, NULL, 'q' }, { "master", no_argument, NULL, 3 }, { "slave", no_argument, NULL, 4 }, { "system", required_argument, NULL, 's' }, { "login", required_argument, NULL, 'u' }, { "wait", no_argument, NULL, 'w' }, { "try-next", no_argument, NULL, 'z' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; int main (argc, argv) int argc; char **argv; { /* -c: Whether to be quiet. */ boolean fquiet = FALSE; /* -C: Only call the system if there is work. */ boolean fifwork = FALSE; /* -D: don't detach from controlling terminal. */ boolean fdetach = TRUE; /* -e: Whether to do an endless loop of accepting calls. */ boolean fendless = FALSE; /* -f: Whether to force a call despite status of previous call. */ boolean fforce = FALSE; /* -i type: type of port to use for stdin. */ enum uuconf_porttype tstdintype = UUCONF_PORTTYPE_STDIN; /* -I file: configuration file name. */ const char *zconfig = NULL; /* -l: Whether to give a single login prompt. */ boolean flogin = FALSE; /* -P port: port to use; in master mode, call out on this port. In slave mode, accept logins on this port. If port not specified, then in master mode figure it out for each system, and in slave mode use stdin and stdout. */ const char *zport = NULL; /* -q: Whether to start uuxqt when done. */ boolean fuuxqt = TRUE; /* -r1: Whether we are the master. */ boolean fmaster = FALSE; /* -s,-S system: system to call. */ const char *zsystem = NULL; /* -u: Login name to use. */ const char *zlogin = NULL; /* -w: Whether to wait for a call after doing one. */ boolean fwait = FALSE; /* -z: Try next alternate if call fails. */ boolean ftrynext = FALSE; const char *zopts; int iopt; struct uuconf_port *qport; struct uuconf_port sport; boolean fret = TRUE; pointer puuconf; int iuuconf; #if DEBUG > 1 int iholddebug; #endif zProgram = argv[0]; /* When uucico is invoked by login, the first character of the program will be a dash. We don't want that. */ if (*zProgram == '-') ++zProgram; #if COHERENT_C_OPTION zopts = "c:CDefi:I:lp:qr:s:S:u:x:X:vwz"; #else zopts = "cCDefi:I:lp:qr:s:S:u:x:X:vwz"; #endif while ((iopt = getopt_long (argc, argv, zopts, asLongopts, (int *) NULL)) != EOF) { #if COHERENT_C_OPTION if (iopt == 'c') { iopt = 's'; fifwork = TRUE; } #endif switch (iopt) { case 2: case 'c': /* Don't warn if a call is attempted at a bad time, and don't print the "No work" message. */ fquiet = TRUE; break; case 'C': fifwork = TRUE; break; case 'D': /* Don't detach from controlling terminal. */ fdetach = FALSE; break; case 'e': /* Do an endless loop of accepting calls. */ fendless = TRUE; break; case 'f': /* Force a call even if it hasn't been long enough since the last failed call. */ fforce = TRUE; break; case 'i': /* Type of port to use for standard input. Only TLI is supported here, and only if HAVE_TLI is true. This permits the Network Listener to tell uucico to use TLI I/O calls. */ if (strcasecmp (optarg, "tli") != 0) fprintf (stderr, "%s: unsupported port type \"%s\"\n", zProgram, optarg); else { #if HAVE_TLI tstdintype = UUCONF_PORTTYPE_TLI; #else fprintf (stderr, "%s: not compiled with TLI support\n", zProgram); #endif } break; case 'l': /* Prompt for login name and password. */ flogin = TRUE; break; case 'p': /* Port to use */ zport = optarg; break; case 'q': /* Don't start uuxqt. */ fuuxqt = FALSE; break; case 'r': /* Set mode: -r1 for master, -r0 for slave (default) */ if (strcmp (optarg, "1") == 0) fmaster = TRUE; else if (strcmp (optarg, "0") == 0) fmaster = FALSE; else uusage (); break; case 's': /* Set system name */ zsystem = optarg; fmaster = TRUE; break; case 'S': /* Set system name and force call like -f */ zsystem = optarg; fforce = TRUE; fmaster = TRUE; break; case 'u': /* Some versions of uucpd invoke uucico with a -u argument specifying the login name. If invoked by a privileged user, we use it instead of the result of zsysdep_login_name. */ if (fsysdep_privileged ()) zlogin = optarg; else fprintf (stderr, "%s: ignoring command line login name: not a privileged user\n", zProgram); break; case 'w': /* Call out and then wait for a call in */ fwait = TRUE; break; case 'z': /* Try next alternate if call fails. */ ftrynext = TRUE; break; case 'I': /* Set configuration file name (default is in sysdep.h). */ if (fsysdep_other_config (optarg)) zconfig = optarg; break; case 'x': case 'X': #if DEBUG > 1 /* Set debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'v': /* Print version and exit. */ printf ("uucico (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002, 2003 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 4: /* --slave. */ fmaster = FALSE; break; case 3: /* --master. */ fmaster = TRUE; break; case 1: /* --help. */ uhelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found, and flag value set. */ break; default: uusage (); /*NOTREACHED*/ } } if (optind != argc) uusage (); if (fwait && zport == NULL) { fprintf (stderr, "%s: -w requires -p", zProgram); uusage (); } iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); pUuconf = puuconf; #if DEBUG > 1 { const char *zdebug; iuuconf = uuconf_debuglevel (puuconf, &zdebug); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (zdebug != NULL) iDebug |= idebug_parse (zdebug); } #endif /* If a port was named, get its information. */ if (zport == NULL) qport = NULL; else { iuuconf = uuconf_find_port (puuconf, zport, (long) 0, (long) 0, (int (*) P((struct uuconf_port *, pointer))) NULL, (pointer) NULL, &sport); if (iuuconf == UUCONF_NOT_FOUND) ulog (LOG_FATAL, "%s: port not found", zport); else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); qport = &sport; } #ifdef SIGINT usysdep_signal (SIGINT); #endif #ifdef SIGHUP usysdep_signal (SIGHUP); #endif #ifdef SIGQUIT usysdep_signal (SIGQUIT); #endif #ifdef SIGTERM usysdep_signal (SIGTERM); #endif #ifdef SIGPIPE usysdep_signal (SIGPIPE); #endif usysdep_initialize (puuconf, INIT_SUID); ulog_to_file (puuconf, TRUE); ulog_fatal_fn (uabort); if (fmaster) { if (zsystem != NULL) { /* A system was named. Call it. */ iuuconf = uuconf_system_info (puuconf, zsystem, &sLocked_system); if (iuuconf == UUCONF_NOT_FOUND) ulog (LOG_FATAL, "%s: System not found", zsystem); else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); /* Detach from the controlling terminal for the call. This probably makes sense only on Unix. We want the modem line to become the controlling terminal. */ if (fdetach && (qport == NULL || qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN)) usysdep_detach (); ulog_system (sLocked_system.uuconf_zname); #if DEBUG > 1 iholddebug = iDebug; if (sLocked_system.uuconf_zdebug != NULL) iDebug |= idebug_parse (sLocked_system.uuconf_zdebug); #endif if (! fsysdep_lock_system (&sLocked_system)) { ulog (LOG_ERROR, "System already locked"); fret = FALSE; } else { fLocked_system = TRUE; fret = fcall (puuconf, zconfig, fuuxqt, &sLocked_system, qport, fifwork, fforce, fdetach, fquiet, ftrynext); if (fLocked_system) { (void) fsysdep_unlock_system (&sLocked_system); fLocked_system = FALSE; } } #if DEBUG > 1 iDebug = iholddebug; #endif ulog_system ((const char *) NULL); (void) uuconf_system_free (puuconf, &sLocked_system); } else { char **pznames, **pz; int c, i; boolean fdidone; /* Call all systems which have work to do. */ fret = TRUE; fdidone = FALSE; iuuconf = uuconf_system_names (puuconf, &pznames, 0); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); /* Randomize the order in which we call the systems. */ c = 0; for (pz = pznames; *pz != NULL; pz++) c++; srand ((unsigned int) ixsysdep_time ((long *) NULL)); for (i = c - 1; i > 0; i--) { int iuse; char *zhold; iuse = rand () % (i + 1); zhold = pznames[i]; pznames[i] = pznames[iuse]; pznames[iuse] = zhold; } for (pz = pznames; *pz != NULL && ! FGOT_SIGNAL (); pz++) { iuuconf = uuconf_system_info (puuconf, *pz, &sLocked_system); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); xfree ((pointer) *pz); continue; } if (fsysdep_has_work (&sLocked_system)) { fdidone = TRUE; /* Detach from the controlling terminal. On Unix this means that we will wind up forking a new process for each system we call. */ if (fdetach && (qport == NULL || qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN)) usysdep_detach (); ulog_system (sLocked_system.uuconf_zname); #if DEBUG > 1 iholddebug = iDebug; if (sLocked_system.uuconf_zdebug != NULL) iDebug |= idebug_parse (sLocked_system.uuconf_zdebug); #endif if (! fsysdep_lock_system (&sLocked_system)) { ulog (LOG_ERROR, "System already locked"); fret = FALSE; } else { fLocked_system = TRUE; if (! fcall (puuconf, zconfig, fuuxqt, &sLocked_system, qport, TRUE, fforce, fdetach, fquiet, ftrynext)) fret = FALSE; /* Now ignore any SIGHUP that we got. */ afSignal[INDEXSIG_SIGHUP] = FALSE; if (fLocked_system) { (void) fsysdep_unlock_system (&sLocked_system); fLocked_system = FALSE; } } #if DEBUG > 1 iDebug = iholddebug; #endif ulog_system ((const char *) NULL); } (void) uuconf_system_free (puuconf, &sLocked_system); xfree ((pointer) *pz); } xfree ((pointer) pznames); if (! fdidone && ! fquiet) ulog (LOG_NORMAL, "No work"); } /* If requested, wait for calls after dialing out. */ if (fwait) { fendless = TRUE; fmaster = FALSE; } } if (! fmaster) { struct sconnection sconn; boolean flocked; /* If a port was specified by name, we go into endless loop mode. In this mode, we wait for calls and prompt them with "login:" and "Password:", so that they think we are a regular UNIX system. If we aren't in endless loop mode, we have been called by some other system. If flogin is TRUE, we prompt with "login:" and "Password:" a single time. */ fret = TRUE; zsystem = NULL; if (! fconn_init (qport, &sconn, tstdintype)) fret = FALSE; if (qport != NULL) { /* We are not using standard input. Detach from the controlling terminal, so that the port we are about to use becomes our controlling terminal. */ if (fdetach && qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN) usysdep_detach (); } if (fconn_lock (&sconn, TRUE, FALSE)) flocked = TRUE; else { flocked = FALSE; ulog (LOG_ERROR, "%s: Port already locked", qport->uuconf_zname); fret = FALSE; } if (fret) { if (! fconn_open (&sconn, (long) 0, (long) 0, TRUE, FALSE)) fret = FALSE; qConn = &sconn; } if (fret) { if (fendless) { while (! FGOT_SIGNAL () && flogin_prompt (puuconf, zconfig, fuuxqt, &sconn, (const char *) NULL, (const char **) NULL)) { /* Close and reopen the port in between calls. */ if (! fconn_close (&sconn, puuconf, (struct uuconf_dialer *) NULL, TRUE) || ! fconn_open (&sconn, (long) 0, (long) 0, TRUE, FALSE)) break; } fret = FALSE; } else { if (flogin) fret = flogin_prompt (puuconf, zconfig, fuuxqt, &sconn, zlogin, &zsystem); else { #if DEBUG > 1 iholddebug = iDebug; #endif if (zlogin == NULL) zlogin = zsysdep_login_name (); fret = faccept_call (puuconf, zconfig, fuuxqt, zlogin, &sconn, &zsystem); #if DEBUG > 1 iDebug = iholddebug; #endif } } } if (qConn != NULL) { if (! fconn_close (&sconn, puuconf, (struct uuconf_dialer *) NULL, fret)) fret = FALSE; qConn = NULL; } if (flocked) (void) fconn_unlock (&sconn); uconn_free (&sconn); } ulog_close (); ustats_close (); /* If we got a SIGTERM, perhaps because the system is going down, don't run uuxqt. We go ahead and run it for any other signal, since I think they indicate more temporary conditions. */ if (afSignal[INDEXSIG_SIGTERM]) fuuxqt = FALSE; if (fuuxqt) { int irunuuxqt; iuuconf = uuconf_runuuxqt (puuconf, &irunuuxqt); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_ERROR, puuconf, iuuconf); else if (irunuuxqt == UUCONF_RUNUUXQT_ONCE) { /* Detach from the controlling terminal before starting up uuxqt, so that it runs as a true daemon. */ if (fdetach) usysdep_detach (); if (! fspawn_uuxqt (FALSE, zsystem, zconfig)) fret = FALSE; } } usysdep_exit (fret); /* Avoid complaints about not returning. */ return 0; } /* Print out a usage message and die. */ static void uusage () { fprintf (stderr, "Usage: %s [options]\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EXIT_FAILURE); } /* Print a help message. */ static void uhelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); printf ("Usage: %s [options]\n", zProgram); printf (" -s,-S,--system system: Call system (-S implies -f)\n"); printf (" -f,--force: Force call despite system status\n"); printf (" -r state: 1 for master, 0 for slave (default)\n"); printf (" --master: Act as master\n"); printf (" --slave: Act as slave (default)\n"); printf (" -p,--port port: Specify port\n"); printf (" -l,--prompt: Prompt for login name and password\n"); printf (" -e,--loop: Endless loop of login prompts and daemon execution\n"); printf (" -w,--wait: After calling out, wait for incoming calls\n"); printf (" -q,--nouuxqt: Don't start uuxqt when done\n"); printf (" -c,--quiet: Don't log bad time or no work warnings\n"); printf (" -C,--ifwork: Only call named system if there is work\n"); printf (" -D,--nodetach: Don't detach from controlling terminal\n"); printf (" -u,--login: Set login name (privileged users only)\n"); printf (" -i,--stdin type: Type of standard input (only TLI supported)\n"); printf (" -z,--try-next: If a call fails, try the next alternate\n"); printf (" -x,-X,--debug debug: Set debugging level\n"); #if HAVE_TAYLOR_CONFIG printf (" -I,--config file: Set configuration file to use\n"); #endif /* HAVE_TAYLOR_CONFIG */ printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } /* This function is called when a LOG_FATAL error occurs. */ static void uabort () { if (fLocked_system) ufailed (&sDaemon); ulog_user ((const char *) NULL); if (qConn != NULL) { (void) fconn_close (qConn, pUuconf, (struct uuconf_dialer *) NULL, FALSE); (void) fconn_unlock (qConn); uconn_free (qConn); } if (fLocked_system) { (void) fsysdep_unlock_system (&sLocked_system); fLocked_system = FALSE; } ulog_system ((const char *) NULL); ulog_close (); ustats_close (); usysdep_exit (FALSE); } /* The number of seconds in one day. We must cast to long for this to be calculated correctly on a machine with 16 bit ints. */ #define SECS_PER_DAY ((long) 24 * (long) 60 * (long) 60) /* Call another system, trying all the possible sets of calling instructions. The qsys argument is the system to call. The qport argument is the port to use, and may be NULL. If the fifwork argument is TRUE, the call is only placed if there is work to be done. If the fforce argument is TRUE, a call is forced even if not enough time has passed since the last failed call. If the fquiet argument is FALSE (the normal case), then a warning is given if calls are not permitted at this time. */ static boolean fcall (puuconf, zconfig, fuuxqt, qorigsys, qport, fifwork, fforce, fdetach, fquiet, ftrynext) pointer puuconf; const char *zconfig; boolean fuuxqt; const struct uuconf_system *qorigsys; struct uuconf_port *qport; boolean fifwork; boolean fforce; boolean fdetach; boolean fquiet; boolean ftrynext; { struct sstatus sstat; long inow; boolean fbadtime, fnevertime, ffoundwork; const struct uuconf_system *qsys; if (! fsysdep_get_status (qorigsys, &sstat, (boolean *) NULL)) return FALSE; ubuffree (sstat.zstring); /* Make sure it's been long enough since the last failed call, and that we haven't exceeded the maximum number of retries. Even if we are over the limit on retries, we permit a call to be made if 24 hours have passed. This 24 hour limit is still controlled by the retry time. We ignore times in the future, presumably the result of some sort of error. */ inow = ixsysdep_time ((long *) NULL); if (! fforce) { if (qorigsys->uuconf_cmax_retries > 0 && sstat.cretries >= qorigsys->uuconf_cmax_retries && sstat.ilast <= inow && sstat.ilast + SECS_PER_DAY > inow) { ulog (LOG_ERROR, "Too many retries"); return FALSE; } if ((sstat.ttype == STATUS_COMPLETE ? sstat.ilast + qorigsys->uuconf_csuccess_wait > inow : sstat.ilast + sstat.cwait > inow) && sstat.ilast <= inow) { ulog (LOG_NORMAL, "Retry time not reached"); return FALSE; } } sDaemon.puuconf = puuconf; sDaemon.zconfig = zconfig; if (! fuuxqt) sDaemon.irunuuxqt = UUCONF_RUNUUXQT_NEVER; else { int iuuconf; iuuconf = uuconf_runuuxqt (puuconf, &sDaemon.irunuuxqt); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_ERROR, puuconf, iuuconf); } fbadtime = TRUE; fnevertime = TRUE; ffoundwork = FALSE; for (qsys = qorigsys; qsys != NULL; qsys = qsys->uuconf_qalternate) { int cretry; boolean fany, fret, fcalled; if (FGOT_SIGNAL ()) return FALSE; if (! qsys->uuconf_fcall || qsys->uuconf_qtimegrade == NULL) continue; /* If a port was specified, and this alternate does not use the specified port, but a later alternate does use the specified port, skip this alternate. This permits specifying a port as a way to select a particular alternate. There probably ought to be a way to select a specific alternate, but there isn't. */ if (qport != NULL && (qsys->uuconf_qport != NULL || (qsys->uuconf_zport != NULL && strcmp (qport->uuconf_zname, qsys->uuconf_zport) != 0))) { const struct uuconf_system *ql; for (ql = qsys->uuconf_qalternate; ql != NULL; ql = ql->uuconf_qalternate) { if (ql->uuconf_qport == NULL && ql->uuconf_zport != NULL && strcmp (ql->uuconf_zport, qport->uuconf_zname) == 0) break; } if (ql != NULL) continue; } fnevertime = FALSE; /* Make sure this is a legal time to call. */ if (! ftimespan_match (qsys->uuconf_qtimegrade, (long *) NULL, &cretry)) continue; fbadtime = FALSE; sDaemon.qsys = qsys; sDaemon.zlocalname = NULL; sDaemon.qconn = NULL; sDaemon.qproto = NULL; sDaemon.cchans = 1; sDaemon.clocal_size = -1; sDaemon.cremote_size = -1; sDaemon.cmax_ever = -2; sDaemon.cmax_receive = -1; sDaemon.csent = 0; sDaemon.creceived = 0; sDaemon.cxfiles_received = 0; sDaemon.ifeatures = 0; sDaemon.frequest_hangup = FALSE; sDaemon.fhangup_requested = FALSE; sDaemon.fhangup = FALSE; sDaemon.fmaster = TRUE; sDaemon.fcaller = TRUE; sDaemon.ireliable = 0; sDaemon.bgrade = '\0'; /* Queue up any work there is to do. */ if (! fqueue (&sDaemon, &fany)) return FALSE; /* If we are only supposed to call if there is work, and there isn't any work, check the next alternates. We can't give up at this point because there might be some other alternates with fewer restrictions on grade or file transfer size. */ if (fifwork && ! fany) { uclear_queue (&sDaemon); continue; } ffoundwork = TRUE; fret = fconn_call (&sDaemon, qport, &sstat, cretry, &fcalled); uclear_queue (&sDaemon); if (fret) return TRUE; if (fcalled && ! ftrynext) return FALSE; /* Now we have to dump that port so that we can aquire a new one. On Unix this means that we will fork and get a new process ID, so we must unlock and relock the system. */ if (fdetach) { (void) fsysdep_unlock_system (&sLocked_system); fLocked_system = FALSE; usysdep_detach (); if (! fsysdep_lock_system (&sLocked_system)) return FALSE; fLocked_system = TRUE; } } /* We only get here if no call succeeded. If fbadtime is TRUE it was the wrong time for all the alternates. Otherwise, if ffoundwork is FALSE there was no work for any of the alternates. Otherwise, we attempted a call and fconn_call logged an error message. */ if (fbadtime) { if (! fquiet) ulog (LOG_NORMAL, "Wrong time to call"); /* Update the status, unless the system can never be called. If the system can never be called, there is little point to putting in a ``wrong time to call'' message. We don't change the number of retries, although we do set the wait until the next retry to 0. */ if (! fnevertime) { sstat.ttype = STATUS_WRONG_TIME; sstat.ilast = inow; sstat.cwait = 0; (void) fsysdep_set_status (qorigsys, &sstat); } } else if (! ffoundwork) { if (! fquiet) ulog (LOG_NORMAL, "No work"); return TRUE; } return FALSE; } /* Find a port to use when calling a system, open a connection, and dial the system. The actual call is done in fdo_call. This routine is responsible for opening and closing the connection. */ static boolean fconn_call (qdaemon, qport, qstat, cretry, pfcalled) struct sdaemon *qdaemon; struct uuconf_port *qport; struct sstatus *qstat; int cretry; boolean *pfcalled; { pointer puuconf; const struct uuconf_system *qsys; struct uuconf_port sport; struct sconnection sconn; enum tstatus_type terr; boolean fret; puuconf = qdaemon->puuconf; qsys = qdaemon->qsys; *pfcalled = FALSE; /* Ignore any SIGHUP signal we may have received up to this point. This is needed on Unix because we may have gotten one from the shell before we detached from the controlling terminal. */ afSignal[INDEXSIG_SIGHUP] = FALSE; /* If no port was specified on the command line, use any port defined for the system. To select the system port: 1) see if port information was specified directly; 2) see if a port was named; 3) get an available port given the baud rate. We don't change the system status if a port is unavailable; i.e. we don't force the system to wait for the retry time. */ if (qport == NULL) qport = qsys->uuconf_qport; if (qport != NULL) { if (! fconn_init (qport, &sconn, UUCONF_PORTTYPE_UNKNOWN)) return FALSE; if (! fconn_lock (&sconn, FALSE, FALSE)) { ulog (LOG_ERROR, "%s: Port already locked", qport->uuconf_zname); return FALSE; } } else { struct spass s; int iuuconf; s.fmatched = FALSE; s.flocked = FALSE; s.qconn = &sconn; iuuconf = uuconf_find_port (puuconf, qsys->uuconf_zport, qsys->uuconf_ibaud, qsys->uuconf_ihighbaud, iuport_lock, (pointer) &s, &sport); if (iuuconf == UUCONF_NOT_FOUND) { if (! s.fmatched) ulog (LOG_ERROR, "No matching ports"); else { ulog (LOG_ERROR, "All matching ports in use"); qstat->ttype = STATUS_PORT_FAILED; /* We don't change cretries for this case. */ qstat->ilast = ixsysdep_time ((long *) NULL); if (cretry == 0) qstat->cwait = CRETRY_WAIT (qstat->cretries); else qstat->cwait = cretry * 60; (void) fsysdep_set_status (qsys, qstat); } return FALSE; } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); if (s.flocked) { (void) fconn_unlock (&sconn); uconn_free (&sconn); } return FALSE; } } if (! fconn_open (&sconn, qsys->uuconf_ibaud, qsys->uuconf_ihighbaud, FALSE, FALSE)) { terr = STATUS_PORT_FAILED; fret = FALSE; } else { struct uuconf_dialer *qdialer; struct uuconf_dialer sdialer; enum tdialerfound tdialer; if (qsys->uuconf_zalternate == NULL) ulog (LOG_NORMAL, "Calling system %s (port %s)", qsys->uuconf_zname, zLdevice == NULL ? (char *) "unknown" : zLdevice); else ulog (LOG_NORMAL, "Calling system %s (alternate %s, port %s)", qsys->uuconf_zname, qsys->uuconf_zalternate, zLdevice == NULL ? (char *) "unknown" : zLdevice); qdialer = NULL; if (! fconn_dial (&sconn, puuconf, qsys, qsys->uuconf_zphone, &sdialer, &tdialer)) { tdialer = DIALERFOUND_FALSE; terr = STATUS_DIAL_FAILED; fret = FALSE; } else { qdaemon->qconn = &sconn; if (tdialer == DIALERFOUND_FALSE) qdialer = NULL; else qdialer = &sdialer; fret = fdo_call (qdaemon, qstat, qdialer, pfcalled, &terr); } (void) fconn_close (&sconn, puuconf, qdialer, fret); if (tdialer == DIALERFOUND_FREE) (void) uuconf_dialer_free (puuconf, &sdialer); } if (! fret) { DEBUG_MESSAGE2 (DEBUG_HANDSHAKE, "Call failed: %d (%s)", (int) terr, azStatus[(int) terr]); qstat->ttype = terr; qstat->cretries++; qstat->ilast = ixsysdep_time ((long *) NULL); if (cretry == 0) qstat->cwait = CRETRY_WAIT (qstat->cretries); else qstat->cwait = cretry * 60; (void) fsysdep_set_status (qsys, qstat); } (void) fconn_unlock (&sconn); uconn_free (&sconn); if (qport == NULL) (void) uuconf_port_free (puuconf, &sport); return fret; } /* Do the actual work of calling another system. The qsys argument is the system to call, the qconn argument is the connection to use, the qstat argument holds the current status of the ssystem, and the qdialer argument holds the dialer being used (it may be NULL). If we log in successfully, set *pfcalled to TRUE; this is used to distinguish a failed dial from a failure during the call. If an error occurs *pterr is set to the status type to record. */ static boolean fdo_call (qdaemon, qstat, qdialer, pfcalled, pterr) struct sdaemon *qdaemon; struct sstatus *qstat; const struct uuconf_dialer *qdialer; boolean *pfcalled; enum tstatus_type *pterr; { pointer puuconf; const struct uuconf_system *qsys; struct sconnection *qconn; int iuuconf; int istrip; boolean fstrip; const char *zport; char *zstr; long istart_time; char *zlog; puuconf = qdaemon->puuconf; qsys = qdaemon->qsys; qconn = qdaemon->qconn; iuuconf = uuconf_strip (puuconf, &istrip); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } fstrip = (istrip & UUCONF_STRIP_PROTO) != 0; *pterr = STATUS_LOGIN_FAILED; if (qconn->qport == NULL) zport = "unknown"; else zport = qconn->qport->uuconf_zname; if (! fchat (qconn, puuconf, &qsys->uuconf_schat, qsys, (const struct uuconf_dialer *) NULL, (const char *) NULL, FALSE, zport, iconn_baud (qconn))) return FALSE; *pfcalled = TRUE; istart_time = ixsysdep_time ((long *) NULL); *pterr = STATUS_HANDSHAKE_FAILED; /* We should now see "Shere" from the other system. Newer systems send "Shere=foo" where foo is the remote name. */ zstr = zget_uucp_cmd (qconn, TRUE, fstrip); if (zstr == NULL) return FALSE; if (strncmp (zstr, "Shere", 5) != 0) { ulog (LOG_ERROR, "Bad startup string (expected \"Shere\" got \"%s\")", zstr); ubuffree (zstr); return FALSE; } ulog (LOG_NORMAL, "Login successful"); qstat->ttype = STATUS_TALKING; qstat->ilast = ixsysdep_time ((long *) NULL); qstat->cretries = 0; qstat->cwait = 0; if (! fsysdep_set_status (qsys, qstat)) return FALSE; if (zstr[5] == '=') { const char *zheresys; size_t clen; int icmp; /* Some UUCP packages only provide seven characters in the Shere machine name. Others only provide fourteen. */ zheresys = zstr + 6; clen = strlen (zheresys); if (clen == 7 || clen == 14) icmp = strncmp (zheresys, qsys->uuconf_zname, clen); else icmp = strcmp (zheresys, qsys->uuconf_zname); if (icmp != 0) { if (qsys->uuconf_pzalias != NULL) { char **pz; for (pz = qsys->uuconf_pzalias; *pz != NULL; pz++) { if (clen == 7 || clen == 14) icmp = strncmp (zheresys, *pz, clen); else icmp = strcmp (zheresys, *pz); if (icmp == 0) break; } } if (icmp != 0) { ulog (LOG_ERROR, "Called wrong system (%s)", zheresys); ubuffree (zstr); return FALSE; } } } #if DEBUG > 1 else if (zstr[5] != '\0') DEBUG_MESSAGE1 (DEBUG_HANDSHAKE, "fdo_call: Strange Shere: %s", zstr); #endif ubuffree (zstr); /* We now send "S" name switches, where name is our UUCP name. If we are using sequence numbers with this system, we send a -Q argument with the sequence number. If the call-timegrade command was used, we send a -p argument and a -vgrade= argument with the grade to send us (we send both argument to make it more likely that one is recognized). We always send a -N (for new) switch indicating what new features we support. */ { long ival; char bgrade; char *zsend; boolean fret; /* Determine the grade we should request of the other system. A '\0' means that no restrictions have been made. */ if (! ftimespan_match (qsys->uuconf_qcalltimegrade, &ival, (int *) NULL)) bgrade = '\0'; else bgrade = (char) ival; /* Determine the name we will call ourselves. */ if (qsys->uuconf_zlocalname != NULL) qdaemon->zlocalname = qsys->uuconf_zlocalname; else { iuuconf = uuconf_localname (puuconf, &qdaemon->zlocalname); if (iuuconf == UUCONF_NOT_FOUND) { qdaemon->zlocalname = zsysdep_localname (); if (qdaemon->zlocalname == NULL) return FALSE; } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } } zsend = zbufalc (strlen (qdaemon->zlocalname) + 70); if (! qsys->uuconf_fsequence) { if (bgrade == '\0') sprintf (zsend, "S%s -R -N0%o", qdaemon->zlocalname, (unsigned int) (FEATURE_SIZES | FEATURE_EXEC | FEATURE_RESTART | FEATURE_QUOTES)); else sprintf (zsend, "S%s -p%c -vgrade=%c -R -N0%o", qdaemon->zlocalname, bgrade, bgrade, (unsigned int) (FEATURE_SIZES | FEATURE_EXEC | FEATURE_RESTART | FEATURE_QUOTES)); } else { long iseq; iseq = ixsysdep_get_sequence (qsys); if (iseq < 0) return FALSE; if (bgrade == '\0') sprintf (zsend, "S%s -Q%ld -R -N0%o", qdaemon->zlocalname, iseq, (unsigned int) (FEATURE_SIZES | FEATURE_EXEC | FEATURE_RESTART | FEATURE_QUOTES)); else sprintf (zsend, "S%s -Q%ld -p%c -vgrade=%c -R -N0%o", qdaemon->zlocalname, iseq, bgrade, bgrade, (unsigned int) (FEATURE_SIZES | FEATURE_EXEC | FEATURE_RESTART | FEATURE_QUOTES)); } fret = fsend_uucp_cmd (qconn, zsend); ubuffree (zsend); if (! fret) return FALSE; } /* Now we should see ROK or Rreason where reason gives a cryptic reason for failure. If we are talking to a counterpart, we will get back ROKN, possibly with a feature bitfield attached. */ zstr = zget_uucp_cmd (qconn, TRUE, fstrip); if (zstr == NULL) return FALSE; if (zstr[0] != 'R') { ulog (LOG_ERROR, "Bad response to handshake string (%s)", zstr); ubuffree (zstr); return FALSE; } if (strncmp (zstr + 1, "OKN", sizeof "OKN" - 1) == 0) { if (zstr[sizeof "ROKN" - 1] == '\0') qdaemon->ifeatures |= FEATURE_SIZES | FEATURE_V103; else qdaemon->ifeatures |= (int) strtol (zstr + sizeof "ROKN" - 1, (char **) NULL, 0); } else if (strncmp (zstr + 1, "OK", sizeof "OK" - 1) == 0) { if (zstr[sizeof "ROK" - 1] != '\0') { char *zopt; /* SVR4 UUCP returns options following the ROK string. */ zopt = zstr + sizeof "ROK" - 1; while (*zopt != '\0') { char b; long c; char *zend; b = *zopt++; if (isspace (b) || b != '-') continue; switch (*zopt) { case 'R': qdaemon->ifeatures |= (FEATURE_RESTART | FEATURE_SVR4 | FEATURE_SIZES); break; case 'U': c = strtol (zopt, &zend, 0); if (c > 0 && c <= LONG_MAX / (long) 512) qdaemon->cmax_receive = c * (long) 512; zopt = zend; break; } while (*zopt != '\0' && ! isspace (*zopt)) ++zopt; } } } else if (strcmp (zstr + 1, "CB") == 0) { ulog (LOG_NORMAL, "Remote system will call back"); qstat->ttype = STATUS_COMPLETE; (void) fsysdep_set_status (qsys, qstat); ubuffree (zstr); return TRUE; } else { ulog (LOG_ERROR, "Handshake failed (%s)", zstr + 1); ubuffree (zstr); return FALSE; } ubuffree (zstr); /* The slave should now send \020Pprotos\0 where protos is a list of supported protocols. Each protocol is a single character. */ zstr = zget_uucp_cmd (qconn, TRUE, fstrip); if (zstr == NULL) return FALSE; if (zstr[0] != 'P') { ulog (LOG_ERROR, "Bad protocol handshake (%s)", zstr); ubuffree (zstr); return FALSE; } /* Determine the reliability characteristics of the connection by combining information for the port and the dialer. If we have no information, default to a reliable eight-bit full-duplex connection. */ if (qconn->qport != NULL && (qconn->qport->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) qdaemon->ireliable = qconn->qport->uuconf_ireliable; if (qdialer != NULL && (qdialer->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) { if (qdaemon->ireliable != 0) qdaemon->ireliable &= qdialer->uuconf_ireliable; else qdaemon->ireliable = qdialer->uuconf_ireliable; } if (qdaemon->ireliable == 0) qdaemon->ireliable = (UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX | UUCONF_RELIABLE_SPECIFIED); /* Now decide which protocol to use. The system and the port may have their own list of protocols. */ { size_t i; char ab[5]; i = CPROTOCOLS; if (qsys->uuconf_zprotocols != NULL || (qconn->qport != NULL && qconn->qport->uuconf_zprotocols != NULL)) { const char *zproto; if (qsys->uuconf_zprotocols != NULL) zproto = qsys->uuconf_zprotocols; else zproto = qconn->qport->uuconf_zprotocols; for (; *zproto != '\0'; zproto++) { if (strchr (zstr + 1, *zproto) != NULL) { for (i = 0; i < CPROTOCOLS; i++) if (asProtocols[i].bname == *zproto) break; if (i < CPROTOCOLS) break; } } } else { /* If neither the system nor the port specified a list of protocols, we want only protocols that match the known reliability of the dialer and the port. */ for (i = 0; i < CPROTOCOLS; i++) { int ipr; ipr = asProtocols[i].ireliable; if ((ipr & qdaemon->ireliable) != ipr) continue; if (strchr (zstr + 1, asProtocols[i].bname) != NULL) break; } } ubuffree (zstr); if (i >= CPROTOCOLS) { (void) fsend_uucp_cmd (qconn, "UN"); ulog (LOG_ERROR, "No mutually supported protocols"); return FALSE; } qdaemon->qproto = &asProtocols[i]; /* If we are using a half-duplex line, act as though we have only a single channel; otherwise we might start a send and a receive at the same time. */ if ((qdaemon->ireliable & UUCONF_RELIABLE_FULLDUPLEX) == 0) qdaemon->cchans = 1; else qdaemon->cchans = asProtocols[i].cchans; sprintf (ab, "U%c", qdaemon->qproto->bname); if (! fsend_uucp_cmd (qconn, ab)) return FALSE; } /* Run any protocol parameter commands. */ if (qdaemon->qproto->qcmds != NULL) { if (qsys->uuconf_qproto_params != NULL) uapply_proto_params (puuconf, qdaemon->qproto->bname, qdaemon->qproto->qcmds, qsys->uuconf_qproto_params); if (qconn->qport != NULL && qconn->qport->uuconf_qproto_params != NULL) uapply_proto_params (puuconf, qdaemon->qproto->bname, qdaemon->qproto->qcmds, qconn->qport->uuconf_qproto_params); if (qdialer != NULL && qdialer->uuconf_qproto_params != NULL) uapply_proto_params (puuconf, qdaemon->qproto->bname, qdaemon->qproto->qcmds, qdialer->uuconf_qproto_params); } /* Turn on the selected protocol. */ if (! (*qdaemon->qproto->pfstart) (qdaemon, &zlog)) return FALSE; if (zlog == NULL) { zlog = zbufalc (sizeof "protocol ''" + 1); sprintf (zlog, "protocol '%c'", qdaemon->qproto->bname); } ulog (LOG_NORMAL, "Handshake successful (%s)", zlog); ubuffree (zlog); *pterr = STATUS_FAILED; { boolean fret; long iend_time; fret = floop (qdaemon); /* Now send the hangup message. As the caller, we send six O's and expect to receive seven O's. We send the six O's twice to help the other side. We don't worry about errors here. */ if (fsend_uucp_cmd (qconn, "OOOOOO") && fsend_uucp_cmd (qconn, "OOOOOO")) { int i, fdone; /* We look for the remote hangup string to ensure that the modem has sent out our hangup string. This is only necessary because some versions of UUCP complain if they don't get the hangup string. The remote site should send 7 O's, but some versions of UUCP only send 6. We look for the string several times because supposedly some implementations send some garbage after the last packet but before the hangup string. */ for (i = 0; i < 25; i++) { zstr = zget_uucp_cmd (qconn, FALSE, fstrip); if (zstr == NULL) break; fdone = strstr (zstr, "OOOOOO") != NULL; ubuffree (zstr); if (fdone) break; } } iend_time = ixsysdep_time ((long *) NULL); ulog (LOG_NORMAL, "Call complete (%ld seconds %ld bytes %ld bps)", iend_time - istart_time, qdaemon->csent + qdaemon->creceived, (iend_time != istart_time ? (qdaemon->csent + qdaemon->creceived) / (iend_time - istart_time) : 0)); if (fret) { qstat->ttype = STATUS_COMPLETE; qstat->ilast = iend_time; (void) fsysdep_set_status (qsys, qstat); } if (qdaemon->irunuuxqt == UUCONF_RUNUUXQT_PERCALL || (qdaemon->irunuuxqt > 0 && qdaemon->cxfiles_received > 0)) (void) fspawn_uuxqt (TRUE, qdaemon->qsys->uuconf_zname, qdaemon->zconfig); return fret; } } /* This routine is called via uuconf_find_port when a matching port is found. It tries to lock the port. If it fails, it returns UUCONF_NOT_FOUND to force uuconf_find_port to continue searching for the next matching port. */ static int iuport_lock (qport, pinfo) struct uuconf_port *qport; pointer pinfo; { struct spass *q = (struct spass *) pinfo; q->fmatched = TRUE; if (! fconn_init (qport, q->qconn, UUCONF_PORTTYPE_UNKNOWN)) return UUCONF_NOT_FOUND; else if (! fconn_lock (q->qconn, FALSE, FALSE)) { uconn_free (q->qconn); return UUCONF_NOT_FOUND; } else { q->flocked = TRUE; return UUCONF_SUCCESS; } } /* The information structure used for the uuconf_callin comparison function. */ struct scallin_info { const char *zuser; const char *zpass; }; /* Prompt for a login name and a password, and run as the slave. */ static boolean flogin_prompt (puuconf, zconfig, fuuxqt, qconn, zlogin, pzsystem) pointer puuconf; const char *zconfig; boolean fuuxqt; struct sconnection *qconn; const char *zlogin; const char **pzsystem; { int iuuconf; int istrip; boolean fstrip; char *zuser, *zpass; boolean fret; struct scallin_info s; if (pzsystem != NULL) *pzsystem = NULL; DEBUG_MESSAGE0 (DEBUG_HANDSHAKE, "flogin_prompt: Waiting for login"); iuuconf = uuconf_strip (puuconf, &istrip); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } fstrip = (istrip & UUCONF_STRIP_LOGIN) != 0; zuser = NULL; if (zlogin == NULL) { do { ubuffree (zuser); if (! fconn_write (qconn, "login: ", sizeof "login: " - 1)) return FALSE; zuser = zget_typed_line (qconn, fstrip); } while (zuser != NULL && *zuser == '\0'); if (zuser == NULL) return TRUE; zlogin = zuser; } if (! fconn_write (qconn, "Password:", sizeof "Password:" - 1)) { ubuffree (zuser); return FALSE; } zpass = zget_typed_line (qconn, fstrip); if (zpass == NULL) { ubuffree (zuser); return TRUE; } fret = TRUE; s.zuser = zlogin; s.zpass = zpass; iuuconf = uuconf_callin (puuconf, icallin_cmp, &s); ubuffree (zpass); if (iuuconf == UUCONF_NOT_FOUND) ulog (LOG_ERROR, "Bad login"); else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fret = FALSE; } else { #if DEBUG > 1 int iholddebug; #endif /* We ignore the return value of faccept_call because we really don't care whether the call succeeded or not. We are going to reset the port anyhow. */ #if DEBUG > 1 iholddebug = iDebug; #endif (void) faccept_call (puuconf, zconfig, fuuxqt, zlogin, qconn, pzsystem); #if DEBUG > 1 iDebug = iholddebug; #endif } ubuffree (zuser); return fret; } /* The comparison function which we pass to uuconf_callin. This expands escape sequences in the login name, and either encrypts or expands escape sequences in the password. */ static int icallin_cmp (iwhich, pinfo, zfile) int iwhich; pointer pinfo; const char *zfile; { struct scallin_info *qinfo = (struct scallin_info *) pinfo; char *zcopy; int icmp; #if HAVE_ENCRYPTED_PASSWORDS if (iwhich != 0) return strcmp (crypt (qinfo->zpass, zfile), zfile) == 0; #endif zcopy = zbufcpy (zfile); (void) cescape (zcopy); if (iwhich == 0) icmp = strcmp (qinfo->zuser, zcopy); else icmp = strcmp (qinfo->zpass, zcopy); ubuffree (zcopy); return icmp == 0; } /* Accept a call from a remote system. If pqsys is not NULL, *pqsys will be set to the system that called in if known. */ static boolean faccept_call (puuconf, zconfig, fuuxqt, zlogin, qconn, pzsystem) pointer puuconf; const char *zconfig; boolean fuuxqt; const char *zlogin; struct sconnection *qconn; const char **pzsystem; { long istart_time; int iuuconf; int istrip; boolean fstrip; const char *zport; struct uuconf_port *qport; struct uuconf_port sport; struct uuconf_dialer *qdialer; struct uuconf_dialer sdialer; boolean ftcp_port; char *zsend, *zspace; boolean fret; char *zstr; struct uuconf_system ssys; const struct uuconf_system *qsys; const struct uuconf_system *qany; char *zloc; struct sstatus sstat; boolean fgotseq, fgotn; size_t i; char *zlog; char *zgrade; if (pzsystem != NULL) *pzsystem = NULL; ulog (LOG_NORMAL, "Incoming call (login %s port %s)", zlogin, zLdevice == NULL ? (char *) "unknown" : zLdevice); istart_time = ixsysdep_time ((long *) NULL); iuuconf = uuconf_strip (puuconf, &istrip); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, (struct uuconf_port *) NULL, &sport, (char *) NULL); return FALSE; } fstrip = (istrip & UUCONF_STRIP_PROTO) != 0; /* Figure out protocol parameters determined by the port. If no port was specified we're reading standard input, so try to get the port name and read information from the port file. We only use the port information to get protocol parameters; we don't want to start treating the port as though it were a modem, for example. */ if (qconn->qport != NULL) { qport = qconn->qport; zport = qport->uuconf_zname; ftcp_port = FALSE; } else { zport = zsysdep_port_name (&ftcp_port); if (zport == NULL) { qport = NULL; zport = "unknown"; } else { iuuconf = uuconf_find_port (puuconf, zport, (long) 0, (long) 0, (int (*) P((struct uuconf_port *, pointer pinfo))) NULL, (pointer) NULL, &sport); if (iuuconf == UUCONF_NOT_FOUND) qport = NULL; else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, (struct uuconf_port *) NULL, &sport, (char *) NULL); return FALSE; } else qport = &sport; } } /* If we've managed to figure out that this is a modem port, now try to get protocol parameters from the dialer. */ qdialer = NULL; if (qport != NULL) { if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM) { if (qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL) { const char *zdialer; zdialer = qport->uuconf_u.uuconf_smodem.uuconf_pzdialer[0]; iuuconf = uuconf_dialer_info (puuconf, zdialer, &sdialer); if (iuuconf == UUCONF_SUCCESS) qdialer = &sdialer; } else qdialer = qport->uuconf_u.uuconf_smodem.uuconf_qdialer; } else if (qport->uuconf_ttype == UUCONF_PORTTYPE_TCP || (qport->uuconf_ttype == UUCONF_PORTTYPE_TLI && (qport->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) == 0)) ftcp_port = TRUE; } sDaemon.puuconf = puuconf; sDaemon.zconfig = zconfig; if (! fuuxqt) sDaemon.irunuuxqt = UUCONF_RUNUUXQT_NEVER; else { iuuconf = uuconf_runuuxqt (puuconf, &sDaemon.irunuuxqt); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_ERROR, puuconf, iuuconf); } sDaemon.qsys = NULL; sDaemon.zlocalname = NULL; sDaemon.qconn = qconn; sDaemon.qproto = NULL; sDaemon.cchans = 1; sDaemon.clocal_size = -1; sDaemon.cremote_size = -1; sDaemon.cmax_ever = -2; sDaemon.cmax_receive = -1; sDaemon.csent = 0; sDaemon.creceived = 0; sDaemon.cxfiles_received = 0; sDaemon.ifeatures = 0; sDaemon.frequest_hangup = FALSE; sDaemon.fhangup_requested = FALSE; sDaemon.fhangup = FALSE; sDaemon.fmaster = FALSE; sDaemon.fcaller = FALSE; sDaemon.ireliable = 0; sDaemon.bgrade = UUCONF_GRADE_LOW; /* Get the local name to use. If uuconf_login_localname returns a value, it is not always freed up, although it should be. */ iuuconf = uuconf_login_localname (puuconf, zlogin, &zloc); if (iuuconf == UUCONF_SUCCESS) sDaemon.zlocalname = zloc; else if (iuuconf == UUCONF_NOT_FOUND) { sDaemon.zlocalname = zsysdep_localname (); if (sDaemon.zlocalname == NULL) { uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, qport, &sport, (char *) NULL); return FALSE; } } else { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, qport, &sport, (char *) NULL); return FALSE; } /* Tell the remote system who we are. */ zsend = zbufalc (strlen (sDaemon.zlocalname) + sizeof "Shere="); sprintf (zsend, "Shere=%s", sDaemon.zlocalname); fret = fsend_uucp_cmd (qconn, zsend); ubuffree (zsend); if (! fret) { uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, qport, &sport, zloc); return FALSE; } zstr = zget_uucp_cmd (qconn, TRUE, fstrip); if (zstr == NULL) { uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, qport, &sport, zloc); return FALSE; } if (zstr[0] != 'S') { ulog (LOG_ERROR, "Bad introduction string"); ubuffree (zstr); uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, qport, &sport, zloc); return FALSE; } zspace = strchr (zstr, ' '); if (zspace != NULL) *zspace = '\0'; iuuconf = uuconf_system_info (puuconf, zstr + 1, &ssys); if (iuuconf == UUCONF_NOT_FOUND) { char *zscript; /* Run the remote.unknown script, if appropriate. */ iuuconf = uuconf_remote_unknown (puuconf, &zscript); if (iuuconf == UUCONF_SUCCESS) { if (! fsysdep_unknown_caller (zscript, zstr + 1)) { xfree ((pointer) zscript); (void) fsend_uucp_cmd (qconn, "RYou are unknown to me"); ubuffree (zstr); uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, qport, &sport, zloc); return FALSE; } xfree ((pointer) zscript); } else if (iuuconf != UUCONF_NOT_FOUND) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); ubuffree (zstr); uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, qport, &sport, zloc); return FALSE; } if (! funknown_system (puuconf, zstr + 1, &ssys)) { (void) fsend_uucp_cmd (qconn, "RYou are unknown to me"); ulog (LOG_ERROR, "Call from unknown system %s", zstr + 1); ubuffree (zstr); uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, qport, &sport, zloc); return FALSE; } } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); ubuffree (zstr); uaccept_call_cleanup (puuconf, (struct uuconf_system *) NULL, qport, &sport, zloc); return FALSE; } qany = NULL; for (qsys = &ssys; qsys != NULL; qsys = qsys->uuconf_qalternate) { if (! qsys->uuconf_fcalled) continue; if (qsys->uuconf_zcalled_login == NULL || strcmp (qsys->uuconf_zcalled_login, "ANY") == 0) { if (qany == NULL) qany = qsys; } else if (strcmp (qsys->uuconf_zcalled_login, zlogin) == 0) break; } if (qsys == NULL && qany != NULL) { iuuconf = uuconf_validate (puuconf, qany, zlogin); if (iuuconf == UUCONF_SUCCESS) qsys = qany; else if (iuuconf != UUCONF_NOT_FOUND) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); ubuffree (zstr); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } } if (qsys == NULL) { (void) fsend_uucp_cmd (qconn, "RLOGIN"); ulog (LOG_ERROR, "System %s used wrong login name %s", zstr + 1, zlogin); ubuffree (zstr); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } sDaemon.qsys = qsys; if (pzsystem != NULL) *pzsystem = zbufcpy (qsys->uuconf_zname); ulog_system (qsys->uuconf_zname); #if DEBUG > 1 if (qsys->uuconf_zdebug != NULL) iDebug |= idebug_parse (qsys->uuconf_zdebug); #endif /* See if we are supposed to call the system back. This will queue up an empty command. It would be better to actually call back directly at this point as well. */ if (qsys->uuconf_fcallback) { (void) fsend_uucp_cmd (qconn, "RCB"); ulog (LOG_NORMAL, "Will call back"); /* Clear any existing status. */ sstat.ttype = STATUS_COMPLETE; sstat.cretries = 0; sstat.ilast = ixsysdep_time ((long *) NULL); sstat.cwait = 0; (void) fsysdep_set_status (qsys, &sstat); ubuffree (zsysdep_spool_commands (qsys, UUCONF_GRADE_HIGH, 0, (const struct scmd *) NULL, (boolean *) NULL)); ubuffree (zstr); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return TRUE; } /* We only permit one call at a time from a remote system. Lock it. */ if (! fsysdep_lock_system (qsys)) { if (qsys->uuconf_fsequence) { /* At this point the calling system has already incremented its sequence number, so we increment ours. This will only cause a mismatch if the other system is not what it says it is. */ (void) ixsysdep_get_sequence (qsys); } (void) fsend_uucp_cmd (qconn, "RLCK"); ulog (LOG_ERROR, "System already locked"); ubuffree (zstr); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } sLocked_system = *qsys; fLocked_system = TRUE; /* Set the system status. We don't care what the status was before. We also don't want to kill the conversation just because we can't output the .Status file, so we ignore any errors. */ sstat.ttype = STATUS_TALKING; sstat.cretries = 0; sstat.ilast = ixsysdep_time ((long *) NULL); sstat.cwait = 0; (void) fsysdep_set_status (qsys, &sstat); /* Check the arguments of the remote system, if any. */ fgotseq = FALSE; fgotn = FALSE; if (zspace != NULL) { char **paz; char **pzset; ++zspace; /* Break the introduction line up into arguments. */ paz = (char **) xmalloc ((strlen (zspace) / 2 + 2) * sizeof (char *)); pzset = paz; *pzset++ = NULL; while (TRUE) { while (*zspace != '\0' && isspace (BUCHAR (*zspace))) ++zspace; if (*zspace == '\0') break; *pzset++ = zspace; ++zspace; while (*zspace != '\0' && ! isspace (BUCHAR (*zspace))) ++zspace; if (*zspace == '\0') break; *zspace++ = '\0'; } if (pzset != paz + 1) { int iopt; *pzset = NULL; /* We are going to use getopt to parse the arguments. We must clear optind to force getopt to reinitialize, and clear opterr to prevent getopt from printing an error message. This approach assumes we are using the GNU getopt, which is distributed with the program anyhow. */ optind = 0; opterr = 0; while ((iopt = getopt (pzset - paz, paz, "N::p:Q:RU:v:x:")) != EOF) { long iseq; long c; char b; int iwant; switch (iopt) { case 'N': /* This is used to indicate support for Taylor UUCP extensions. An plain -N mean support for size negotiation. If -N is followed by a number (with no intervening space), the number is a bit field of feature flags as defined in trans.h. Note that the argument may start with 0x for hex or 0 for octal. */ fgotn = TRUE; if (optarg == NULL) sDaemon.ifeatures |= FEATURE_SIZES | FEATURE_V103; else sDaemon.ifeatures |= (int) strtol (optarg, (char **) NULL, 0); break; case 'p': /* The argument is the lowest grade of work the local system should send. */ if (UUCONF_GRADE_LEGAL (optarg[0])) sDaemon.bgrade = optarg[0]; break; case 'Q': /* The conversation sequence number. */ iseq = strtol (optarg, (char **) NULL, 10); if (qsys->uuconf_fsequence && iseq != ixsysdep_get_sequence (qsys)) { (void) fsend_uucp_cmd (qconn, "RBADSEQ"); ulog (LOG_ERROR, "Out of sequence call rejected"); sstat.ttype = STATUS_FAILED; (void) fsysdep_set_status (qsys, &sstat); xfree ((pointer) paz); ubuffree (zstr); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } fgotseq = TRUE; break; case 'R': /* The remote system supports file restart. */ sDaemon.ifeatures |= FEATURE_RESTART; break; case 'U': /* The maximum file size the remote system is prepared to received, in blocks where each block is 512 bytes. */ c = strtol (optarg, (char **) NULL, 0); if (c > 0 && c < LONG_MAX / (long) 512) sDaemon.cmax_receive = c * (long) 512; break; case 'v': /* -vgrade=X can be used to set the lowest grade of work the local system should send. */ if (strncmp (optarg, "grade=", sizeof "grade=" - 1) == 0) { b = optarg[sizeof "grade=" - 1]; if (UUCONF_GRADE_LEGAL (b)) sDaemon.bgrade = b; } break; case 'x': iwant = (int) strtol (optarg, (char **) NULL, 10); #if DEBUG > 1 if (iwant <= 9) iwant = (1 << iwant) - 1; if (qsys->uuconf_zmax_remote_debug != NULL) iwant &= idebug_parse (qsys->uuconf_zmax_remote_debug); else iwant &= DEBUG_ABNORMAL | DEBUG_CHAT | DEBUG_HANDSHAKE; if ((iDebug | iwant) != iDebug) { iDebug |= iwant; ulog (LOG_NORMAL, "Setting debugging mode to 0%o", iDebug); } #endif break; default: break; } } } xfree ((pointer) paz); } ubuffree (zstr); if (qsys->uuconf_fsequence && ! fgotseq) { (void) fsend_uucp_cmd (qconn, "RBADSEQ"); ulog (LOG_ERROR, "No sequence number (call rejected)"); sstat.ttype = STATUS_FAILED; (void) fsysdep_set_status (qsys, &sstat); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } /* We recognized the system, and the sequence number (if any) was OK. Send an ROK, and send a list of protocols. If we got the -N switch, send ROKN to confirm it; if the -N switch was followed by a feature bitfield, return our own feature bitfield. */ { char ab[20]; const char *zreply; if (! fgotn) { if ((sDaemon.ifeatures & FEATURE_RESTART) == 0) zreply = "ROK"; else { /* We got -R without -N, so assume that this is SVR4 UUCP. SVR4 UUCP expects ROK -R to signal support for file restart. */ sDaemon.ifeatures |= FEATURE_SVR4 | FEATURE_SIZES; zreply = "ROK -R"; } } else if ((sDaemon.ifeatures & FEATURE_V103) != 0) zreply = "ROKN"; else { sprintf (ab, "ROKN0%o", (unsigned int) (FEATURE_SIZES | FEATURE_EXEC | FEATURE_RESTART | FEATURE_QUOTES)); zreply = ab; } if (! fsend_uucp_cmd (qconn, zreply)) { sstat.ttype = STATUS_FAILED; (void) fsysdep_set_status (qsys, &sstat); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } } /* Determine the reliability of the connection based on the reliability of the port and the dialer. If we have no information, default to a reliable eight-bit full-duplex connection. */ if (ftcp_port) sDaemon.ireliable = (UUCONF_RELIABLE_SPECIFIED | UUCONF_RELIABLE_ENDTOEND | UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX); else { if (qport != NULL && (qport->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) sDaemon.ireliable = qport->uuconf_ireliable; if (qdialer != NULL && (qdialer->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) { if (sDaemon.ireliable != 0) sDaemon.ireliable &= qdialer->uuconf_ireliable; else sDaemon.ireliable = qdialer->uuconf_ireliable; } if (sDaemon.ireliable == 0) sDaemon.ireliable = (UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX | UUCONF_RELIABLE_SPECIFIED); } if (qsys->uuconf_zprotocols != NULL || (qport != NULL && qport->uuconf_zprotocols != NULL)) { const char *zprotos; if (qsys->uuconf_zprotocols != NULL) zprotos = qsys->uuconf_zprotocols; else zprotos = qport->uuconf_zprotocols; zsend = zbufalc (strlen (zprotos) + 2); sprintf (zsend, "P%s", zprotos); } else { char *zset; zsend = zbufalc (CPROTOCOLS + 2); zset = zsend; *zset++ = 'P'; /* If the system did not specify a list of protocols, we want only protocols that match the known reliability of the dialer and the port. */ for (i = 0; i < CPROTOCOLS; i++) { int ipr; ipr = asProtocols[i].ireliable; if ((ipr & sDaemon.ireliable) != ipr) continue; *zset++ = asProtocols[i].bname; } *zset = '\0'; } fret = fsend_uucp_cmd (qconn, zsend); ubuffree (zsend); if (! fret) { sstat.ttype = STATUS_FAILED; (void) fsysdep_set_status (qsys, &sstat); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } /* The master will now send back the selected protocol. */ zstr = zget_uucp_cmd (qconn, TRUE, fstrip); if (zstr == NULL) { sstat.ttype = STATUS_FAILED; (void) fsysdep_set_status (qsys, &sstat); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } if (zstr[0] != 'U') { ulog (LOG_ERROR, "Bad protocol response string"); sstat.ttype = STATUS_FAILED; (void) fsysdep_set_status (qsys, &sstat); ubuffree (zstr); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } if (zstr[1] == 'N') { ulog (LOG_ERROR, "No supported protocol"); sstat.ttype = STATUS_FAILED; (void) fsysdep_set_status (qsys, &sstat); ubuffree (zstr); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } for (i = 0; i < CPROTOCOLS; i++) if (asProtocols[i].bname == zstr[1]) break; ubuffree (zstr); if (i >= CPROTOCOLS) { ulog (LOG_ERROR, "No supported protocol"); sstat.ttype = STATUS_FAILED; (void) fsysdep_set_status (qsys, &sstat); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } sDaemon.qproto = &asProtocols[i]; /* If we are using a half-duplex line, act as though we have only a single channel; otherwise we might start a send and a receive at the same time. */ if ((sDaemon.ireliable & UUCONF_RELIABLE_FULLDUPLEX) == 0) sDaemon.cchans = 1; else sDaemon.cchans = asProtocols[i].cchans; /* Run the chat script for when a call is received. */ if (! fchat (qconn, puuconf, &qsys->uuconf_scalled_chat, qsys, (const struct uuconf_dialer *) NULL, (const char *) NULL, FALSE, zport, iconn_baud (qconn))) { sstat.ttype = STATUS_FAILED; sstat.ilast = ixsysdep_time ((long *) NULL); (void) fsysdep_set_status (qsys, &sstat); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } /* Run any protocol parameter commands. */ if (sDaemon.qproto->qcmds != NULL) { if (qsys->uuconf_qproto_params != NULL) uapply_proto_params (puuconf, sDaemon.qproto->bname, sDaemon.qproto->qcmds, qsys->uuconf_qproto_params); if (qport != NULL && qport->uuconf_qproto_params != NULL) uapply_proto_params (puuconf, sDaemon.qproto->bname, sDaemon.qproto->qcmds, qport->uuconf_qproto_params); if (qdialer != NULL && qdialer->uuconf_qproto_params != NULL) uapply_proto_params (puuconf, sDaemon.qproto->bname, sDaemon.qproto->qcmds, qdialer->uuconf_qproto_params); } /* We don't need the dialer information any more. */ if (qdialer == &sdialer) (void) uuconf_dialer_free (puuconf, &sdialer); /* Turn on the selected protocol and get any jobs queued for the system. */ if (! (*sDaemon.qproto->pfstart) (&sDaemon, &zlog) || ! fqueue (&sDaemon, (boolean *) NULL)) { uclear_queue (&sDaemon); sstat.ttype = STATUS_FAILED; sstat.ilast = ixsysdep_time ((long *) NULL); (void) fsysdep_set_status (qsys, &sstat); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return FALSE; } if (zlog == NULL) { zlog = zbufalc (sizeof "protocol ''" + 1); sprintf (zlog, "protocol '%c'", sDaemon.qproto->bname); } zgrade = zbufalc (sizeof "grade " + 1); if (sDaemon.bgrade == UUCONF_GRADE_LOW) *zgrade = '\0'; else sprintf (zgrade, "grade %c ", sDaemon.bgrade); /* If we are using HAVE_HDB_LOGGING, then the previous ``incoming call'' message went to the general log, since we didn't know the system name at that point. In that case, we repeat the port and login names. */ #if HAVE_HDB_LOGGING ulog (LOG_NORMAL, "Handshake successful (login %s port %s %s%s)", zlogin, zLdevice == NULL ? "unknown" : zLdevice, zgrade, zlog); #else /* ! HAVE_HDB_LOGGING */ ulog (LOG_NORMAL, "Handshake successful (%s%s)", zgrade, zlog); #endif /* ! HAVE_HDB_LOGGING */ ubuffree (zlog); ubuffree (zgrade); { long iend_time; fret = floop (&sDaemon); /* Hangup. As the answerer, we send seven O's and expect to receive six O's. We send the seven O's twice to help the other side. We don't worry about errors here. */ if (fsend_uucp_cmd (qconn, "OOOOOOO") && fsend_uucp_cmd (qconn, "OOOOOOO")) { int fdone; /* We look for the remote hangup string to ensure that the modem has sent out our hangup string. This is only necessary because some versions of UUCP complain if they don't get the hangup string. We look for the string several times because supposedly some implementations send some garbage after the last packet but before the hangup string. */ for (i = 0; i < 25; i++) { zstr = zget_uucp_cmd (qconn, FALSE, fstrip); if (zstr == NULL) break; fdone = strstr (zstr, "OOOOOO") != NULL; ubuffree (zstr); if (fdone) break; } } iend_time = ixsysdep_time ((long *) NULL); ulog (LOG_NORMAL, "Call complete (%ld seconds %ld bytes %ld bps)", iend_time - istart_time, sDaemon.csent + sDaemon.creceived, (iend_time != istart_time ? (sDaemon.csent + sDaemon.creceived) / (iend_time - istart_time) : 0)); uclear_queue (&sDaemon); if (fret) sstat.ttype = STATUS_COMPLETE; else sstat.ttype = STATUS_FAILED; sstat.ilast = iend_time; (void) fsysdep_set_status (qsys, &sstat); if (sDaemon.irunuuxqt == UUCONF_RUNUUXQT_PERCALL || (sDaemon.irunuuxqt > 0 && sDaemon.cxfiles_received > 0)) (void) fspawn_uuxqt (TRUE, qsys->uuconf_zname, zconfig); uaccept_call_cleanup (puuconf, &ssys, qport, &sport, zloc); return fret; } } /* Clean up after faccept_call. */ static void uaccept_call_cleanup (puuconf, qfreesys, qport, qfreeport, zloc) pointer puuconf ATTRIBUTE_UNUSED; struct uuconf_system *qfreesys; struct uuconf_port *qport; struct uuconf_port *qfreeport; char *zloc; { if (fLocked_system) { (void) fsysdep_unlock_system (&sLocked_system); fLocked_system = FALSE; } if (qfreesys != NULL) (void) uuconf_system_free (puuconf, qfreesys); if (qport == qfreeport) (void) uuconf_port_free (puuconf, qfreeport); xfree ((pointer) zloc); ulog_system ((const char *) NULL); } /* Apply protocol parameters, once we know the protocol. */ static void uapply_proto_params (puuconf, bproto, qcmds, pas) pointer puuconf; int bproto; struct uuconf_cmdtab *qcmds; struct uuconf_proto_param *pas; { struct uuconf_proto_param *qp; for (qp = pas; qp->uuconf_bproto != '\0'; qp++) { if (qp->uuconf_bproto == bproto) { struct uuconf_proto_param_entry *qe; for (qe = qp->uuconf_qentries; qe->uuconf_cargs > 0; qe++) { int iuuconf; iuuconf = uuconf_cmd_args (puuconf, qe->uuconf_cargs, qe->uuconf_pzargs, qcmds, (pointer) NULL, (uuconf_cmdtabfn) NULL, 0, (pointer) NULL); if (UUCONF_ERROR_VALUE (iuuconf) != UUCONF_SUCCESS) { ulog (LOG_ERROR, "Error in %c protocol parameters", bproto); ulog_uuconf (LOG_ERROR, puuconf, iuuconf); } } break; } } } /* Send a string to the other system beginning with a DLE character and terminated with a null byte. This is only used when no protocol is in force. */ static boolean fsend_uucp_cmd (qconn, z) struct sconnection *qconn; const char *z; { size_t cwrite; char *zalc; boolean fret; DEBUG_MESSAGE1 (DEBUG_HANDSHAKE, "fsend_uucp_cmd: Sending \"%s\"", z); cwrite = strlen (z) + 2; zalc = zbufalc (cwrite); zalc[0] = '\020'; memcpy (zalc + 1, z, cwrite - 1); fret = fconn_write (qconn, zalc, cwrite); ubuffree (zalc); return fret; } /* Get a UUCP command beginning with a DLE character and ending with a null byte. This is only used when no protocol is in force. This implementation has the potential of being seriously slow. It also doesn't have any real error recovery. The frequired argument is passed as TRUE if we need the string; we don't care that much if we're closing down the connection anyhow. */ #define CTIMEOUT (120) #define CSHORTTIMEOUT (10) #define CINCREMENT (100) static char * zget_uucp_cmd (qconn, frequired, fstrip) struct sconnection *qconn; boolean frequired; boolean fstrip; { char *zalc; size_t calc; size_t cgot; boolean fintro; long iendtime; int ctimeout; #if DEBUG > 1 int cchars; int iolddebug; #endif iendtime = ixsysdep_time ((long *) NULL); if (frequired) iendtime += CTIMEOUT; else iendtime += CSHORTTIMEOUT; #if DEBUG > 1 cchars = 0; iolddebug = iDebug; if (FDEBUGGING (DEBUG_HANDSHAKE)) { ulog (LOG_DEBUG_START, "zget_uucp_cmd: Got \""); iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT); } #endif zalc = NULL; calc = 0; cgot = 0; fintro = FALSE; while ((ctimeout = (int) (iendtime - ixsysdep_time ((long *) NULL))) > 0) { int b; b = breceive_char (qconn, ctimeout, frequired); /* Now b == -1 on timeout, -2 on error. */ if (b < 0) { #if DEBUG > 1 if (FDEBUGGING (DEBUG_HANDSHAKE)) { ulog (LOG_DEBUG_END, "\" (%s)", b == -1 ? "timeout" : "error"); iDebug = iolddebug; } #endif if (b == -1 && frequired) ulog (LOG_ERROR, "Timeout"); ubuffree (zalc); return NULL; } /* Apparently some systems use parity on these strings, so we optionally strip the parity bit. */ if (fstrip) b &= 0x7f; #if DEBUG > 1 if (FDEBUGGING (DEBUG_HANDSHAKE)) { char ab[5]; ++cchars; if (cchars > 60) { ulog (LOG_DEBUG_END, "\""); ulog (LOG_DEBUG_START, "zget_uucp_cmd: Got \""); cchars = 0; } (void) cdebug_char (ab, b); ulog (LOG_DEBUG_CONTINUE, "%s", ab); } #endif if (! fintro) { if (b == '\020') fintro = TRUE; continue; } /* If we see another DLE, something has gone wrong; continue as though this were the first one we saw. */ if (b == '\020') { cgot = 0; continue; } /* Some systems send a trailing \n on the Shere line. As far as I can tell this line can never contain a \n, so this modification should be safe enough. */ if (b == '\r' || b == '\n') b = '\0'; if (cgot >= calc) { char *znew; calc += CINCREMENT; znew = zbufalc (calc); if (cgot > 0) memcpy (znew, zalc, cgot); ubuffree (zalc); zalc = znew; } zalc[cgot] = (char) b; ++cgot; if (b == '\0') { #if DEBUG > 1 if (FDEBUGGING (DEBUG_HANDSHAKE)) { ulog (LOG_DEBUG_END, "\""); iDebug = iolddebug; } #endif return zalc; } } #if DEBUG > 1 if (FDEBUGGING (DEBUG_HANDSHAKE)) { ulog (LOG_DEBUG_END, "\" (timeout)"); iDebug = iolddebug; } #endif ubuffree (zalc); if (frequired) ulog (LOG_ERROR, "Timeout"); return NULL; } /* Read a sequence of characters up to a newline or carriage return, and return the line without the line terminating character. Remember whether the last string we returned ended in \r; if it did, ignore a leading \n to account for \r\n pairs. */ static char * zget_typed_line (qconn, fstrip) struct sconnection *qconn; boolean fstrip; { static boolean flastcr; char *zalc; size_t calc; size_t cgot; #if DEBUG > 1 int cchars; int iolddebug; cchars = 0; iolddebug = iDebug; if (FDEBUGGING (DEBUG_CHAT)) { ulog (LOG_DEBUG_START, "zget_typed_line: Got \""); iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT); } #endif zalc = NULL; calc = 0; cgot = 0; while (TRUE) { int b; b = breceive_char (qconn, CTIMEOUT, FALSE); /* Now b == -1 on timeout, -2 on error. */ if (b == -2 || FGOT_SIGNAL ()) { #if DEBUG > 1 if (FDEBUGGING (DEBUG_CHAT)) { ulog (LOG_DEBUG_END, "\" (error)"); iDebug = iolddebug; } #endif ubuffree (zalc); flastcr = FALSE; return NULL; } if (b == -1) { flastcr = FALSE; continue; } /* Optionally strip the parity bit. */ if (fstrip) b &= 0x7f; #if DEBUG > 1 if (FDEBUGGING (DEBUG_CHAT)) { char ab[5]; ++cchars; if (cchars > 60) { ulog (LOG_DEBUG_END, "\""); ulog (LOG_DEBUG_START, "zget_typed_line: Got \""); cchars = 0; } (void) cdebug_char (ab, b); ulog (LOG_DEBUG_CONTINUE, "%s", ab); } #endif if (b == '\n' && cgot == 0 && flastcr) { /* Ignore \n in \r\n pair. */ flastcr = FALSE; continue; } flastcr = FALSE; if (cgot >= calc) { char *znew; calc += CINCREMENT; znew = zbufalc (calc); if (cgot > 0) memcpy (znew, zalc, cgot); ubuffree (zalc); zalc = znew; } if (b == '\n') b = '\0'; else if (b == '\r') { flastcr = TRUE; b = '\0'; } zalc[cgot] = (char) b; ++cgot; if (b == '\0') { #if DEBUG > 1 if (FDEBUGGING (DEBUG_CHAT)) { ulog (LOG_DEBUG_END, "\""); iDebug = iolddebug; } #endif return zalc; } } } /* Spawn a uuxqt job. This probably belongs in some other file, but I don't have a good place for it. We used to spawn uuxqt with a -s option for zsys, but that doesn't help much, and when max-uuxqts is used it permits one system to hog the uuxqt jobs. */ boolean fspawn_uuxqt (ffork, zsys, zconfig) boolean ffork; const char *zsys ATTRIBUTE_UNUSED; const char *zconfig; { char *zconfigarg; boolean fret; if (zconfig == NULL) zconfigarg = NULL; else { zconfigarg = zbufalc (sizeof "-I" + strlen (zconfig)); sprintf (zconfigarg, "-I%s", zconfig); } fret = fsysdep_run (ffork, "uuxqt", zconfigarg, (const char *) NULL); ubuffree (zconfigarg); return fret; } uucp-1.07/trans.h0000664000076400007640000002532607665321756007441 /* trans.h Header file for file and command transfer routines. Copyright (C) 1992, 1993, 1994, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ /* The maximum possible number of channels. */ #define IMAX_CHAN (16) /* The ifeatures field of the sdaemon structure is an or of the following values. These values are sent during the uucico handshake, and MUST NOT CHANGE. */ /* File size negotiation. */ #define FEATURE_SIZES (01) /* File transfer restart. */ #define FEATURE_RESTART (02) /* The E (execute) command. */ #define FEATURE_EXEC (04) /* Version 1.03: requires decimal size in S and R command. Needless to say, this should not be used by any new programs. */ #define FEATURE_V103 (010) /* SVR4 UUCP: expects dummy string between notify field and size field in send command. There is probably some meaning to this string, but I don't know what it is. If I ever find out, this flag will still be used to indicate it. */ #define FEATURE_SVR4 (020) /* Supports the 'q' option in UUCP protocol commands. When the 'q' option appears in a command, it means that the string fields are backslash quoted. */ #define FEATURE_QUOTES (040) /* This structure is used to hold information concerning the communication link established with the remote system. */ struct sdaemon { /* Global uuconf pointer. */ pointer puuconf; /* Configuration file name argument (from -I option). */ const char *zconfig; /* How often to spawn uuxqt (from uuconf_runuuxqt). */ int irunuuxqt; /* Remote system information. */ const struct uuconf_system *qsys; /* Local name being used. */ const char *zlocalname; /* Connection structure. */ struct sconnection *qconn; /* Protocol being used. */ const struct sprotocol *qproto; /* Number of channels being used. */ int cchans; /* The largest file size permitted for a local request. */ long clocal_size; /* The largest file size permitted for a remote request. */ long cremote_size; /* The largest file size that may ever be transferred. */ long cmax_ever; /* The remote system ulimit. */ long cmax_receive; /* Number of bytes sent. */ long csent; /* Number of bytes received. */ long creceived; /* Number of execution files received since the last time we spawned uuxqt. */ long cxfiles_received; /* Features supported by the remote side. */ int ifeatures; /* TRUE if we should request the remote side to hang up. */ boolean frequest_hangup; /* TRUE if the remote side requested a hangup. */ boolean fhangup_requested; /* TRUE if we are hanging up. */ boolean fhangup; /* TRUE if the local system is currently the master. */ boolean fmaster; /* TRUE if the local system placed the call. */ boolean fcaller; /* UUCONF_RELIABLE_* flags for the connection. */ int ireliable; /* If fcaller is FALSE, the lowest grade which may be transferred during this call. */ char bgrade; }; /* This structure is used to hold a file or command transfer which is in progress. */ struct stransfer { /* Next file transfer in queue. */ struct stransfer *qnext; /* Previous file transfer in queue. */ struct stransfer *qprev; /* Points to the queue this structure is on. */ struct stransfer **pqqueue; /* The function to call to send some data. */ boolean (*psendfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon)); /* The function to call when data is received. */ boolean (*precfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon, const char *zdata, size_t cdata)); /* Type specific information. */ pointer pinfo; /* TRUE if we are sending the file e (this is used to avoid a call to psendfn). */ boolean fsendfile; /* TRUE if we are receiving the file e (this is used to avoid a call to precfn). */ boolean frecfile; /* The file to read or write. */ openfile_t e; /* The position we are at in the file. */ long ipos; /* TRUE if we are waiting for a command string. */ boolean fcmd; /* The command string we have so far. */ char *zcmd; /* The length of the command string we have so far. */ size_t ccmd; /* Local destination number. */ int ilocal; /* Remote destination number. */ int iremote; /* The command. */ struct scmd s; /* A message to log when work starts. */ char *zlog; /* The process time; imicros can be negative. */ long isecs; long imicros; /* Number of bytes sent or received. */ long cbytes; }; /* Reasons that a file transfer might fail. */ enum tfailure { /* No failure. */ FAILURE_NONE, /* No permission for operation. */ FAILURE_PERM, /* Can't open necessary file. */ FAILURE_OPEN, /* Not enough space to receive file. */ FAILURE_SIZE, /* File was received in a previous conversation. */ FAILURE_RECEIVED }; /* The main loop which talks to the remote system, passing transfer requests and file back and forth. */ extern boolean floop P((struct sdaemon *qdaemon)); /* Allocate a new transfer structure. */ extern struct stransfer *qtransalc P((struct scmd *qcmd)); /* Free a transfer structure. */ extern void utransfree P((struct stransfer *qtrans)); /* Queue up local requests. If pfany is not NULL, this sets *pfany to TRUE if there are, in fact, any local requests which can be done at this point. */ extern boolean fqueue P((struct sdaemon *qdaemon, boolean *pfany)); /* Clear away any queued requests. This may be called more than once at the end of a call. */ extern void uclear_queue P((struct sdaemon *qdaemon)); /* Queue a new transfer request made by the local system. */ extern boolean fqueue_local P((struct sdaemon *qdaemon, struct stransfer *qtrans)); /* Queue a new transfer request made by the remote system. */ extern boolean fqueue_remote P((struct sdaemon *qdaemon, struct stransfer *qtrans)); /* Queue a transfer request which wants to send something. */ extern boolean fqueue_send P((struct sdaemon *qdaemon, struct stransfer *qtrans)); /* Queue a transfer request which wants to receiving something. */ extern boolean fqueue_receive P((struct sdaemon *qdaemon, struct stransfer *qtrans)); /* Prepare to send a file by local or remote request. */ extern boolean flocal_send_file_init P((struct sdaemon *qdaemon, struct scmd *qcmd)); extern boolean fremote_send_file_init P((struct sdaemon *qdaemon, struct scmd *qcmd, int iremote)); /* Prepare to receive a file by local or remote request. */ extern boolean flocal_rec_file_init P((struct sdaemon *qdaemon, struct scmd *qcmd)); extern boolean fremote_rec_file_init P((struct sdaemon *qdaemon, struct scmd *qcmd, int iremote)); /* Prepare to request work by local or remote request. */ extern boolean flocal_xcmd_init P((struct sdaemon *qdaemon, struct scmd *qcmd)); extern boolean fremote_xcmd_init P((struct sdaemon *qdaemon, struct scmd *qcmd, int iremote)); /* We have lost the connection; record any in progress file transfers in the statistics file and discard any temporary files. */ extern void ufailed P((struct sdaemon *qdaemon)); /* Check that there is enough disk space for a file receive. Return FALSE if there is not. */ extern boolean frec_check_free P((struct stransfer *qtrans, long cfree_space)); /* Discard the temporary file being used to receive a file, if appropriate. */ extern boolean frec_discard_temp P((struct sdaemon *qdaemon, struct stransfer *qtrans)); /* Handle data received by a protocol. This is called by the protocol specific routines as data comes in. The data is passed as two buffers because that is convenient for packet based protocols, but normally csecond will be 0. The ilocal argument is the local channel number, and the iremote argument is the remote channel number. Either may be -1, if the protocol does not have channels. The ipos argument is the position in the file, if the protocol knows it; for most protocols, this will be -1. The fallacked argument should be set to TRUE if the remote has acknowledged all outstanding data; see uwindow_acked, below, for details. This will set *pfexit to TRUE if there is something for the main loop to do. A file is complete is when a zero length buffer is passed (cfirst == 0). A command is complete when data containing a null byte is passed. This will return FALSE on error. If the protocol pfwait entry point should exit and let the top level loop continue, *pfexit will be set to TRUE (if pfexit is not NULL). This will not set *pfexit to FALSE, so the caller must do that. */ extern boolean fgot_data P((struct sdaemon *qdaemon, const char *zfirst, size_t cfirst, const char *zsecond, size_t csecond, int ilocal, int iremote, long ipos, boolean fallacked, boolean *pfexit)); /* This routine is called when an ack is sent for a file receive. */ extern void usent_receive_ack P((struct sdaemon *qdaemon, struct stransfer *qtrans)); /* A protocol may call this routine to indicate the packets have been acknowledged by the remote system. If the fallacked argument is TRUE, then all outstanding packets have been acknowledged; for convenience, this may also be indicated by passing fallacked as TRUE to fgot_data, above. Otherwise this routine should be called each time a complete window is acked by the remote system. The transfer code uses this information to keep track of when an acknowledgement of a file receive has been seen by the other side, so that file receives may be handled cleanly if the connection is lost. */ extern void uwindow_acked P((struct sdaemon *qdaemon, boolean fallacked)); /* Spawn a uuxqt process. The ffork argument is passed to fsysdep_run. If the zsys argument is not NULL, then -s zsys is passed to uuxqt. The zconfig argument is the name of the configuration file, from the -I option. */ extern boolean fspawn_uuxqt P((boolean ffork, const char *zsys, const char *zconfig)); uucp-1.07/trans.c0000664000076400007640000011367407665321756007440 /* trans.c Routines to handle file transfers. Copyright (C) 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char trans_rcsid[] = "$Id: trans.c,v 1.49 2002/03/05 19:10:41 ian Rel $"; #endif #include #include "uudefs.h" #include "uuconf.h" #include "prot.h" #include "system.h" #include "trans.h" /* To avoid wasting a lot of time scanning the spool directory, which might cause the remote system to time out, we limit each scan to pick up at most a certain number of files. */ #define COMMANDS_PER_SCAN (200) /* The structure we use when waiting for an acknowledgement of a confirmed received file in fsent_receive_ack. */ struct sreceive_ack { struct sreceive_ack *qnext; char *zto; char *ztemp; boolean fmarked; }; /* Local functions. */ static void utqueue P((struct stransfer **, struct stransfer *, boolean fhead)); static void utdequeue P((struct stransfer *)); static void utchanalc P((struct sdaemon *qdaemon, struct stransfer *qtrans)); __inline__ static struct stransfer *qtchan P((int ichan)); __inline__ static void utchanfree P((struct stransfer *qtrans)); static void utfree_queue P((struct stransfer **pq)); static boolean fttime P((struct sdaemon *qdaemon, long *pisecs, long *pimicros)); static boolean fcheck_queue P((struct sdaemon *qdaemon)); static boolean ftadd_cmd P((struct sdaemon *qdaemon, const char *z, size_t cdata, int iremote, boolean flast)); static boolean fremote_hangup_reply P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static void utfree_receive_ack P((struct sreceive_ack *q)); static void utfree_acked P((void)); static boolean flocal_poll_file P((struct stransfer *qtrans, struct sdaemon *qdaemon)); /* Queue of transfer structures that are ready to start which have been requested by the local system. These are only permitted to start when the local system is the master. */ static struct stransfer *qTlocal; /* Queue of transfer structures that are ready to start which have been requested by the remote system. These are responses to commands received from the remote system, and should be started as soon as possible. */ static struct stransfer *qTremote; /* Queue of transfer structures that have been started and want to send information. This should be static, but the 'a' protocol looks at it, at least for now. */ struct stransfer *qTsend; /* Queue of transfer structures that have been started and are waiting to receive information. */ static struct stransfer *qTreceive; /* Queue of free transfer structures. */ static struct stransfer *qTavail; /* Array of transfer structures indexed by local channel number. This is maintained for local jobs. */ static struct stransfer *aqTchan[IMAX_CHAN + 1]; /* Number of local channel numbers currently allocated. */ static int cTchans; /* Next channel number to allocate. */ static int iTchan; /* Array of transfer structures indexed by remote channel number. This is maintained for remote jobs. */ static struct stransfer *aqTremote[IMAX_CHAN + 1]; /* The transaction we are currently receiving. This is used to avoid getting the time too frequently. */ static struct stransfer *qTtiming_rec; /* The time from which to charge any received data. This is either the last time we charged for received data, or the last time something was put on the empty receive queue. */ static long iTrecsecs; static long iTrecmicros; /* The minimum amount of time, in seconds, to wait between times we check the spool directory, if we are busy transferring data. If we have nothing to do, we will check the spool directory regardless of how long ago the last check was. This should probably be configurable. */ #define CCHECKWAIT (600) /* The time we last checked the spool directory for work. This is set from the return value of ixsysdep_process_time, not ixsysdep_time, for convenience in the routines which use it. */ static long iTchecktime; /* The size of the command we have read so far in ftadd_cmd. */ static size_t cTcmdlen; /* A list of structures used when waiting for an acknowledgement of a confirmed received file in fsent_receive_ack. */ static struct sreceive_ack *qTreceive_ack; /* Queue up a transfer structure before *pq. This puts it at the head or the tail of the list headed by *pq. */ static void utqueue (pq, q, fhead) struct stransfer **pq; struct stransfer *q; boolean fhead; { if (*pq == NULL) { *pq = q; q->qprev = q->qnext = q; } else { q->qnext = *pq; q->qprev = (*pq)->qprev; q->qprev->qnext = q; q->qnext->qprev = q; if (fhead) *pq = q; } q->pqqueue = pq; } /* Dequeue a transfer structure. */ static void utdequeue (q) struct stransfer *q; { if (q->pqqueue != NULL) { if (*(q->pqqueue) == q) { if (q->qnext == q) *(q->pqqueue) = NULL; else *(q->pqqueue) = q->qnext; } q->pqqueue = NULL; } if (q->qprev != NULL) q->qprev->qnext = q->qnext; if (q->qnext != NULL) q->qnext->qprev = q->qprev; q->qprev = NULL; q->qnext = NULL; } /* Queue up a transfer structure requested by the local system. */ /*ARGSIGNORED*/ boolean fqueue_local (qdaemon, qtrans) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; struct stransfer *qtrans; { utdequeue (qtrans); utqueue (&qTlocal, qtrans, FALSE); return TRUE; } /* Queue up a transfer structure requested by the remote system. The stransfer structure should have the iremote field set. We need to record it, so that any subsequent data associated with this channel can be routed to the right place. */ boolean fqueue_remote (qdaemon, qtrans) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; struct stransfer *qtrans; { DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fqueue_remote: Channel %d", qtrans->iremote); if (qtrans->iremote > 0) aqTremote[qtrans->iremote] = qtrans; utdequeue (qtrans); utqueue (&qTremote, qtrans, FALSE); return TRUE; } /* Queue up a transfer with something to send. */ boolean fqueue_send (qdaemon, qtrans) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; struct stransfer *qtrans; { #if DEBUG > 0 if (qtrans->psendfn == NULL) ulog (LOG_FATAL, "fqueue_send: Bad call"); #endif utdequeue (qtrans); /* Sort the send queue to always send commands before files, and to sort jobs by grade. */ if (qTsend == NULL) utqueue (&qTsend, qtrans, FALSE); else { register struct stransfer *q; boolean ffirst; ffirst = TRUE; q = qTsend; do { if (! qtrans->fsendfile && q->fsendfile) break; if ((! qtrans->fsendfile || q->fsendfile) && UUCONF_GRADE_CMP (qtrans->s.bgrade, q->s.bgrade) < 0) break; ffirst = FALSE; q = q->qnext; } while (q != qTsend); qtrans->qnext = q; qtrans->qprev = q->qprev; q->qprev = qtrans; qtrans->qprev->qnext = qtrans; if (ffirst) qTsend = qtrans; qtrans->pqqueue = &qTsend; } return TRUE; } /* Queue up a transfer with something to receive. */ boolean fqueue_receive (qdaemon, qtrans) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; struct stransfer *qtrans; { #if DEBUG > 0 if (qtrans->precfn == NULL) ulog (LOG_FATAL, "fqueue_receive: Bad call"); #endif /* If this is the only item on the receive queue, we do not want to charge it for any time during which we have not been waiting for anything, so update the receive timestamp. */ if (qTreceive == NULL) iTrecsecs = ixsysdep_process_time (&iTrecmicros); utdequeue (qtrans); utqueue (&qTreceive, qtrans, FALSE); return TRUE; } /* Get a new local channel number. */ static void utchanalc (qdaemon, qtrans) struct sdaemon *qdaemon; struct stransfer *qtrans; { do { ++iTchan; if (iTchan > qdaemon->cchans) iTchan = 1; } while (aqTchan[iTchan] != NULL); qtrans->ilocal = iTchan; aqTchan[iTchan] = qtrans; ++cTchans; } /* Return the transfer for a channel number. */ __inline__ static struct stransfer * qtchan (ic) int ic; { return aqTchan[ic]; } /* Clear the channel number for a transfer. */ __inline__ static void utchanfree (qt) struct stransfer *qt; { if (qt->ilocal != 0) { aqTchan[qt->ilocal] = NULL; qt->ilocal = 0; --cTchans; } } /* Allocate a new transfer structure. */ struct stransfer * qtransalc (qcmd) struct scmd *qcmd; { register struct stransfer *q; q = qTavail; if (q != NULL) utdequeue (q); else q = (struct stransfer *) xmalloc (sizeof (struct stransfer)); q->qnext = NULL; q->qprev = NULL; q->pqqueue = NULL; q->psendfn = NULL; q->precfn = NULL; q->pinfo = NULL; q->fsendfile = FALSE; q->frecfile = FALSE; q->e = EFILECLOSED; q->ipos = 0; q->fcmd = FALSE; q->zcmd = NULL; q->ccmd = 0; q->ilocal = 0; q->iremote = 0; if (qcmd != NULL) { q->s = *qcmd; q->s.zfrom = zbufcpy (qcmd->zfrom); q->s.zto = zbufcpy (qcmd->zto); q->s.zuser = zbufcpy (qcmd->zuser); q->s.zoptions = zbufcpy (qcmd->zoptions); q->s.ztemp = zbufcpy (qcmd->ztemp); q->s.znotify = zbufcpy (qcmd->znotify); q->s.zcmd = zbufcpy (qcmd->zcmd); } else { q->s.zfrom = NULL; q->s.zto = NULL; q->s.zuser = NULL; q->s.zoptions = NULL; q->s.ztemp = NULL; q->s.znotify = NULL; q->s.zcmd = NULL; } q->zlog = NULL; q->isecs = 0; q->imicros = 0; q->cbytes = 0; return q; } /* Free a transfer structure. This does not free any pinfo information that may have been allocated. */ void utransfree (q) struct stransfer *q; { ubuffree (q->zcmd); ubuffree ((char *) q->s.zfrom); ubuffree ((char *) q->s.zto); ubuffree ((char *) q->s.zuser); ubuffree ((char *) q->s.zoptions); ubuffree ((char *) q->s.ztemp); ubuffree ((char *) q->s.znotify); ubuffree ((char *) q->s.zcmd); utchanfree (q); if (q->iremote > 0) { aqTremote[q->iremote] = NULL; q->iremote = 0; } if (ffileisopen (q->e)) { (void) ffileclose (q->e); q->e = EFILECLOSED; } #if DEBUG > 0 q->zcmd = NULL; q->s.zfrom = NULL; q->s.zto = NULL; q->s.zuser = NULL; q->s.zoptions = NULL; q->s.ztemp = NULL; q->s.znotify = NULL; q->s.zcmd = NULL; q->psendfn = NULL; q->precfn = NULL; #endif /* Avoid any possible confusion in the timing code. */ if (qTtiming_rec == q) qTtiming_rec = NULL; utdequeue (q); utqueue (&qTavail, q, FALSE); } /* Free a queue of transfer structures. */ static void utfree_queue (pq) struct stransfer **pq; { while (*pq != NULL) utransfree (*pq); } /* Get the time. This is a wrapper around ixsysdep_process_time. If enough time has elapsed since the last time we got the time, check the work queue. */ static boolean fttime (qdaemon, pisecs, pimicros) struct sdaemon *qdaemon; long *pisecs; long *pimicros; { *pisecs = ixsysdep_process_time (pimicros); if (*pisecs - iTchecktime >= CCHECKWAIT) { if (! fcheck_queue (qdaemon)) return FALSE; } return TRUE; } /* Gather local commands and queue them up for later processing. Also recompute time based control values. */ boolean fqueue (qdaemon, pfany) struct sdaemon *qdaemon; boolean *pfany; { const struct uuconf_system *qsys; long ival; int bgrade; struct uuconf_timespan *qlocal_size, *qremote_size; if (pfany != NULL) *pfany = FALSE; qsys = qdaemon->qsys; /* If we are not the caller, the grade will be set during the initial handshake, although this may be overridden by the calledtimegrade configuration option. */ if (! qdaemon->fcaller) { if (! ftimespan_match (qsys->uuconf_qcalledtimegrade, &ival, (int *) NULL)) bgrade = qdaemon->bgrade; else bgrade = (char) ival; } else { if (! ftimespan_match (qsys->uuconf_qtimegrade, &ival, (int *) NULL)) bgrade = '\0'; else bgrade = (char) ival; } /* Determine the maximum sizes we can send and receive. */ if (qdaemon->fcaller) { qlocal_size = qsys->uuconf_qcall_local_size; qremote_size = qsys->uuconf_qcall_remote_size; } else { qlocal_size = qsys->uuconf_qcalled_local_size; qremote_size = qsys->uuconf_qcalled_remote_size; } if (! ftimespan_match (qlocal_size, &qdaemon->clocal_size, (int *) NULL)) qdaemon->clocal_size = (long) -1; if (! ftimespan_match (qremote_size, &qdaemon->cremote_size, (int *) NULL)) qdaemon->cremote_size = (long) -1; if (bgrade == '\0') return TRUE; if (! fsysdep_get_work_init (qsys, bgrade, COMMANDS_PER_SCAN)) return FALSE; while (TRUE) { struct scmd s; if (! fsysdep_get_work (qsys, bgrade, COMMANDS_PER_SCAN, &s)) return FALSE; if (s.bcmd == 'H') { ulog_user ((const char *) NULL); break; } if (s.bcmd == 'P') { struct stransfer *qtrans; /* A poll file. */ ulog_user ((const char *) NULL); qtrans = qtransalc (&s); qtrans->psendfn = flocal_poll_file; if (! fqueue_local (qdaemon, qtrans)) return FALSE; continue; } ulog_user (s.zuser); switch (s.bcmd) { case 'S': case 'E': if (! flocal_send_file_init (qdaemon, &s)) return FALSE; break; case 'R': if (! flocal_rec_file_init (qdaemon, &s)) return FALSE; break; case 'X': if (! flocal_xcmd_init (qdaemon, &s)) return FALSE; break; #if DEBUG > 0 default: ulog (LOG_FATAL, "fqueue: Can't happen"); break; #endif } } if (pfany != NULL) *pfany = qTlocal != NULL; iTchecktime = ixsysdep_process_time ((long *) NULL); return TRUE; } /* Clear everything off the work queue. This is used when the call is complete, or if the call is never made. */ void uclear_queue (qdaemon) struct sdaemon *qdaemon; { int i; usysdep_get_work_free (qdaemon->qsys); utfree_queue (&qTlocal); utfree_queue (&qTremote); utfree_queue (&qTsend); utfree_queue (&qTreceive); cTchans = 0; iTchan = 0; qTtiming_rec = NULL; cTcmdlen = 0; if (qTreceive_ack != NULL) utfree_acked (); for (i = 0; i < IMAX_CHAN + 1; i++) { aqTchan[i] = NULL; aqTremote[i] = NULL; } } /* Recheck the work queue during a conversation. This is only called if it's been more than CCHECKWAIT seconds since the last time the queue was checked. */ static boolean fcheck_queue (qdaemon) struct sdaemon *qdaemon; { /* Only check if we are the master, or if there are multiple channels, or if we aren't already trying to get the other side to hang up. Otherwise, there's nothing we can do with any new jobs we might find. */ if (qdaemon->fmaster || qdaemon->cchans > 1 || ! qdaemon->frequest_hangup) { boolean fany; DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fcheck_queue: Rechecking work queue"); if (! fqueue (qdaemon, &fany)) return FALSE; /* If we found something to do, and we're not the master, and we don't have multiple channels to send new jobs over, try to get the other side to hang up. */ if (fany && ! qdaemon->fmaster && qdaemon->cchans <= 1) qdaemon->frequest_hangup = TRUE; } return TRUE; } /* The main transfer loop. The uucico daemon spends essentially all its time in this function. */ boolean floop (qdaemon) struct sdaemon *qdaemon; { boolean fret; fret = TRUE; while (! qdaemon->fhangup) { register struct stransfer *q; #if DEBUG > 1 /* If we're doing any debugging, close the log and debugging files regularly. This will let people copy them off and remove them while the conversation is in progresss. */ if (iDebug != 0) { ulog_close (); ustats_close (); } #endif if (qdaemon->fmaster) { boolean fhangup; /* We've managed to become the master, so we no longer want to request a hangup. */ qdaemon->frequest_hangup = FALSE; fhangup = FALSE; if (qdaemon->fhangup_requested && qTsend == NULL && (qTreceive == NULL || qdaemon->cchans > 1)) { /* The remote system has requested that we transfer control by sending CYM after receiving a file. */ DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "floop: Transferring control at remote request"); fhangup = TRUE; } else if (qTremote == NULL && qTlocal == NULL && qTsend == NULL && qTreceive == NULL) { /* We don't have anything to do. Try to find some new jobs. If we can't, transfer control. */ if (! fqueue (qdaemon, (boolean *) NULL)) { fret = FALSE; break; } if (qTlocal == NULL) { DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "floop: No work for master"); fhangup = TRUE; } } if (fhangup) { if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, "H", 0, 0)) { fret = FALSE; break; } qdaemon->fmaster = FALSE; } } /* If we are no long the master, clear any requested hangup. We may have already hung up before checking this variable in the block above. */ if (! qdaemon->fmaster) qdaemon->fhangup_requested = FALSE; /* Immediately queue up any remote jobs. We don't need local channel numbers for them, since we can disambiguate based on the remote channel number. */ while (qTremote != NULL) { q = qTremote; utdequeue (q); utqueue (&qTsend, q, TRUE); } /* If we are the master, or if we have multiple channels, try to queue up additional local jobs. */ if (qdaemon->fmaster || qdaemon->cchans > 1) { while (qTlocal != NULL && cTchans < qdaemon->cchans) { /* We have room for an additional channel. */ q = qTlocal; if (! fqueue_send (qdaemon, q)) { fret = FALSE; break; } utchanalc (qdaemon, q); } if (! fret) break; } q = qTsend; if (q == NULL) { ulog_user ((const char *) NULL); DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "floop: Waiting for data"); if (! (*qdaemon->qproto->pfwait) (qdaemon)) { fret = FALSE; break; } } else { ulog_user (q->s.zuser); if (! q->fsendfile) { /* Technically, we should add the time required for this call to q->isecs and q->imicros. In practice, the amount of time required should be sufficiently small that it can be safely disregarded. */ if (! (*q->psendfn) (q, qdaemon)) { fret = FALSE; break; } } else { long isecs, imicros; boolean fcharged; long cmax_time; long istart = 0; long inextsecs = 0, inextmicros; if (! fttime (qdaemon, &isecs, &imicros)) { fret = FALSE; break; } fcharged = FALSE; if (q->zlog != NULL) { ulog (LOG_NORMAL, "%s", q->zlog); ubuffree (q->zlog); q->zlog = NULL; } cmax_time = qdaemon->qsys->uuconf_cmax_file_time; if (qdaemon->cchans <= 1) cmax_time = 0; if (cmax_time > 0) istart = ixsysdep_time (NULL); /* We can read the file in a tight loop until we have a command to send, or the file send has been cancelled, or we have a remote job to deal with, or the maximum file send time has been exceeded. We can disregard any changes to qTlocal since we already have something to send anyhow. */ while (q == qTsend && q->fsendfile && qTremote == NULL) { char *zdata; size_t cdata; long ipos; zdata = (*qdaemon->qproto->pzgetspace) (qdaemon, &cdata); if (zdata == NULL) { fret = FALSE; break; } if (ffileeof (q->e)) cdata = 0; else { cdata = cfileread (q->e, zdata, cdata); if (ffileioerror (q->e, cdata)) { /* There is no way to report a file reading error, so we just drop the connection. */ ulog (LOG_ERROR, "read: %s", strerror (errno)); fret = FALSE; break; } } ipos = q->ipos; q->ipos += cdata; q->cbytes += cdata; if (! (*qdaemon->qproto->pfsenddata) (qdaemon, zdata, cdata, q->ilocal, q->iremote, ipos)) { fret = FALSE; break; } if (cdata == 0) { /* We must update the time now, because this call may make an entry in the statistics file. */ inextsecs = ixsysdep_process_time (&inextmicros); DEBUG_MESSAGE4 (DEBUG_UUCP_PROTO, "floop: Charging %ld to %c %s %s", ((inextsecs - isecs) * 1000000 + inextmicros - imicros), q->s.bcmd, q->s.zfrom, q->s.zto); q->isecs += inextsecs - isecs; q->imicros += inextmicros - imicros; fcharged = TRUE; q->fsendfile = FALSE; if (! (*q->psendfn) (q, qdaemon)) fret = FALSE; break; } if (cmax_time > 0 && q->qnext != q && ixsysdep_time (NULL) - istart >= cmax_time) { DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "floop: Switch file"); utdequeue (q); utqueue (&qTsend, q, FALSE); } } if (! fret) break; if (! fcharged) { inextsecs = ixsysdep_process_time (&inextmicros); DEBUG_MESSAGE4 (DEBUG_UUCP_PROTO, "floop: Charging %ld to %c %s %s", ((inextsecs - isecs) * 1000000 + inextmicros - imicros), q->s.bcmd, q->s.zfrom, q->s.zto); q->isecs += inextsecs - isecs; q->imicros += inextmicros - imicros; } if (inextsecs - iTchecktime >= CCHECKWAIT) { if (! fcheck_queue (qdaemon)) { fret = FALSE; break; } } } } } ulog_user ((const char *) NULL); (void) (*qdaemon->qproto->pfshutdown) (qdaemon); if (fret) uwindow_acked (qdaemon, TRUE); else ufailed (qdaemon); return fret; } /* This is called by the protocol routines when they have received some data. If pfexit is not NULL, *pfexit should be set to TRUE if the protocol receive loop should exit back to the main floop routine, above. It is only important to set *pfexit to TRUE if the main loop called the pfwait entry point, so we need never set it to TRUE if we just receive data for a file. This routine never sets *pfexit to FALSE. */ boolean fgot_data (qdaemon, zfirst, cfirst, zsecond, csecond, ilocal, iremote, ipos, fallacked, pfexit) struct sdaemon *qdaemon; const char *zfirst; size_t cfirst; const char *zsecond; size_t csecond; int ilocal; int iremote; long ipos; boolean fallacked; boolean *pfexit; { struct stransfer *q; int cwrote; boolean fret; long isecs, imicros; if (fallacked && qTreceive_ack != NULL) uwindow_acked (qdaemon, TRUE); /* Now we have to decide which transfer structure gets the data. If ilocal is -1, it means that the protocol does not know where to route the data. In that case we route it to the first transfer that is waiting for data, or, if none, as a new command. If ilocal is 0, we either select based on the remote channel number or we have a new command. */ if (ilocal == -1 && qTreceive != NULL) q = qTreceive; else if (ilocal == 0 && iremote > 0 && aqTremote[iremote] != NULL) q = aqTremote[iremote]; else if (ilocal <= 0) { const char *znull; ulog_user ((const char *) NULL); /* This data is part of a command. If there is no null character in the data, this string will be continued by the next packet. Otherwise this must be the last string in the command, and we don't care about what comes after the null byte. */ znull = (const char *) memchr (zfirst, '\0', cfirst); if (znull != NULL) fret = ftadd_cmd (qdaemon, zfirst, (size_t) (znull - zfirst), iremote, TRUE); else { fret = ftadd_cmd (qdaemon, zfirst, cfirst, iremote, FALSE); if (fret && csecond > 0) { znull = (const char *) memchr (zsecond, '\0', csecond); if (znull != NULL) fret = ftadd_cmd (qdaemon, zsecond, (size_t) (znull - zsecond), iremote, TRUE); else fret = ftadd_cmd (qdaemon, zsecond, csecond, iremote, FALSE); } } if (pfexit != NULL && (qdaemon->fhangup || qTremote != NULL)) *pfexit = TRUE; /* Time spent waiting for a new command is not charged to anybody. */ if (! fttime (qdaemon, &iTrecsecs, &iTrecmicros)) fret = FALSE; return fret; } else { /* Get the transfer structure this data is intended for. */ q = qtchan (ilocal); } #if DEBUG > 0 if (q == NULL || q->precfn == NULL) { ulog (LOG_ERROR, "Protocol error: %lu bytes remote %d local %d", (unsigned long) (cfirst + csecond), iremote, ilocal); return FALSE; } #endif ulog_user (q->s.zuser); fret = TRUE; if (q->zlog != NULL && ! q->fsendfile) { ulog (LOG_NORMAL, "%s", q->zlog); ubuffree (q->zlog); q->zlog = NULL; } if (cfirst == 0 || q->fcmd || ! q->frecfile || q != qTtiming_rec) { struct stransfer *qcharge; /* Either we are receiving some sort of command, or we are receiving data for a transfer other than the one we are currently timing. It we are currently timing a transfer, charge any accumulated time to it. Otherwise, if we currently have something to send, just forget about the accumulated time (when using a bidirectional protocol, it's very difficult to charge this time correctly). Otherwise, charge it to whatever transfer receives it. */ if (! fttime (qdaemon, &isecs, &imicros)) fret = FALSE; if (qTtiming_rec != NULL) qcharge = qTtiming_rec; else if (qTsend != NULL) qcharge = NULL; else qcharge = q; if (qcharge != NULL) { DEBUG_MESSAGE4 (DEBUG_UUCP_PROTO, "fgot_data: Charging %ld to %c %s %s", ((isecs - iTrecsecs) * 1000000 + imicros - iTrecmicros), qcharge->s.bcmd, qcharge->s.zfrom, qcharge->s.zto); qcharge->isecs += isecs - iTrecsecs; qcharge->imicros += imicros - iTrecmicros; } iTrecsecs = isecs; iTrecmicros = imicros; /* If we received file data, start timing the new transfer. */ if (cfirst == 0 || q->fcmd || ! q->frecfile) qTtiming_rec = NULL; else qTtiming_rec = q; } /* If we're receiving a command, then accumulate it up to the null byte. */ if (q->fcmd) { const char *znull; znull = NULL; while (cfirst > 0) { size_t cnew; char *znew; znull = (const char *) memchr (zfirst, '\0', cfirst); if (znull != NULL) cnew = znull - zfirst; else cnew = cfirst; znew = zbufalc (q->ccmd + cnew + 1); if (q->ccmd > 0) memcpy (znew, q->zcmd, q->ccmd); memcpy (znew + q->ccmd, zfirst, cnew); znew[q->ccmd + cnew] = '\0'; ubuffree (q->zcmd); q->zcmd = znew; q->ccmd += cnew; if (znull != NULL) break; zfirst = zsecond; cfirst = csecond; csecond = 0; } if (znull != NULL) { char *zcmd; size_t ccmd; zcmd = q->zcmd; ccmd = q->ccmd; q->fcmd = FALSE; q->zcmd = NULL; q->ccmd = 0; if (! (*q->precfn) (q, qdaemon, zcmd, ccmd + 1)) fret = FALSE; ubuffree (zcmd); } if (pfexit != NULL && (qdaemon->fhangup || qdaemon->fmaster || qTsend != NULL)) *pfexit = TRUE; } else if (! q->frecfile || cfirst == 0) { /* We're either not receiving a file or the file transfer is complete. */ q->frecfile = FALSE; if (! (*q->precfn) (q, qdaemon, zfirst, cfirst)) fret = FALSE; if (fret && csecond > 0) return fgot_data (qdaemon, zsecond, csecond, (const char *) NULL, (size_t) 0, ilocal, iremote, ipos + (long) cfirst, FALSE, pfexit); if (pfexit != NULL && (qdaemon->fhangup || qdaemon->fmaster || qTsend != NULL)) *pfexit = TRUE; } else { if (ipos != -1 && ipos != q->ipos) { DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fgot_data: Seeking to %ld", ipos); if (! ffileseek (q->e, ipos)) { ulog (LOG_ERROR, "seek: %s", strerror (errno)); fret = FALSE; } q->ipos = ipos; } if (fret) { while (cfirst > 0) { cwrote = cfilewrite (q->e, (char *) zfirst, cfirst); if (cwrote >= 0 && (size_t) cwrote == cfirst) { #if FREE_SPACE_DELTA > 0 long cfree_space; /* Check that there is still enough space on the disk. If there isn't, we drop the connection, because we have no way to abort a file transfer in progress. */ cfree_space = qdaemon->qsys->uuconf_cfree_space; if (cfree_space > 0 && ((size_t) (q->cbytes / FREE_SPACE_DELTA) != (q->cbytes + cfirst) / FREE_SPACE_DELTA) && ! frec_check_free (q, cfree_space)) { fret = FALSE; break; } #endif q->cbytes += cfirst; q->ipos += cfirst; } else { if (ffileioerror (q->e, cwrote)) ulog (LOG_ERROR, "write: %s", strerror (errno)); else ulog (LOG_ERROR, "Wrote %d to file when trying to write %lu", cwrote, (unsigned long) cfirst); /* Any write error is almost certainly a temporary condition, or else UUCP would not be functioning at all. If we continue to accept the file, we will wind up rejecting it at the end (what else could we do?) and the remote system will throw away the request. We're better off just dropping the connection, which is what happens when we return FALSE, and trying again later. */ fret = FALSE; break; } zfirst = zsecond; cfirst = csecond; csecond = 0; } } if (pfexit != NULL && qdaemon->fhangup) *pfexit = TRUE; } return fret; } /* Accumulate a string into a command. If the command is complete, start up a new transfer. */ static boolean ftadd_cmd (qdaemon, z, clen, iremote, flast) struct sdaemon *qdaemon; const char *z; size_t clen; int iremote; boolean flast; { static char *zbuf; static size_t cbuf; size_t cneed; struct scmd s; cneed = cTcmdlen + clen + 1; if (cneed > cbuf) { zbuf = (char *) xrealloc ((pointer) zbuf, cneed); cbuf = cneed; } memcpy (zbuf + cTcmdlen, z, clen); zbuf[cTcmdlen + clen] = '\0'; if (! flast) { cTcmdlen += clen; return TRUE; } /* Don't save this string for next time. */ cTcmdlen = 0; DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "ftadd_cmd: Got command \"%s\"", zbuf); if (! fparse_cmd (zbuf, &s) || s.bcmd == 'P') { ulog (LOG_ERROR, "Received garbled command \"%s\"", zbuf); return TRUE; } /* Some systems seem to sometimes send garbage at the end of the command. Avoid interpreting it as a size if sizes are not supported. */ if ((qdaemon->ifeatures & FEATURE_SIZES) == 0) s.cbytes = -1; if (s.bcmd != 'H' && s.bcmd != 'Y' && s.bcmd != 'N') ulog_user (s.zuser); else ulog_user ((const char *) NULL); switch (s.bcmd) { case 'S': case 'E': return fremote_send_file_init (qdaemon, &s, iremote); case 'R': return fremote_rec_file_init (qdaemon, &s, iremote); case 'X': return fremote_xcmd_init (qdaemon, &s, iremote); case 'H': /* This is a remote request for a hangup. We close the log files so that they may be moved at this point. */ ulog_close (); ustats_close (); { struct stransfer *q; q = qtransalc ((struct scmd *) NULL); q->psendfn = fremote_hangup_reply; q->iremote = iremote; q->s.bcmd = 'H'; return fqueue_remote (qdaemon, q); } case 'N': /* This means a hangup request is being denied; we just ignore this and wait for further commands. */ return TRUE; case 'Y': /* This is a remote confirmation of a hangup. We reconfirm. */ if (qdaemon->fhangup) return TRUE; #if DEBUG > 0 if (qdaemon->fmaster) ulog (LOG_ERROR, "Got hangup reply as master"); #endif /* Don't check errors rigorously here, since the other side might jump the gun and hang up. The fLog_sighup variable will get set TRUE again when the port is closed. */ fLog_sighup = FALSE; (void) (*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, iremote); qdaemon->fhangup = TRUE; return TRUE; #if DEBUG > 0 default: ulog (LOG_FATAL, "ftadd_cmd: Can't happen"); return FALSE; #endif } } /* The remote system is requesting a hang up. If we have something to do, send an HN. Otherwise send two HY commands (the other side is presumed to send an HY command between the first and second, but we don't bother to wait for it) and hang up. */ static boolean fremote_hangup_reply (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { boolean fret; utransfree (qtrans); if (qTremote == NULL && qTlocal == NULL && qTsend == NULL && qTreceive == NULL) { if (! fqueue (qdaemon, (boolean *) NULL)) return FALSE; if (qTlocal == NULL) { DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fremote_hangup_reply: No work"); fret = ((*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, 0) && (*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, 0)); qdaemon->fhangup = TRUE; return fret; } } DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fremote_hangup_reply: Found work"); fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, "HN", 0, 0); qdaemon->fmaster = TRUE; return fret; } /* As described in system.h, we need to keep track of which files have been successfully received for which we do not know that the other system has received our acknowledgement. This routine is called to keep a list of such files. */ static struct sreceive_ack *qTfree_receive_ack; void usent_receive_ack (qdaemon, qtrans) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; struct stransfer *qtrans; { struct sreceive_ack *q; if (qTfree_receive_ack == NULL) q = (struct sreceive_ack *) xmalloc (sizeof (struct sreceive_ack)); else { q = qTfree_receive_ack; qTfree_receive_ack = q->qnext; } q->qnext = qTreceive_ack; q->zto = zbufcpy (qtrans->s.zto); q->ztemp = zbufcpy (qtrans->s.ztemp); q->fmarked = FALSE; qTreceive_ack = q; } /* Free an sreceive_ack structure. */ static void utfree_receive_ack (q) struct sreceive_ack *q; { ubuffree (q->zto); ubuffree (q->ztemp); q->qnext = qTfree_receive_ack; qTfree_receive_ack = q; } /* This routine is called by the protocol code when either all outstanding data has been acknowledged or one complete window has passed. It may be called directly by the protocol, or it may be called via fgot_data. If one complete window has passed, then all unmarked receives are marked, and we know that all marked ones have been acked. */ void uwindow_acked (qdaemon, fallacked) struct sdaemon *qdaemon; boolean fallacked; { register struct sreceive_ack **pq; pq = &qTreceive_ack; while (*pq != NULL) { if (fallacked || (*pq)->fmarked) { struct sreceive_ack *q; q = *pq; (void) fsysdep_forget_reception (qdaemon->qsys, q->zto, q->ztemp); *pq = q->qnext; utfree_receive_ack (q); } else { (*pq)->fmarked = TRUE; pq = &(*pq)->qnext; } } } /* Free the qTreceive_ack list. */ static void utfree_acked () { struct sreceive_ack *q; q = qTreceive_ack; while (q != NULL) { struct sreceive_ack *qnext; qnext = q->qnext; utfree_receive_ack (q); q = qnext; } qTreceive_ack = NULL; } /* This routine is called when an error occurred and we are crashing out of the connection. It is used to report statistics on failed transfers to the statistics file, and it also discards useless temporary files for file receptions. Note that the number of bytes we report as having been sent has little or nothing to do with the number of bytes the remote site actually received. */ void ufailed (qdaemon) struct sdaemon *qdaemon; { register struct stransfer *q; if (qTsend != NULL) { q = qTsend; do { if ((q->fsendfile || q->frecfile) && q->cbytes > 0) { ustats (FALSE, q->s.zuser, qdaemon->qsys->uuconf_zname, q->fsendfile, q->cbytes, q->isecs, q->imicros, qdaemon->fcaller); if (q->fsendfile) qdaemon->csent += q->cbytes; else qdaemon->creceived += q->cbytes; } if (q->frecfile) (void) frec_discard_temp (qdaemon, q); q = q->qnext; } while (q != qTsend); } if (qTreceive != NULL) { q = qTreceive; do { if ((q->fsendfile || q->frecfile) && q->cbytes > 0) { ustats (FALSE, q->s.zuser, qdaemon->qsys->uuconf_zname, q->fsendfile, q->cbytes, q->isecs, q->imicros, qdaemon->fcaller); if (q->fsendfile) qdaemon->csent += q->cbytes; else qdaemon->creceived += q->cbytes; } if (q->frecfile) (void) frec_discard_temp (qdaemon, q); q = q->qnext; } while (q != qTreceive); } } /* When a local poll file is found, it is entered on the queue like any other job. When it is pulled off the queue, this function is called. It just calls fsysdep_did_work, which will remove the poll file. This ensures that poll files are only removed if the system is actually called. */ /*ARGSUSED*/ static boolean flocal_poll_file (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon ATTRIBUTE_UNUSED; { boolean fret; fret = fsysdep_did_work (qtrans->s.pseq); utransfree (qtrans); return fret; } uucp-1.07/send.c0000664000076400007640000012344407665321756007236 /* send.c Routines to send a file. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char send_rcsid[] = "$Id: send.c,v 1.57 2002/03/05 19:10:41 ian Rel $"; #endif #include #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "prot.h" #include "trans.h" /* We keep this information in the pinfo field of the stransfer structure. */ struct ssendinfo { /* Local user to send mail to (may be NULL). */ char *zmail; /* Full file name. */ char *zfile; /* Number of bytes in file. */ long cbytes; /* TRUE if this was a local request. */ boolean flocal; /* TRUE if this is a spool directory file. */ boolean fspool; /* TRUE if the file has been completely sent. */ boolean fsent; /* TRUE if the file send will never succeed; used by flocal_send_cancelled. */ boolean fnever; /* Execution file for sending an unsupported E request. */ char *zexec; /* Confirmation command received in fsend_await_confirm. */ char *zconfirm; }; /* Local functions. */ static void usfree_send P((struct stransfer *qtrans)); static boolean flocal_send_fail P((struct scmd *qcmd, struct sdaemon *qdaemon, const char *zwhy)); static boolean flocal_send_request P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static boolean flocal_send_await_reply P((struct stransfer *qtrans, struct sdaemon *qdaemon, const char *zdata, size_t cdata)); static boolean flocal_send_cancelled P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static boolean flocal_send_open_file P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static boolean fremote_rec_fail P((struct sdaemon *qdaemon, enum tfailure twhy, int iremote)); static boolean fremote_rec_fail_send P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static boolean fremote_rec_reply P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static boolean fsend_file_end P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static boolean fsend_await_confirm P((struct stransfer *qtrans, struct sdaemon *qdaemon, const char *zdata, size_t cdata)); static boolean fsend_exec_file_init P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static void usadd_exec_line P((char **pz, size_t *pcalc, size_t *pclen, int bcmd, const char *z1, const char *z2, boolean fquote)); static boolean fsend_exec_file P((struct stransfer *qtrans, struct sdaemon *qdaemon)); /* Free up a send stransfer structure. */ static void usfree_send (qtrans) struct stransfer *qtrans; { struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; if (qinfo != NULL) { ubuffree (qinfo->zmail); ubuffree (qinfo->zfile); ubuffree (qinfo->zexec); ubuffree (qinfo->zconfirm); xfree (qtrans->pinfo); } utransfree (qtrans); } /* Set up a local request to send a file. This may be called before we have even tried to call the remote system. If we are using a traditional protocol, which doesn't support channel numbers and doesn't permit the file to be sent until an acknowledgement has been received, the sequence of function calls looks like this: flocal_send_file_init --> fqueue_local flocal_send_request (sends S request) --> fqueue_receive flocal_send_await_reply (waits for SY) --> fqueue_send flocal_send_open_file (opens file, calls pffile) --> fqueue_send send file fsend_file_end (calls pffile) --> fqueue_receive fsend_await_confirm (waits for CY) If flocal_send_await_reply gets an SN, it deletes the request. If the SY reply contains a file position at which to start sending, flocal_send_await_reply sets qinfo->ipos. This gets more complex if the protocol supports channels. In that case, we want to start sending the file data immediately, to avoid the round trip delay between flocal_send_request and flocal_send_await_reply. To do this, flocal_send_request calls fqueue_send rather than fqueue_receive. The main execution sequence looks like this: flocal_send_file_init --> fqueue_local flocal_send_request (sends S request) --> fqueue_send flocal_send_open_file (opens file, calls pffile) --> fqueue_send send file fsend_file_end (calls pffile) --> fqueue_receive sometime: flocal_send_await_reply (waits for SY) fsend_await_confirm (waits for CY) In this case flocal_send_await_reply must be run before fsend_await_confirm; it may be run anytime after flocal_send_request. If flocal_send_await_reply is called before the entire file has been sent: if it gets an SN, it sets the file position to the end and arranges to call flocal_send_cancelled. If it gets a file position request, it must adjust the file position accordingly. If flocal_send_await_reply is called after the entire file has been sent: if it gets an SN, it can simply delete the request. It can ignore any file position request. If the request is not deleted, flocal_send_await_reply must arrange for the next string to be passed to fsend_await_confirm. Presumably fsend_await_confirm will only be called after the entire file has been sent. Just to make things even more complex, these same routines support sending execution requests, since that is much like sending a file. For an execution request, the bcmd character will be E rather than S. If an execution request is being sent to a system which does not support them, it must be sent as two S requests instead. The second one will be the execution file, but no actual file is created; instead the zexec and znext fields in the ssendinfo structure are used. So if the bcmd character is E, then if the zexec field is NULL, the data file is being sent, otherwise the fake execution file is being sent. */ boolean flocal_send_file_init (qdaemon, qcmd) struct sdaemon *qdaemon; struct scmd *qcmd; { const struct uuconf_system *qsys; boolean fspool; char *zfile; long cbytes; struct ssendinfo *qinfo; struct stransfer *qtrans; qsys = qdaemon->qsys; if (qdaemon->fcaller ? ! qsys->uuconf_fcall_transfer : ! qsys->uuconf_fcalled_transfer) { /* uux or uucp should have already made sure that the transfer is possible, but it might have changed since then. */ if (! qsys->uuconf_fcall_transfer && ! qsys->uuconf_fcalled_transfer) return flocal_send_fail (qcmd, qdaemon, "not permitted to transfer files"); /* We can't do the request now, but it may get done later. */ return TRUE; } /* The 'C' option means that the file has been copied to the spool directory. */ if (strchr (qcmd->zoptions, 'C') == NULL && ! fspool_file (qcmd->zfrom)) { fspool = FALSE; if (! fin_directory_list (qcmd->zfrom, qsys->uuconf_pzlocal_send, qsys->uuconf_zpubdir, TRUE, TRUE, qcmd->zuser)) return flocal_send_fail (qcmd, qdaemon, "not permitted to send"); zfile = zbufcpy (qcmd->zfrom); } else { fspool = TRUE; zfile = zsysdep_spool_file_name (qsys, qcmd->ztemp, qcmd->pseq); if (zfile == NULL) return FALSE; } /* Make sure we meet any local size restrictions. The connection may not have been opened at this point, so we can't check remote size restrictions. */ cbytes = csysdep_size (zfile); if (cbytes < 0) { ubuffree (zfile); if (cbytes != -1) return flocal_send_fail (qcmd, qdaemon, "can not get size"); /* A cbytes value of -1 means that the file does not exist. This can happen legitimately if it has already been sent from the spool directory. */ if (! fspool) return flocal_send_fail (qcmd, qdaemon, "does not exist"); (void) fsysdep_did_work (qcmd->pseq); return TRUE; } if (qdaemon->clocal_size != -1 && qdaemon->clocal_size < cbytes) { ubuffree (zfile); if (qdaemon->cmax_ever == -2) { long c1, c2; c1 = cmax_size_ever (qsys->uuconf_qcall_local_size); c2 = cmax_size_ever (qsys->uuconf_qcalled_local_size); if (c1 > c2) qdaemon->cmax_ever = c1; else qdaemon->cmax_ever = c2; } if (qdaemon->cmax_ever != -1 && qdaemon->cmax_ever < qcmd->cbytes) return flocal_send_fail (qcmd, qdaemon, "too large to send"); return TRUE; } /* We are now prepared to send the command to the remote system. We queue up a transfer request to send the command when we are ready. */ qinfo = (struct ssendinfo *) xmalloc (sizeof (struct ssendinfo)); if (strchr (qcmd->zoptions, 'm') == NULL) qinfo->zmail = NULL; else qinfo->zmail = zbufcpy (qcmd->zuser); qinfo->zfile = zfile; qinfo->cbytes = cbytes; qinfo->flocal = strchr (qcmd->zuser, '!') == NULL; qinfo->fspool = fspool; qinfo->fsent = FALSE; qinfo->zexec = NULL; qinfo->zconfirm = NULL; qtrans = qtransalc (qcmd); qtrans->psendfn = flocal_send_request; qtrans->pinfo = (pointer) qinfo; return fqueue_local (qdaemon, qtrans); } /* Clean up after a failing local send request. If zwhy is not NULL, this reports an error to the log file and to the user. */ static boolean flocal_send_fail (qcmd, qdaemon, zwhy) struct scmd *qcmd; struct sdaemon *qdaemon; const char *zwhy; { if (zwhy != NULL) { const char *zfrom; char *zfree; const char *ztemp; if (qcmd->bcmd != 'E') { zfrom = qcmd->zfrom; zfree = NULL; } else { zfree = zbufalc (strlen (qcmd->zfrom) + sizeof " (execution of \"\")" + strlen (qcmd->zcmd)); sprintf (zfree, "%s (execution of \"%s\")", qcmd->zfrom, qcmd->zcmd); zfrom = zfree; } ulog (LOG_ERROR, "%s: %s", zfrom, zwhy); /* We only save the temporary file if this is a request from the local system; otherwise a remote system could launch a denial of service attack by filling up the .Preserve directory (local users have much simpler methods for this type of denial of service attack, so there is little point to using a more sophisticated scheme). */ if (strchr (qcmd->zuser, '!') == NULL) ztemp = zsysdep_save_temp_file (qcmd->pseq); else ztemp = NULL; (void) fmail_transfer (FALSE, qcmd->zuser, (const char *) NULL, zwhy, zfrom, (const char *) NULL, qcmd->zto, qdaemon->qsys->uuconf_zname, ztemp); ubuffree (zfree); } (void) fsysdep_did_work (qcmd->pseq); return TRUE; } /* This is called when we are ready to send the request to the remote system. We form the request and send it over. If the protocol does not support multiple channels, we start waiting for the response; otherwise we can start sending the file immediately. */ static boolean flocal_send_request (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; boolean fquote; const struct scmd *qcmd; struct scmd squoted; const char *znotify; char absize[20]; char *zsend; boolean fret; /* Make sure the file meets any remote size restrictions. */ if (qdaemon->cmax_receive != -1 && qdaemon->cmax_receive < qinfo->cbytes) { fret = flocal_send_fail (&qtrans->s, qdaemon, "too large for receiver"); usfree_send (qtrans); return fret; } /* Make sure the file still exists--it may have been removed between the conversation startup and now. After we have sent over the S command we must give an error if we can't find the file. */ if (! fsysdep_file_exists (qinfo->zfile)) { (void) fsysdep_did_work (qtrans->s.pseq); usfree_send (qtrans); return TRUE; } /* If we are using a protocol which can make multiple channels, then we can open and send the file whenever we are ready. This is because we will be able to distinguish the response by the channel it is directed to. This assumes that every protocol which supports multiple channels also supports sending the file position in mid-stream, since otherwise we would not be able to restart files. */ qtrans->fcmd = TRUE; qtrans->psendfn = flocal_send_open_file; qtrans->precfn = flocal_send_await_reply; if (qdaemon->cchans > 1) fret = fqueue_send (qdaemon, qtrans); else fret = fqueue_receive (qdaemon, qtrans); if (! fret) return FALSE; fquote = fcmd_needs_quotes (&qtrans->s); if (! fquote) qcmd = &qtrans->s; else { if ((qdaemon->ifeatures & FEATURE_QUOTES) == 0) { fret = flocal_send_fail (&qtrans->s, qdaemon, "remote system does not support required quoting"); usfree_send (qtrans); return fret; } uquote_cmd (&qtrans->s, &squoted); qcmd = &squoted; } /* Construct the notify string to send. If we are going to send a size or an execution command, it must be non-empty. */ znotify = qcmd->znotify; if (znotify == NULL) znotify = ""; if ((qdaemon->ifeatures & FEATURE_SIZES) != 0 || (qcmd->bcmd == 'E' && (qdaemon->ifeatures & FEATURE_EXEC) != 0)) { if (*znotify == '\0') znotify = "\"\""; } else { /* We don't need a notify string. Some crufty UUCP code can't handle a pair of double quotes. */ if (strcmp (znotify, "\"\"") == 0) znotify = ""; } /* Construct the size string to send. */ if ((qdaemon->ifeatures & FEATURE_SIZES) == 0 && (qcmd->bcmd != 'E' || (qdaemon->ifeatures & FEATURE_EXEC) == 0)) absize[0] = '\0'; else if ((qdaemon->ifeatures & FEATURE_V103) == 0) sprintf (absize, "0x%lx", (unsigned long) qinfo->cbytes); else sprintf (absize, "%ld", qinfo->cbytes); zsend = zbufalc (strlen (qcmd->zfrom) + strlen (qcmd->zto) + strlen (qcmd->zuser) + strlen (qcmd->zoptions) + strlen (qcmd->ztemp) + strlen (znotify) + strlen (absize) + (qcmd->zcmd != NULL ? strlen (qcmd->zcmd) : 0) + 50); /* If this an execution request and the other side supports execution requests, we send an E command. Otherwise we send an S command. The case of an execution request when we are sending the fake execution file is handled just like an S request at this point. */ if (qcmd->bcmd == 'E' && (qdaemon->ifeatures & FEATURE_EXEC) != 0) { /* Send the string E zfrom zto zuser zoptions ztemp imode znotify size zcmd to the remote system. We put a '-' in front of the (possibly empty) options and a '0' in front of the mode. */ sprintf (zsend, "E %s %s %s -%s %s 0%o %s %s %s", qcmd->zfrom, qcmd->zto, qcmd->zuser, qcmd->zoptions, qcmd->ztemp, qcmd->imode, znotify, absize, qcmd->zcmd); } else { const char *zoptions, *zdummy; /* Send the string S zfrom zto zuser zoptions ztemp imode znotify to the remote system. We put a '-' in front of the (possibly empty) options and a '0' in front of the mode. If size negotiation is supported, we also send the size; in this case if znotify is empty we must send it as "". If this is really an execution request, we have to simplify the options string to remove the various execution options which may confuse the remote system. SVR4 expects a string "dummy" between the notify string and the size; I don't know why. */ if (qcmd->bcmd != 'E') zoptions = qcmd->zoptions; else if (strchr (qcmd->zoptions, 'C') != NULL) { /* This should set zoptions to "C", but at least one UUCP program gets confused by it. That means that it will fail in certain cases, but I suppose we might as well kowtow to compatibility. This shouldn't matter to any other program, I hope. */ zoptions = ""; } else zoptions = "c"; if ((qdaemon->ifeatures & FEATURE_SVR4) != 0) zdummy = " dummy "; else zdummy = " "; sprintf (zsend, "S %s %s %s -%s %s 0%o %s%s%s", qcmd->zfrom, qcmd->zto, qcmd->zuser, zoptions, qcmd->ztemp, qcmd->imode, znotify, zdummy, absize); } fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, qtrans->ilocal, qtrans->iremote); ubuffree (zsend); if (fquote) ufree_quoted_cmd (&squoted); /* If fret is FALSE, we should free qtrans here, but see the comment at the end of flocal_rec_send_request. */ return fret; } /* This is called when a reply is received for the send request. As described at length above, if the protocol supports multiple channels we may be in the middle of sending the file, or we may even finished sending the file. */ static boolean flocal_send_await_reply (qtrans, qdaemon, zdata, cdata) struct stransfer *qtrans; struct sdaemon *qdaemon; const char *zdata; size_t cdata ATTRIBUTE_UNUSED; { struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; char bcmd; if (qtrans->s.bcmd == 'E' && (qdaemon->ifeatures & FEATURE_EXEC) != 0) bcmd = 'E'; else bcmd = 'S'; if (zdata[0] != bcmd || (zdata[1] != 'Y' && zdata[1] != 'N')) { ulog (LOG_ERROR, "%s: Bad response to %c request: \"%s\"", qtrans->s.zfrom, bcmd, zdata); usfree_send (qtrans); return FALSE; } if (zdata[1] == 'N') { const char *zerr; boolean fnever; fnever = TRUE; if (zdata[2] == '2') zerr = "permission denied by remote"; else if (zdata[2] == '4') { zerr = "remote cannot create work files"; fnever = FALSE; } else if (zdata[2] == '6') { zerr = "too large for remote now"; fnever = FALSE; } else if (zdata[2] == '7') { /* The file is too large to ever send. */ zerr = "too large for remote"; } else if (zdata[2] == '8') { /* The file was already received by the remote system. This is not an error, it just means that the ack from the remote was lost in the previous conversation, and there is no need to resend the file. */ zerr = NULL; } else if (zdata[2] == '9') { /* Remote has run out of channels. */ zerr = "too many channels for remote"; fnever = FALSE; /* Drop one channel; using exactly one channel causes slightly different behahaviour in a few places, so don't decrement to one. */ if (qdaemon->cchans > 2) --qdaemon->cchans; } else zerr = "unknown reason"; if (! fnever || (qtrans->s.bcmd == 'E' && (qdaemon->ifeatures & FEATURE_EXEC) == 0 && qinfo->zexec == NULL)) { if (qtrans->s.bcmd == 'E') ulog (LOG_ERROR, "%s (execution of \"%s\"): %s", qtrans->s.zfrom, qtrans->s.zcmd, zerr); else ulog (LOG_ERROR, "%s: %s", qtrans->s.zfrom, zerr); } else { if (! flocal_send_fail (&qtrans->s, qdaemon, zerr)) return FALSE; } /* If the protocol does not support multiple channels, we can simply remove the transaction. Otherwise we must make sure the remote side knows that we have finished sending the file data. If we have already sent the entire file, there will be no confusion. */ if (qdaemon->cchans == 1 || qinfo->fsent) { /* If we are breaking a 'E' command into two 'S' commands, and that was for the first 'S' command, we still have to send the second one. */ if (fnever && qtrans->s.bcmd == 'E' && (qdaemon->ifeatures & FEATURE_EXEC) == 0 && qinfo->zexec == NULL) return fsend_exec_file_init (qtrans, qdaemon); usfree_send (qtrans); return TRUE; } else { /* Seek to the end of the file so that the next read will send end of file. We have to be careful here, because we may not have opened the file yet, or we may have actually already sent end of file--we could be being called because of data received while the end of file block was sent. */ if (qtrans->fsendfile && ! ffileseekend (qtrans->e)) { ulog (LOG_ERROR, "seek to end: %s", strerror (errno)); usfree_send (qtrans); return FALSE; } qtrans->psendfn = flocal_send_cancelled; qtrans->precfn = NULL; qinfo->fnever = fnever; return fqueue_send (qdaemon, qtrans); } } /* A number following the SY or EY is the file position to start sending from. If we are already sending the file, we must set the position accordingly. */ if (zdata[2] != '\0') { long cskip; cskip = strtol ((char *) (zdata + 2), (char **) NULL, 0); if (cskip > 0 && qtrans->ipos < cskip) { if (qtrans->fsendfile && ! qinfo->fsent) { if (! ffileseek (qtrans->e, cskip)) { ulog (LOG_ERROR, "seek: %s", strerror (errno)); usfree_send (qtrans); return FALSE; } } qtrans->ipos = cskip; } } /* Now queue up to send the file or to wait for the confirmation. We already set psendfn at the end of flocal_send_request. If the protocol supports multiple channels, we have already called fqueue_send; calling it again would move the request in the queue, which would make the log file a bit confusing. */ qtrans->fcmd = TRUE; qtrans->precfn = fsend_await_confirm; if (qinfo->fsent) return fqueue_receive (qdaemon, qtrans); else if (qdaemon->cchans <= 1) return fqueue_send (qdaemon, qtrans); else return TRUE; } /* Open the file, if any, and prepare to send it. */ static boolean flocal_send_open_file (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; const char *zuser; /* If this is not a fake execution file, open it. */ if (qinfo->zexec == NULL) { /* If there is an ! in the user name, this is a remote request queued up by fremote_xcmd_init. */ zuser = qtrans->s.zuser; if (strchr (zuser, '!') != NULL) zuser = NULL; qtrans->e = esysdep_open_send (qdaemon->qsys, qinfo->zfile, ! qinfo->fspool, zuser); if (! ffileisopen (qtrans->e)) { (void) fmail_transfer (FALSE, qtrans->s.zuser, (const char *) NULL, "cannot open file", qtrans->s.zfrom, (const char *) NULL, qtrans->s.zto, qdaemon->qsys->uuconf_zname, (qinfo->flocal ? zsysdep_save_temp_file (qtrans->s.pseq) : (const char *) NULL)); (void) fsysdep_did_work (qtrans->s.pseq); usfree_send (qtrans); /* Unfortunately, there is no way to cancel a file send after we've already put it in progress. So we have to return FALSE to drop the connection. */ return FALSE; } } /* If flocal_send_await_reply has received a reply with a file position, it will have set qtrans->ipos to the position at which to start. */ if (qtrans->ipos > 0) { if (qinfo->zexec != NULL) { if (qtrans->ipos > qtrans->cbytes) qtrans->ipos = qtrans->cbytes; } else { if (! ffileseek (qtrans->e, qtrans->ipos)) { ulog (LOG_ERROR, "seek: %s", strerror (errno)); usfree_send (qtrans); return FALSE; } } } /* We don't bother to log sending the execution file. */ if (qinfo->zexec == NULL) { const char *zsend; char *zalc; if (qtrans->s.bcmd != 'E') { zsend = qtrans->s.zfrom; zalc = NULL; } else { zalc = zbufalc (strlen (qtrans->s.zcmd) + sizeof " ()" + strlen (qtrans->s.zfrom)); sprintf (zalc, "%s (%s)", qtrans->s.zcmd, qtrans->s.zfrom); zsend = zalc; } qtrans->zlog = zbufalc (sizeof "Sending ( bytes resume at )" + strlen (zsend) + 50); sprintf (qtrans->zlog, "Sending %s (%ld bytes", zsend, qinfo->cbytes); if (qtrans->ipos > 0) sprintf (qtrans->zlog + strlen (qtrans->zlog), " resume at %ld", qtrans->ipos); strcat (qtrans->zlog, ")"); ubuffree (zalc); } if (qdaemon->qproto->pffile != NULL) { boolean fhandled; if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, TRUE, qinfo->cbytes - qtrans->ipos, &fhandled)) { usfree_send (qtrans); return FALSE; } if (fhandled) return TRUE; } if (qinfo->zexec != NULL) qtrans->psendfn = fsend_exec_file; else { qtrans->fsendfile = TRUE; qtrans->psendfn = fsend_file_end; } return fqueue_send (qdaemon, qtrans); } /* Cancel a file send. This is only called for a protocol which supports multiple channels. It is needed so that both systems agree as to when a channel is no longer needed. */ static boolean flocal_send_cancelled (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; /* If we are breaking a 'E' command into two 'S' commands, and that was for the first 'S' command, and the first 'S' command will never be sent, we still have to send the second one. */ if (qinfo->fnever && qtrans->s.bcmd == 'E' && (qdaemon->ifeatures & FEATURE_EXEC) == 0 && qinfo->zexec == NULL) return fsend_exec_file_init (qtrans, qdaemon); usfree_send (qtrans); return TRUE; } /* A remote request to receive a file (meaning that we have to send a file). The sequence of functions calls is as follows: fremote_rec_file_init (open file) --> fqueue_remote fremote_rec_reply (send RY, call pffile) --> fqueue_send send file fsend_file_end (calls pffile) --> fqueue_receive fsend_await_confirm (waits for CY) */ boolean fremote_rec_file_init (qdaemon, qcmd, iremote) struct sdaemon *qdaemon; struct scmd *qcmd; int iremote; { const struct uuconf_system *qsys; char *zfile; boolean fbadname; long cbytes; unsigned int imode; openfile_t e; struct ssendinfo *qinfo; struct stransfer *qtrans; qsys = qdaemon->qsys; if (! qsys->uuconf_fsend_request) { ulog (LOG_ERROR, "%s: not permitted to send files to remote", qcmd->zfrom); return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote); } if (fspool_file (qcmd->zfrom)) { ulog (LOG_ERROR, "%s: not permitted to send", qcmd->zfrom); return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote); } zfile = zsysdep_local_file (qcmd->zfrom, qsys->uuconf_zpubdir, &fbadname); if (zfile == NULL && fbadname) { ulog (LOG_ERROR, "%s: bad local file name", qcmd->zfrom); return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote); } if (zfile != NULL) { char *zbased; zbased = zsysdep_add_base (zfile, qcmd->zto); ubuffree (zfile); zfile = zbased; } if (zfile == NULL) return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote); if (! fin_directory_list (zfile, qsys->uuconf_pzremote_send, qsys->uuconf_zpubdir, TRUE, TRUE, (const char *) NULL)) { ulog (LOG_ERROR, "%s: not permitted to send", zfile); ubuffree (zfile); return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote); } /* If the file is larger than the amount of space the other side reported, we can't send it. Should we adjust this check based on the restart position? */ cbytes = csysdep_size (zfile); if (cbytes != -1 && ((qcmd->cbytes != -1 && qcmd->cbytes < cbytes) || (qdaemon->cremote_size != -1 && qdaemon->cremote_size < cbytes) || (qdaemon->cmax_receive != -1 && qdaemon->cmax_receive < cbytes))) { ulog (LOG_ERROR, "%s: too large to send", zfile); ubuffree (zfile); return fremote_rec_fail (qdaemon, FAILURE_SIZE, iremote); } imode = ixsysdep_file_mode (zfile); e = esysdep_open_send (qsys, zfile, TRUE, (const char *) NULL); if (! ffileisopen (e)) { ubuffree (zfile); return fremote_rec_fail (qdaemon, FAILURE_OPEN, iremote); } /* If the remote requested that the file send start from a particular position, arrange to do so. */ if (qcmd->ipos > 0) { if (! ffileseek (e, qcmd->ipos)) { ulog (LOG_ERROR, "seek: %s", strerror (errno)); ubuffree (zfile); return FALSE; } } qinfo = (struct ssendinfo *) xmalloc (sizeof (struct ssendinfo)); qinfo->zmail = NULL; qinfo->zfile = zfile; qinfo->cbytes = cbytes; qinfo->flocal = FALSE; qinfo->fspool = FALSE; qinfo->fsent = FALSE; qinfo->zexec = NULL; qinfo->zconfirm = NULL; qtrans = qtransalc (qcmd); qtrans->psendfn = fremote_rec_reply; qtrans->iremote = iremote; qtrans->pinfo = (pointer) qinfo; qtrans->e = e; qtrans->ipos = qcmd->ipos; qtrans->s.imode = imode; return fqueue_remote (qdaemon, qtrans); } /* Reply to a receive request from the remote system, and prepare to start sending the file. */ static boolean fremote_rec_reply (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; char absend[50]; qtrans->fsendfile = TRUE; qtrans->psendfn = fsend_file_end; qtrans->fcmd = TRUE; qtrans->precfn = fsend_await_confirm; if (! fqueue_send (qdaemon, qtrans)) return FALSE; qtrans->zlog = zbufalc (sizeof "Sending ( bytes) " + strlen (qtrans->s.zfrom) + 25); sprintf (qtrans->zlog, "Sending %s (%ld bytes)", qtrans->s.zfrom, qinfo->cbytes); /* We send the file size because SVR4 UUCP does. We don't look for it. We send a trailing M if we want to request a hangup. We send it both after the mode and at the end of the entire string; I don't know where programs look for it. */ if (qdaemon->frequest_hangup) DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fremote_rec_reply: Requesting remote to transfer control"); sprintf (absend, "RY 0%o%s 0x%lx%s", qtrans->s.imode, qdaemon->frequest_hangup ? "M" : "", (unsigned long) qinfo->cbytes, qdaemon->frequest_hangup ? "M" : ""); if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, absend, qtrans->ilocal, qtrans->iremote)) { (void) ffileclose (qtrans->e); qtrans->e = EFILECLOSED; /* Should probably free qtrans here, but see the comment at the end of flocal_rec_send_request. */ return FALSE; } if (qdaemon->qproto->pffile != NULL) { boolean fhandled; if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, TRUE, qinfo->cbytes, &fhandled)) { usfree_send (qtrans); return FALSE; } } return TRUE; } /* If we can't send a file as requested by the remote system, queue up a failure reply which will be sent when possible. */ static boolean fremote_rec_fail (qdaemon, twhy, iremote) struct sdaemon *qdaemon; enum tfailure twhy; int iremote; { enum tfailure *ptinfo; struct stransfer *qtrans; ptinfo = (enum tfailure *) xmalloc (sizeof (enum tfailure)); *ptinfo = twhy; qtrans = qtransalc ((struct scmd *) NULL); qtrans->psendfn = fremote_rec_fail_send; qtrans->iremote = iremote; qtrans->pinfo = (pointer) ptinfo; return fqueue_remote (qdaemon, qtrans); } /* Send a failure string for a receive command to the remote system; this is called when we are ready to reply to the command. */ static boolean fremote_rec_fail_send (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { enum tfailure *ptinfo = (enum tfailure *) qtrans->pinfo; const char *z; int ilocal, iremote; switch (*ptinfo) { case FAILURE_PERM: case FAILURE_OPEN: z = "RN2"; break; case FAILURE_SIZE: z = "RN6"; break; default: z = "RN"; break; } ilocal = qtrans->ilocal; iremote = qtrans->iremote; xfree (qtrans->pinfo); utransfree (qtrans); return (*qdaemon->qproto->pfsendcmd) (qdaemon, z, ilocal, iremote); } /* This is called when the main loop has finished sending a file. It prepares to wait for a response from the remote system. Note that if this is a local request and the protocol supports multiple channels, we may not even have received a confirmation of the send request. */ static boolean fsend_file_end (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; if (qdaemon->qproto->pffile != NULL) { boolean fhandled; if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, FALSE, TRUE, (long) -1, &fhandled)) { usfree_send (qtrans); return FALSE; } if (fhandled) return TRUE; } qinfo->fsent = TRUE; /* If zconfirm is set, then we have already received the confirmation, and should call fsend_await_confirm directly. */ if (qinfo->zconfirm != NULL) return fsend_await_confirm (qtrans, qdaemon, qinfo->zconfirm, strlen (qinfo->zconfirm) + 1); /* qtrans->precfn should have been set by a previous function. */ return fqueue_receive (qdaemon, qtrans); } /* Handle the confirmation string received after sending a file. */ /*ARGSUSED*/ static boolean fsend_await_confirm (qtrans, qdaemon, zdata, cdata) struct stransfer *qtrans; struct sdaemon *qdaemon; const char *zdata; size_t cdata ATTRIBUTE_UNUSED; { struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; boolean fnever; const char *zerr; /* If fsent is FALSE, it means that we have received the confirmation before fsend_file_end got called. To avoid confusion, we save away the confirmation message, and let fsend_file_end call us directly. If we did not do this, we would have to fix a thorny race condition in floop, which wants to refer to the qtrans structure after sending the end of the file. */ if (! qinfo->fsent) { qinfo->zconfirm = zbufcpy (zdata); return TRUE; } if (qinfo->zexec == NULL) { (void) ffileclose (qtrans->e); qtrans->e = EFILECLOSED; } fnever = FALSE; if (zdata[0] != 'C' || (zdata[1] != 'Y' && zdata[1] != 'N')) { zerr = "bad confirmation from remote"; ulog (LOG_ERROR, "%s: %s \"%s\"", qtrans->s.zfrom, zerr, zdata); } else if (zdata[1] == 'N') { fnever = TRUE; if (zdata[2] == '5') { zerr = "file could not be stored in final location"; ulog (LOG_ERROR, "%s: %s", qtrans->s.zfrom, zerr); } else { zerr = "file send failed for unknown reason"; ulog (LOG_ERROR, "%s: %s \"%s\"", qtrans->s.zfrom, zerr, zdata); } } else { zerr = NULL; /* If we receive CYM, it means that the other side wants us to hang up so that they can send us something. The fhangup_requested field is checked in the main loop. */ if (zdata[2] == 'M' && qdaemon->fmaster) { DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fsend_await_confirm: Remote has requested transfer of control"); qdaemon->fhangup_requested = TRUE; } } ustats (zerr == NULL, qtrans->s.zuser, qdaemon->qsys->uuconf_zname, TRUE, qtrans->cbytes, qtrans->isecs, qtrans->imicros, qdaemon->fcaller); qdaemon->csent += qtrans->cbytes; if (zerr == NULL) { /* If this is an execution request, and the remote system doesn't support execution requests, we have to set up the fake execution file and loop around again. */ if (qtrans->s.bcmd == 'E' && (qdaemon->ifeatures & FEATURE_EXEC) == 0 && qinfo->zexec == NULL) return fsend_exec_file_init (qtrans, qdaemon); /* Send mail about the transfer if requested. */ if (qinfo->zmail != NULL && *qinfo->zmail != '\0') (void) fmail_transfer (TRUE, qtrans->s.zuser, qinfo->zmail, (const char *) NULL, qtrans->s.zfrom, (const char *) NULL, qtrans->s.zto, qdaemon->qsys->uuconf_zname, (const char *) NULL); if (qtrans->s.pseq != NULL) (void) fsysdep_did_work (qtrans->s.pseq); } else { /* If the file send failed, we only try to save the file and send mail if it was requested locally and it will never succeed. We send mail to qinfo->zmail if set, otherwise to qtrans->s.zuser. I hope this is reasonable. */ if (fnever && qinfo->flocal) { (void) fmail_transfer (FALSE, qtrans->s.zuser, qinfo->zmail, zerr, qtrans->s.zfrom, (const char *) NULL, qtrans->s.zto, qdaemon->qsys->uuconf_zname, zsysdep_save_temp_file (qtrans->s.pseq)); (void) fsysdep_did_work (qtrans->s.pseq); } } usfree_send (qtrans); return TRUE; } /* Prepare to send an execution file to a system which does not support execution requests. We build the execution file in memory, and then call flocal_send_request as though we were sending a real file. Instead of sending a file, the code in flocal_send_open_file will arrange to call fsend_exec_file which will send data out of the buffer we have created. */ static boolean fsend_exec_file_init (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; char *zxqtfile; char abtname[CFILE_NAME_LEN]; char abxname[CFILE_NAME_LEN]; char *z; size_t calc, clen; boolean fquote; z = NULL; calc = 0; clen = 0; fquote = fcmd_needs_quotes (&qtrans->s); if (fquote) usadd_exec_line (&z, &calc, &clen, 'Q', "", "", TRUE); usadd_exec_line (&z, &calc, &clen, 'U', qtrans->s.zuser, qdaemon->zlocalname, fquote); usadd_exec_line (&z, &calc, &clen, 'F', qtrans->s.zto, "", fquote); usadd_exec_line (&z, &calc, &clen, 'I', qtrans->s.zto, "", fquote); if (strchr (qtrans->s.zoptions, 'N') != NULL) usadd_exec_line (&z, &calc, &clen, 'N', "", "", fquote); if (strchr (qtrans->s.zoptions, 'Z') != NULL) usadd_exec_line (&z, &calc, &clen, 'Z', "", "", fquote); if (strchr (qtrans->s.zoptions, 'R') != NULL) usadd_exec_line (&z, &calc, &clen, 'R', qtrans->s.znotify, "", fquote); if (strchr (qtrans->s.zoptions, 'e') != NULL) usadd_exec_line (&z, &calc, &clen, 'e', "", "", fquote); /* For the command, we only quote backslashes. If there is anything which requires fancier handling, uux will not have generated an 'E' command. */ if (! fquote) usadd_exec_line (&z, &calc, &clen, 'C', qtrans->s.zcmd, "", FALSE); else { char *zquoted; zquoted = zquote_cmd_string (qtrans->s.zcmd, TRUE); usadd_exec_line (&z, &calc, &clen, 'C', zquoted, "", FALSE); ubuffree (zquoted); } qinfo->zexec = z; qinfo->cbytes = clen; zxqtfile = zsysdep_data_file_name (qdaemon->qsys, qdaemon->zlocalname, BDEFAULT_UUX_GRADE, TRUE, abtname, (char *) NULL, abxname); if (zxqtfile == NULL) { usfree_send (qtrans); return FALSE; } ubuffree (zxqtfile); ubuffree ((char *) qtrans->s.zfrom); qtrans->s.zfrom = zbufcpy (abtname); ubuffree ((char *) qtrans->s.zto); qtrans->s.zto = zbufcpy (abxname); ubuffree ((char *) qtrans->s.zoptions); qtrans->s.zoptions = zbufcpy ("C"); ubuffree ((char *) qtrans->s.ztemp); qtrans->s.ztemp = zbufcpy (abtname); qtrans->psendfn = flocal_send_request; qtrans->precfn = NULL; qtrans->ipos = 0; qtrans->cbytes = 0; qtrans->isecs = 0; qtrans->imicros = 0; qinfo->fsent = FALSE; ubuffree (qinfo->zconfirm); qinfo->zconfirm = NULL; return fqueue_send (qdaemon, qtrans); } /* Add a line to the fake execution file. */ static void usadd_exec_line (pz, pcalc, pclen, bcmd, z1, z2, fquote) char **pz; size_t *pcalc; size_t *pclen; int bcmd; const char *z1; const char *z2; boolean fquote; { char *z1q; char *z2q; size_t c1, c2; char *znew; z1q = NULL; z2q = NULL; if (fquote) { if (*z1 != '\0') { z1q = zquote_cmd_string (z1, FALSE); z1 = z1q; } if (*z2 != '\0') { z2q = zquote_cmd_string (z2, FALSE); z2 = z2q; } } c1 = strlen (z1); c2 = strlen (z2); if (*pclen + c1 + c2 + 4 >= *pcalc) { *pcalc += c1 + c2 + 100; znew = zbufalc (*pcalc); if (*pclen > 0) { memcpy (znew, *pz, *pclen); ubuffree (*pz); } *pz = znew; } znew = *pz + *pclen; *znew++ = bcmd; if (*z1 != '\0') { *znew++ = ' '; memcpy (znew, z1, c1); znew += c1; if (*z2 != '\0') { *znew++ = ' '; memcpy (znew, z2, c2); znew += c2; } } if (fquote) { ubuffree (z1q); ubuffree (z2q); } /* In some bizarre non-Unix case we might have to worry about the newline here. We don't know how a newline is normally written out to a file, but whatever is written to a file is what we will normally transfer. If that is not simply \n then this fake execution file will not look like other execution files. */ *znew++ = '\n'; *pclen = znew - *pz; } /* This routine is called to send the contents of the fake execution file. Normally file data is sent by the floop routine in trans.c, but since we don't have an actual file we must do it here. This routine sends the complete buffer, followed by a zero length packet, and then calls fsend_file_end. */ static boolean fsend_exec_file (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo; char *zdata; size_t cdata; size_t csend; zdata = (*qdaemon->qproto->pzgetspace) (qdaemon, &cdata); if (zdata == NULL) { usfree_send (qtrans); return FALSE; } csend = qinfo->cbytes - qtrans->ipos; if (csend > cdata) csend = cdata; memcpy (zdata, qinfo->zexec + qtrans->ipos, csend); if (! (*qdaemon->qproto->pfsenddata) (qdaemon, zdata, csend, qtrans->ilocal, qtrans->iremote, qtrans->ipos)) { usfree_send (qtrans); return FALSE; } qtrans->cbytes += csend; qtrans->ipos += csend; if (csend == 0) return fsend_file_end (qtrans, qdaemon); /* Leave the job on the send queue. */ return TRUE; } uucp-1.07/rec.c0000664000076400007640000011164507665321756007056 /* rec.c Routines to receive a file. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char rec_rcsid[] = "$Id: rec.c,v 1.48 2002/03/05 19:10:41 ian Rel $"; #endif #include #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "prot.h" #include "trans.h" /* If the other side does not tell us the size of a file it wants to send us, we assume it is this long. This is only used for free space checking. */ #define CASSUMED_FILE_SIZE (10240) /* We keep this information in the pinfo field of the stransfer structure. */ struct srecinfo { /* Local user to send mail to (may be NULL). */ char *zmail; /* Full file name. */ char *zfile; /* Temporary file name. */ char *ztemp; /* TRUE if this is a spool directory file. */ boolean fspool; /* TRUE if this was a local request. */ boolean flocal; /* TRUE if the file has been completely received. */ boolean freceived; /* TRUE if remote request has been replied to. */ boolean freplied; /* TRUE if we moved the file to the final destination. */ boolean fmoved; }; /* This structure is kept in the pinfo field if we are refusing a remote request. */ struct srecfailinfo { /* Reason for refusal. */ enum tfailure twhy; /* TRUE if we have sent the reason for refusal. */ boolean fsent; /* TRUE if we have seen the end of the file. */ boolean freceived; }; /* Local functions. */ static void urrec_free P((struct stransfer *qtrans)); static boolean flocal_rec_fail P((struct stransfer *qtrans, struct scmd *qcmd, const struct uuconf_system *qsys, const char *zwhy)); static boolean flocal_rec_send_request P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static boolean flocal_rec_await_reply P((struct stransfer *qtrans, struct sdaemon *qdaemon, const char *zdata, size_t cdata)); static boolean fremote_send_reply P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static boolean fremote_send_fail P((struct sdaemon *qdaemon, struct scmd *qcmd, enum tfailure twhy, int iremote)); static boolean fremote_send_fail_send P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static boolean fremote_discard P((struct stransfer *qtrans, struct sdaemon *qdaemon, const char *zdata, size_t cdata)); static boolean frec_file_end P((struct stransfer *qtrans, struct sdaemon *qdaemon, const char *zdata, size_t cdata)); static boolean frec_file_send_confirm P((struct stransfer *qtrans, struct sdaemon *qdaemon)); /* Free up a receive stransfer structure. */ static void urrec_free (qtrans) struct stransfer *qtrans; { struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; if (qinfo != NULL) { ubuffree (qinfo->zmail); ubuffree (qinfo->zfile); ubuffree (qinfo->ztemp); xfree (qtrans->pinfo); } utransfree (qtrans); } /* Set up a request for a file from the remote system. This may be called before the remote system has been called. This is the order of function calls: flocal_rec_file_init --> fqueue_local flocal_rec_send_request (send R ...) --> fqueue_receive flocal_rec_await_reply (open file, call pffile) --> fqueue_receive receive file frec_file_end (close and move file, call pffile) --> fqueue_send frec_file_send_confirm (send CY) */ boolean flocal_rec_file_init (qdaemon, qcmd) struct sdaemon *qdaemon; struct scmd *qcmd; { const struct uuconf_system *qsys; boolean fspool; char *zfile; struct srecinfo *qinfo; struct stransfer *qtrans; qsys = qdaemon->qsys; /* Make sure we are permitted to transfer files. */ if (qdaemon->fcaller ? ! qsys->uuconf_fcall_transfer : ! qsys->uuconf_fcalled_transfer) { /* This case will have been checked by uucp or uux, but it could have changed. */ if (! qsys->uuconf_fcall_transfer && ! qsys->uuconf_fcalled_transfer) return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys, "not permitted to request files"); return TRUE; } fspool = fspool_file (qcmd->zto); if (fspool) { pointer puuconf; int iuuconf; const char *zlocalname; struct uuconf_system slocalsys; /* Normal users are not allowed to request files to be received into the spool directory. To support uux forwarding, we use the special option '9'. This permits a file to be received into the spool directory for the local system only without the usual checking. This is only done for local requests, of course. */ if (qcmd->zto[0] != 'D' || strchr (qcmd->zoptions, '9') == NULL) return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys, "not permitted to receive"); puuconf = qdaemon->puuconf; iuuconf = uuconf_localname (puuconf, &zlocalname); if (iuuconf == UUCONF_NOT_FOUND) { zlocalname = zsysdep_localname (); if (zlocalname == NULL) return FALSE; } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } iuuconf = uuconf_system_info (puuconf, zlocalname, &slocalsys); if (iuuconf == UUCONF_NOT_FOUND) { iuuconf = uuconf_system_local (puuconf, &slocalsys); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } slocalsys.uuconf_zname = (char *) zlocalname; } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } zfile = zsysdep_spool_file_name (&slocalsys, qcmd->zto, qcmd->pseq); (void) uuconf_system_free (puuconf, &slocalsys); if (zfile == NULL) return FALSE; } else { zfile = zsysdep_add_base (qcmd->zto, qcmd->zfrom); if (zfile == NULL) return FALSE; /* Check permissions. */ if (! fin_directory_list (zfile, qsys->uuconf_pzlocal_receive, qsys->uuconf_zpubdir, TRUE, FALSE, qcmd->zuser)) { ubuffree (zfile); return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys, "not permitted to receive"); } /* The 'f' option means that directories should not be created if they do not already exist. */ if (strchr (qcmd->zoptions, 'f') == NULL) { if (! fsysdep_make_dirs (zfile, TRUE)) { ubuffree (zfile); return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys, "cannot create directories"); } } } qinfo = (struct srecinfo *) xmalloc (sizeof (struct srecinfo)); if (strchr (qcmd->zoptions, 'm') == NULL) qinfo->zmail = NULL; else qinfo->zmail = zbufcpy (qcmd->zuser); qinfo->zfile = zfile; qinfo->ztemp = NULL; qinfo->fspool = fspool; qinfo->flocal = TRUE; qinfo->freceived = FALSE; qinfo->freplied = TRUE; qtrans = qtransalc (qcmd); qtrans->psendfn = flocal_rec_send_request; qtrans->pinfo = (pointer) qinfo; return fqueue_local (qdaemon, qtrans); } /* Report an error for a local receive request. */ static boolean flocal_rec_fail (qtrans, qcmd, qsys, zwhy) struct stransfer *qtrans; struct scmd *qcmd; const struct uuconf_system *qsys; const char *zwhy; { if (zwhy != NULL) { ulog (LOG_ERROR, "%s: %s", qcmd->zfrom, zwhy); (void) fmail_transfer (FALSE, qcmd->zuser, (const char *) NULL, zwhy, qcmd->zfrom, qsys->uuconf_zname, qcmd->zto, (const char *) NULL, (const char *) NULL); (void) fsysdep_did_work (qcmd->pseq); } if (qtrans != NULL) urrec_free (qtrans); return TRUE; } /* This is called when we are ready to send the actual request to the other system. */ static boolean flocal_rec_send_request (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; long cbytes, cbytes2; boolean fquote; const struct scmd *qcmd; struct scmd squoted; size_t clen; char *zsend; boolean fret; qinfo->ztemp = zsysdep_receive_temp (qdaemon->qsys, qinfo->zfile, (const char *) NULL, (qdaemon->qproto->frestart && (qdaemon->ifeatures & FEATURE_RESTART) != 0)); if (qinfo->ztemp == NULL) { urrec_free (qtrans); return FALSE; } qtrans->fcmd = TRUE; qtrans->precfn = flocal_rec_await_reply; if (! fqueue_receive (qdaemon, qtrans)) return FALSE; /* Check the amount of free space available for both the temporary file and the real file. */ cbytes = csysdep_bytes_free (qinfo->ztemp); cbytes2 = csysdep_bytes_free (qinfo->zfile); if (cbytes < cbytes2) cbytes = cbytes2; if (cbytes != -1) { cbytes -= qdaemon->qsys->uuconf_cfree_space; if (cbytes < 0) cbytes = 0; } if (qdaemon->clocal_size != -1 && (cbytes == -1 || qdaemon->clocal_size < cbytes)) cbytes = qdaemon->clocal_size; fquote = fcmd_needs_quotes (&qtrans->s); if (! fquote) qcmd = &qtrans->s; else { if ((qdaemon->ifeatures & FEATURE_QUOTES) == 0) return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, "remote system does not support required quoting"); uquote_cmd (&qtrans->s, &squoted); qcmd = &squoted; } /* We send the string R from to user options We put a dash in front of options. If we are talking to a counterpart, we also send the maximum size file we are prepared to accept, as returned by esysdep_open_receive. */ clen = (strlen (qcmd->zfrom) + strlen (qcmd->zto) + strlen (qcmd->zuser) + strlen (qcmd->zoptions) + 30); zsend = zbufalc (clen); if ((qdaemon->ifeatures & FEATURE_SIZES) == 0) sprintf (zsend, "R %s %s %s -%s", qcmd->zfrom, qcmd->zto, qcmd->zuser, qcmd->zoptions); else if ((qdaemon->ifeatures & FEATURE_V103) == 0) sprintf (zsend, "R %s %s %s -%s 0x%lx", qcmd->zfrom, qcmd->zto, qcmd->zuser, qcmd->zoptions, (unsigned long) cbytes); else sprintf (zsend, "R %s %s %s -%s %ld", qcmd->zfrom, qcmd->zto, qcmd->zuser, qcmd->zoptions, cbytes); fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, qtrans->ilocal, qtrans->iremote); ubuffree (zsend); if (fquote) ufree_quoted_cmd (&squoted); /* There is a potential space leak here: if pfsendcmd fails, we might need to free qtrans. However, it is possible that by the time pfsendcmd returns, a response will have been received which led to the freeing of qtrans anyhow. One way to fix this would be some sort of counter in qtrans to track allocations, but since the space leak is small, and the conversation has failed anyhow, it doesn't seem worth it. */ return fret; } /* This is called when a reply is received for the request. */ /*ARGSUSED*/ static boolean flocal_rec_await_reply (qtrans, qdaemon, zdata, cdata) struct stransfer *qtrans; struct sdaemon *qdaemon; const char *zdata; size_t cdata ATTRIBUTE_UNUSED; { struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; const char *zlog; char *zend; if (zdata[0] != 'R' || (zdata[1] != 'Y' && zdata[1] != 'N')) { ulog (LOG_ERROR, "%s: bad response to receive request: \"%s\"", qtrans->s.zfrom, zdata); urrec_free (qtrans); return FALSE; } if (zdata[1] == 'N') { boolean fnever; const char *zerr; fnever = TRUE; if (zdata[2] == '2') zerr = "no such file"; else if (zdata[2] == '6') { /* We sent over the maximum file size we were prepared to receive, and the remote system is telling us that the file is larger than that. Try again later. It would be better if we could know whether there will ever be enough room. */ zerr = "too large to receive now"; fnever = FALSE; } else if (zdata[2] == '9') { /* Remote has run out of channels. */ zerr = "too many channels for remote"; fnever = FALSE; /* Drop one channel; using exactly one channel causes slightly different behahaviour in a few places, so don't decrement to one. */ if (qdaemon->cchans > 2) --qdaemon->cchans; } else zerr = "unknown reason"; if (fnever) return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, zerr); ulog (LOG_ERROR, "%s: %s", qtrans->s.zfrom, zerr); urrec_free (qtrans); return TRUE; } /* The mode should have been sent as "RY 0%o". If it wasn't, we use 0666. */ qtrans->s.imode = (unsigned int) strtol ((char *) (zdata + 2), &zend, 8); if (qtrans->s.imode == 0) qtrans->s.imode = 0666; /* If there is an M after the mode, the remote has requested a hangup. */ if (*zend == 'M' && qdaemon->fmaster) { DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "flocal_rec_await_reply: Remote has requested transfer of control"); qdaemon->fhangup_requested = TRUE; } /* Open the file to receive into. We just ignore any restart count, since we have no way to tell it to the other side. SVR4 may have some way to do this, but I don't know what it is. */ qtrans->e = esysdep_open_receive (qdaemon->qsys, qinfo->zfile, (const char *) NULL, qinfo->ztemp, (long *) NULL); if (! ffileisopen (qtrans->e)) return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, "cannot open file"); if (qinfo->fspool) zlog = qtrans->s.zto; else zlog = qinfo->zfile; qtrans->zlog = zbufalc (sizeof "Receiving " + strlen (zlog)); sprintf (qtrans->zlog, "Receiving %s", zlog); if (qdaemon->qproto->pffile != NULL) { boolean fhandled; if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, FALSE, (long) -1, &fhandled)) return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, (const char *) NULL); if (fhandled) return TRUE; } qtrans->frecfile = TRUE; qtrans->psendfn = frec_file_send_confirm; qtrans->precfn = frec_file_end; return fqueue_receive (qdaemon, qtrans); } /* Make sure there is still enough disk space available to receive a file. */ boolean frec_check_free (qtrans, cfree_space) struct stransfer *qtrans; long cfree_space; { struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; long cfree1, cfree2; cfree1 = csysdep_bytes_free (qinfo->ztemp); cfree2 = csysdep_bytes_free (qinfo->zfile); if (cfree1 < cfree2) cfree1 = cfree2; if (cfree1 != -1 && cfree1 < cfree_space) { ulog (LOG_ERROR, "%s: too big to receive now", qinfo->zfile); return FALSE; } return TRUE; } /* A remote request to send a file to the local system, meaning that we are going to receive a file. If we are using a protocol which does not support multiple channels, the remote system will not start sending us the file until it has received our confirmation. In that case, the order of functions is as follows: fremote_send_file_init (open file) --> fqueue_remote fremote_send_reply (send SY, call pffile) --> fqueue_receive receive file frec_file_end (close and move file, call pffile) --> fqueue_send frec_file_send_confirm (send CY) If the protocol supports multiple channels, then the remote system will start sending the file immediately after the send request. That means that the data may come in before remote_send_reply is called, so frec_file_end may be called before fremote_send_reply. Note that this means the pffile entry points may be called in reverse order for such a protocol. If the send request is rejected, via fremote_send_fail, and the protocol supports multiple channels, we must accept and discard data until a zero byte buffer is received from the other side, indicating that it has received our rejection. This code also handles execution requests, which are very similar to send requests. */ boolean fremote_send_file_init (qdaemon, qcmd, iremote) struct sdaemon *qdaemon; struct scmd *qcmd; int iremote; { const struct uuconf_system *qsys; boolean fspool; char *zfile; openfile_t e; char *ztemp; long cbytes, cbytes2; long crestart; struct srecinfo *qinfo; struct stransfer *qtrans; const char *zlog; qsys = qdaemon->qsys; if (! qsys->uuconf_frec_request) { ulog (LOG_ERROR, "%s: not permitted to receive files from remote", qcmd->zfrom); return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote); } fspool = fspool_file (qcmd->zto); /* We don't accept remote command files. An execution request may only send a simple data file. */ if ((fspool && qcmd->zto[0] == 'C') || (qcmd->bcmd == 'E' && (! fspool || qcmd->zto[0] != 'D'))) { ulog (LOG_ERROR, "%s: not permitted to receive", qcmd->zfrom); return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote); } /* See if we have already received this file in a previous conversation. */ if (fsysdep_already_received (qsys, qcmd->zto, qcmd->ztemp)) return fremote_send_fail (qdaemon, qcmd, FAILURE_RECEIVED, iremote); if (fspool) { zfile = zsysdep_spool_file_name (qsys, qcmd->zto, (pointer) NULL); if (zfile == NULL) return FALSE; } else { boolean fbadname; zfile = zsysdep_local_file (qcmd->zto, qsys->uuconf_zpubdir, &fbadname); if (zfile == NULL && fbadname) { ulog (LOG_ERROR, "%s: bad local file name", qcmd->zto); return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote); } if (zfile != NULL) { char *zadd; zadd = zsysdep_add_base (zfile, qcmd->zfrom); ubuffree (zfile); zfile = zadd; } if (zfile == NULL) return FALSE; /* Check permissions. */ if (! fin_directory_list (zfile, qsys->uuconf_pzremote_receive, qsys->uuconf_zpubdir, TRUE, FALSE, (const char *) NULL)) { ulog (LOG_ERROR, "%s: not permitted to receive", zfile); ubuffree (zfile); return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote); } if (strchr (qcmd->zoptions, 'f') == NULL) { if (! fsysdep_make_dirs (zfile, TRUE)) { ubuffree (zfile); return fremote_send_fail (qdaemon, qcmd, FAILURE_OPEN, iremote); } } } ztemp = zsysdep_receive_temp (qsys, zfile, qcmd->ztemp, (qdaemon->qproto->frestart && (qdaemon->ifeatures & FEATURE_RESTART) != 0)); /* Adjust the number of bytes we are prepared to receive according to the amount of free space we are supposed to leave available and the maximum file size we are permitted to transfer. */ cbytes = csysdep_bytes_free (ztemp); cbytes2 = csysdep_bytes_free (zfile); if (cbytes < cbytes2) cbytes = cbytes2; if (cbytes != -1) { cbytes -= qsys->uuconf_cfree_space; if (cbytes < 0) cbytes = 0; } if (qdaemon->cremote_size != -1 && (cbytes == -1 || qdaemon->cremote_size < cbytes)) cbytes = qdaemon->cremote_size; /* If the number of bytes we are prepared to receive is less than the file size, we must fail. If the remote did not tell us the file size, arbitrarily assumed that it is 10240 bytes. */ if (cbytes != -1) { long csize; csize = qcmd->cbytes; if (csize == -1) csize = CASSUMED_FILE_SIZE; if (cbytes < csize) { ulog (LOG_ERROR, "%s: too big to receive", zfile); ubuffree (ztemp); ubuffree (zfile); return fremote_send_fail (qdaemon, qcmd, FAILURE_SIZE, iremote); } } /* Open the file to receive into. This may find an old copy of the file, which will be used for file restart if the other side supports it. */ crestart = -1; e = esysdep_open_receive (qsys, zfile, qcmd->ztemp, ztemp, ((qdaemon->qproto->frestart && (qdaemon->ifeatures & FEATURE_RESTART) != 0) ? &crestart : (long *) NULL)); if (! ffileisopen (e)) { ubuffree (ztemp); ubuffree (zfile); return fremote_send_fail (qdaemon, qcmd, FAILURE_OPEN, iremote); } if (crestart > 0) { DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fremote_send_file_init: Restarting receive from %ld", crestart); if (! ffileseek (e, crestart)) { ulog (LOG_ERROR, "seek: %s", strerror (errno)); (void) ffileclose (e); ubuffree (ztemp); ubuffree (zfile); return FALSE; } } qinfo = (struct srecinfo *) xmalloc (sizeof (struct srecinfo)); if (strchr (qcmd->zoptions, 'n') == NULL) qinfo->zmail = NULL; else qinfo->zmail = zbufcpy (qcmd->znotify); qinfo->zfile = zfile; qinfo->ztemp = ztemp; qinfo->fspool = fspool; qinfo->flocal = FALSE; qinfo->freceived = FALSE; qinfo->freplied = FALSE; qtrans = qtransalc (qcmd); qtrans->psendfn = fremote_send_reply; qtrans->precfn = frec_file_end; qtrans->iremote = iremote; qtrans->pinfo = (pointer) qinfo; qtrans->frecfile = TRUE; qtrans->e = e; if (crestart > 0) qtrans->ipos = crestart; if (qcmd->bcmd == 'E') zlog = qcmd->zcmd; else { if (qinfo->fspool) zlog = qcmd->zto; else zlog = qinfo->zfile; } qtrans->zlog = zbufalc (sizeof "Receiving ( bytes resume at )" + strlen (zlog) + 50); sprintf (qtrans->zlog, "Receiving %s", zlog); if (crestart > 0 || qcmd->cbytes > 0) { strcat (qtrans->zlog, " ("); if (qcmd->cbytes > 0) { sprintf (qtrans->zlog + strlen (qtrans->zlog), "%ld bytes", qcmd->cbytes); if (crestart > 0) strcat (qtrans->zlog, " "); } if (crestart > 0) sprintf (qtrans->zlog + strlen (qtrans->zlog), "resume at %ld", crestart); strcat (qtrans->zlog, ")"); } return fqueue_remote (qdaemon, qtrans); } /* Reply to a send request, and prepare to receive the file. */ static boolean fremote_send_reply (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; boolean fret; char ab[50]; /* If the file has been completely received, we just want to send the final confirmation. Otherwise, we must wait for the file first. */ qtrans->psendfn = frec_file_send_confirm; if (qinfo->freceived) fret = fqueue_send (qdaemon, qtrans); else fret = fqueue_receive (qdaemon, qtrans); if (! fret) return FALSE; ab[0] = qtrans->s.bcmd; ab[1] = 'Y'; if (qtrans->ipos <= 0) ab[2] = '\0'; else sprintf (ab + 2, " 0x%lx", (unsigned long) qtrans->ipos); qinfo->freplied = TRUE; if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, ab, qtrans->ilocal, qtrans->iremote)) { (void) ffileclose (qtrans->e); qtrans->e = EFILECLOSED; (void) remove (qinfo->ztemp); /* Should probably free qtrans here, but see the comment at the end of flocal_rec_send_request. */ return FALSE; } if (qdaemon->qproto->pffile != NULL) { boolean fhandled; if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, FALSE, (long) -1, &fhandled)) { (void) remove (qinfo->ztemp); urrec_free (qtrans); return FALSE; } } return TRUE; } /* If we can't receive a file, queue up a response to the remote system. */ static boolean fremote_send_fail (qdaemon, qcmd, twhy, iremote) struct sdaemon *qdaemon; struct scmd *qcmd; enum tfailure twhy; int iremote; { struct srecfailinfo *qinfo; struct stransfer *qtrans; qinfo = (struct srecfailinfo *) xmalloc (sizeof (struct srecfailinfo)); qinfo->twhy = twhy; qinfo->fsent = FALSE; /* If the protocol does not support multiple channels (cchans <= 1), then we have essentially already received the entire file. */ qinfo->freceived = qdaemon->cchans <= 1; qtrans = qtransalc (qcmd); qtrans->psendfn = fremote_send_fail_send; qtrans->precfn = fremote_discard; qtrans->iremote = iremote; qtrans->pinfo = (pointer) qinfo; return fqueue_remote (qdaemon, qtrans); } /* Send a failure string for a send command to the remote system; this is called when we are ready to reply to the command. */ static boolean fremote_send_fail_send (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct srecfailinfo *qinfo = (struct srecfailinfo *) qtrans->pinfo; char ab[4]; int ilocal, iremote; ab[0] = qtrans->s.bcmd; ab[1] = 'N'; switch (qinfo->twhy) { case FAILURE_PERM: ab[2] = '2'; break; case FAILURE_OPEN: ab[2] = '4'; break; case FAILURE_SIZE: ab[2] = '6'; break; case FAILURE_RECEIVED: /* Remember this file as though we successfully received it; when the other side acknowledges our rejection, we know that we no longer have to remember that we received this file. */ usent_receive_ack (qdaemon, qtrans); ab[2] = '8'; break; default: ab[2] = '\0'; break; } ab[3] = '\0'; ilocal = qtrans->ilocal; iremote = qtrans->iremote; /* Wait for the end of file marker if we haven't gotten it yet. */ if (! qinfo->freceived) { qinfo->fsent = TRUE; if (! fqueue_receive (qdaemon, qtrans)) return FALSE; } else { xfree (qtrans->pinfo); utransfree (qtrans); } return (*qdaemon->qproto->pfsendcmd) (qdaemon, ab, ilocal, iremote); } /* Discard data until we reach the end of the file. This is used for a protocol with multiple channels, since the remote system may start sending the file before the confirmation is sent. If we refuse the file, the remote system will get us back in synch by sending an empty buffer, which is what we look for here. */ /*ARGSUSED*/ static boolean fremote_discard (qtrans, qdaemon, zdata, cdata) struct stransfer *qtrans; struct sdaemon *qdaemon ATTRIBUTE_UNUSED; const char *zdata ATTRIBUTE_UNUSED; size_t cdata; { struct srecfailinfo *qinfo = (struct srecfailinfo *) qtrans->pinfo; DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fremote_discard: Discarding %lu bytes", (unsigned long) cdata); if (cdata != 0) return TRUE; qinfo->freceived = TRUE; /* If we have already sent the denial, we are done. */ if (qinfo->fsent) { xfree (qtrans->pinfo); utransfree (qtrans); } return TRUE; } /* This is called when a file has been completely received. It sends a response to the remote system. */ /*ARGSUSED*/ static boolean frec_file_end (qtrans, qdaemon, zdata, cdata) struct stransfer *qtrans; struct sdaemon *qdaemon; const char *zdata ATTRIBUTE_UNUSED; size_t cdata ATTRIBUTE_UNUSED; { struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; char *zalc; const char *zerr; boolean fnever; DEBUG_MESSAGE3 (DEBUG_UUCP_PROTO, "frec_file_end: %s to %s (freplied %s)", qtrans->s.zfrom, qtrans->s.zto, qinfo->freplied ? "TRUE" : "FALSE"); if (qdaemon->qproto->pffile != NULL) { boolean fhandled; if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, FALSE, FALSE, (long) -1, &fhandled)) { (void) remove (qinfo->ztemp); urrec_free (qtrans); return FALSE; } if (fhandled) return TRUE; } qinfo->freceived = TRUE; fnever = FALSE; zalc = NULL; if (! fsysdep_sync (qtrans->e, qtrans->s.zto)) { zerr = strerror (errno); (void) ffileclose (qtrans->e); qtrans->e = EFILECLOSED; (void) remove (qinfo->ztemp); } else if (! ffileclose (qtrans->e)) { zerr = strerror (errno); ulog (LOG_ERROR, "%s: close: %s", qtrans->s.zto, zerr); (void) remove (qinfo->ztemp); qtrans->e = EFILECLOSED; } else { qtrans->e = EFILECLOSED; if (! fsysdep_move_file (qinfo->ztemp, qinfo->zfile, qinfo->fspool, FALSE, ! qinfo->fspool, (qinfo->flocal ? qtrans->s.zuser : (const char *) NULL))) { long cspace; /* Keep the temporary file if there is 1.5 times the amount of required free space. This is just a random guess, to make an unusual situtation potentially less painful. */ cspace = csysdep_bytes_free (qinfo->ztemp); if (cspace == -1) cspace = FREE_SPACE_DELTA; cspace -= (qdaemon->qsys->uuconf_cfree_space + qdaemon->qsys->uuconf_cfree_space / 2); if (cspace < 0) { (void) remove (qinfo->ztemp); zerr = "could not move to final location"; } else { const char *az[20]; int i; zalc = zbufalc (sizeof "could not move to final location (left as )" + strlen (qinfo->ztemp)); sprintf (zalc, "could not move to final location (left as %s)", qinfo->ztemp); zerr = zalc; i = 0; az[i++] = "The file\n\t"; az[i++] = qinfo->ztemp; az[i++] = "\nwas saved because the move to the final location failed.\n"; az[i++] = "See the UUCP logs for more details.\n"; az[i++] = "The file transfer was from\n\t"; az[i++] = qdaemon->qsys->uuconf_zname; az[i++] = "!"; az[i++] = qtrans->s.zfrom; az[i++] = "\nto\n\t"; az[i++] = qtrans->s.zto; az[i++] = "\nand was requested by\n\t"; az[i++] = qtrans->s.zuser; az[i++] = "\n"; (void) fsysdep_mail (OWNER, "UUCP temporary file saved", i, az); } ulog (LOG_ERROR, "%s: %s", qinfo->zfile, zerr); fnever = TRUE; } else { if (! qinfo->fspool) { unsigned int imode; /* Unless we can change the ownership of the file, the only choice to make about these bits is whether to set the execute bit or not. */ if ((qtrans->s.imode & 0111) != 0) imode = 0777; else imode = 0666; (void) fsysdep_change_mode (qinfo->zfile, imode); } zerr = NULL; } } ustats (zerr == NULL, qtrans->s.zuser, qdaemon->qsys->uuconf_zname, FALSE, qtrans->cbytes, qtrans->isecs, qtrans->imicros, qdaemon->fcaller); qdaemon->creceived += qtrans->cbytes; if (zerr == NULL) { if (qinfo->zmail != NULL && *qinfo->zmail != '\0') (void) fmail_transfer (TRUE, qtrans->s.zuser, qinfo->zmail, (const char *) NULL, qtrans->s.zfrom, qdaemon->qsys->uuconf_zname, qtrans->s.zto, (const char *) NULL, (const char *) NULL); if (qtrans->s.pseq != NULL) (void) fsysdep_did_work (qtrans->s.pseq); if (! qinfo->flocal) { /* Remember that we have received this file, so that if the connection drops at this point we won't receive it again. We could check the return value here, but if we return FALSE we couldn't do anything but drop the connection, which would hardly be reasonable. Instead we trust that the administrator will notice and handle any error messages, which are very unlikely to occur if everything is set up correctly. */ (void) fsysdep_remember_reception (qdaemon->qsys, qtrans->s.zto, qtrans->s.ztemp); } } else { /* If the transfer failed, we send mail if it was requested locally and if it can never succeed. */ if (qinfo->flocal && fnever) { (void) fmail_transfer (FALSE, qtrans->s.zuser, qinfo->zmail, zerr, qtrans->s.zfrom, qdaemon->qsys->uuconf_zname, qtrans->s.zto, (const char *) NULL, (const char *) NULL); (void) fsysdep_did_work (qtrans->s.pseq); } } ubuffree (zalc); /* If this is an execution request, we must create the execution file itself. */ if (qtrans->s.bcmd == 'E' && zerr == NULL) { char *zxqt, *zxqtfile, *ztemp; FILE *e; boolean fbad; /* We get an execution file name by simply replacing the leading D in the received file name with an X. This pretty much always has to work since we can always receive a file name starting with X, so the system dependent code must be prepared to see one. */ zxqt = zbufcpy (qtrans->s.zto); zxqt[0] = 'X'; zxqtfile = zsysdep_spool_file_name (qdaemon->qsys, zxqt, (pointer) NULL); ubuffree (zxqt); if (zxqtfile == NULL) { urrec_free (qtrans); return FALSE; } /* We have to write via a temporary file, because otherwise uuxqt might pick up the file before we have finished writing it. */ e = NULL; ztemp = zsysdep_receive_temp (qdaemon->qsys, zxqtfile, "D.0", (qdaemon->qproto->frestart && (qdaemon->ifeatures & FEATURE_RESTART) != 0)); if (ztemp != NULL) e = esysdep_fopen (ztemp, FALSE, FALSE, TRUE); if (e == NULL) { ubuffree (zxqtfile); ubuffree (ztemp); urrec_free (qtrans); return FALSE; } if (! fcmd_needs_quotes (&qtrans->s)) { fprintf (e, "U %s %s\n", qtrans->s.zuser, qdaemon->qsys->uuconf_zname); fprintf (e, "F %s\n", qtrans->s.zto); fprintf (e, "I %s\n", qtrans->s.zto); if (strchr (qtrans->s.zoptions, 'R') != NULL) fprintf (e, "R %s\n", qtrans->s.znotify); fprintf (e, "C %s\n", qtrans->s.zcmd); } else { char *z1; char *z2; fprintf (e, "Q\n"); z1 = zquote_cmd_string (qtrans->s.zuser, FALSE); z2 = zquote_cmd_string (qdaemon->qsys->uuconf_zname, FALSE); fprintf (e, "U %s %s\n", z1, z2); ubuffree (z1); ubuffree (z2); z1 = zquote_cmd_string (qtrans->s.zto, FALSE); fprintf (e, "F %s\n", z1); fprintf (e, "I %s\n", z1); ubuffree (z1); if (strchr (qtrans->s.zoptions, 'R') != NULL) { z1 = zquote_cmd_string (qtrans->s.znotify, FALSE); fprintf (e, "R %s\n", z1); ubuffree (z1); } z1 = zquote_cmd_string (qtrans->s.zcmd, TRUE); fprintf (e, "C %s\n", z1); ubuffree (z1); } if (strchr (qtrans->s.zoptions, 'N') != NULL) fprintf (e, "N\n"); if (strchr (qtrans->s.zoptions, 'Z') != NULL) fprintf (e, "Z\n"); if (strchr (qtrans->s.zoptions, 'e') != NULL) fprintf (e, "e\n"); fbad = FALSE; if (! fstdiosync (e, ztemp)) { (void) fclose (e); (void) remove (ztemp); fbad = TRUE; } if (! fbad) { if (fclose (e) == EOF) { ulog (LOG_ERROR, "fclose: %s", strerror (errno)); (void) remove (ztemp); fbad = TRUE; } } if (! fbad) { if (! fsysdep_move_file (ztemp, zxqtfile, TRUE, FALSE, FALSE, (const char *) NULL)) { (void) remove (ztemp); fbad = TRUE; } } ubuffree (zxqtfile); ubuffree (ztemp); if (fbad) { urrec_free (qtrans); return FALSE; } } /* See if we should spawn a uuxqt process. */ if (zerr == NULL && (qtrans->s.bcmd == 'E' || (qinfo->fspool && qtrans->s.zto[0] == 'X'))) { ++qdaemon->cxfiles_received; if (qdaemon->irunuuxqt > 0 && qdaemon->cxfiles_received >= qdaemon->irunuuxqt) { if (fspawn_uuxqt (TRUE, qdaemon->qsys->uuconf_zname, qdaemon->zconfig)) qdaemon->cxfiles_received = 0; } } /* Prepare to send the completion string to the remote system. If we have not yet replied to the remote send request, we leave the transfer structure on the remote queue. Otherwise we add it to the send queue. The psendfn field will already be set. */ qinfo->fmoved = zerr == NULL; if (qinfo->freplied) return fqueue_send (qdaemon, qtrans); return TRUE; } /* Send the final confirmation string to the remote system. */ static boolean frec_file_send_confirm (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; const char *zsend; int ilocal, iremote; if (! qinfo->fmoved) zsend = "CN5"; else if (! qdaemon->frequest_hangup) zsend = "CY"; else { #if DEBUG > 0 if (qdaemon->fmaster) ulog (LOG_FATAL, "frec_file_send_confirm: Can't happen"); #endif DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "frec_send_file_confirm: Requesting remote to transfer control"); zsend = "CYM"; } /* If that was a remote command, then, when the confirmation message is acked, we no longer have to remember that we received that file. */ if (! qinfo->flocal && qinfo->fmoved) usent_receive_ack (qdaemon, qtrans); ilocal = qtrans->ilocal; iremote = qtrans->iremote; urrec_free (qtrans); return (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, ilocal, iremote); } /* Discard a temporary file if it is not useful. A temporary file is useful if it could be used to restart a receive. This is called if the connection is lost. It is only called if qtrans->frecfile is TRUE. */ boolean frec_discard_temp (qdaemon, qtrans) struct sdaemon *qdaemon; struct stransfer *qtrans; { struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; if ((qdaemon->ifeatures & FEATURE_RESTART) == 0 || qtrans->s.ztemp == NULL || qtrans->s.ztemp[0] != 'D' || strcmp (qtrans->s.ztemp, "D.0") == 0) (void) remove (qinfo->ztemp); return TRUE; } uucp-1.07/xcmd.c0000664000076400007640000002505607665321760007233 /* xcmd.c Routines to handle work requests. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char xcmd_rcsid[] = "$Id: xcmd.c,v 1.24 2002/03/05 19:10:42 ian Rel $"; #endif #include #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "prot.h" #include "trans.h" /* Local functions. */ static boolean flocal_xcmd_request P((struct stransfer *qtrans, struct sdaemon *qdaemon)); static boolean flocal_xcmd_await_reply P((struct stransfer *qtrans, struct sdaemon *qdaemon, const char *zdata, size_t cdata)); static boolean fremote_xcmd_reply P((struct stransfer *qtrans, struct sdaemon *qdaemon)); /* Handle a local work request. We just set up the request for transmission. */ boolean flocal_xcmd_init (qdaemon, qcmd) struct sdaemon *qdaemon; struct scmd *qcmd; { struct stransfer *qtrans; qtrans = qtransalc (qcmd); qtrans->psendfn = flocal_xcmd_request; return fqueue_local (qdaemon, qtrans); } /* Send the execution request to the remote system. */ static boolean flocal_xcmd_request (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { boolean fquote; const struct scmd *qcmd; struct scmd squoted; size_t clen; char *zsend; boolean fret; ulog (LOG_NORMAL, "Requesting work: %s to %s", qtrans->s.zfrom, qtrans->s.zto); qtrans->fcmd = TRUE; qtrans->precfn = flocal_xcmd_await_reply; fquote = fcmd_needs_quotes (&qtrans->s); if (! fquote) qcmd = &qtrans->s; else { if ((qdaemon->ifeatures & FEATURE_QUOTES) == 0) { ulog (LOG_ERROR, "%s: remote system does not support required quoting", qtrans->s.zfrom); (void) fmail_transfer (FALSE, qtrans->s.zuser, (const char *) NULL, "remote system does not support required quoting", qtrans->s.zfrom, qdaemon->qsys->uuconf_zname, qtrans->s.zto, (const char *) NULL, (const char *) NULL); (void) fsysdep_did_work (qtrans->s.pseq); utransfree (qtrans); return TRUE; } uquote_cmd (&qtrans->s, &squoted); qcmd = &squoted; } if (! fqueue_receive (qdaemon, qtrans)) return FALSE; /* We send the string X from to user options We put a dash in front of options. */ clen = (strlen (qcmd->zfrom) + strlen (qcmd->zto) + strlen (qcmd->zuser) + strlen (qcmd->zoptions) + 7); zsend = zbufalc (clen); sprintf (zsend, "X %s %s %s -%s", qcmd->zfrom, qcmd->zto, qcmd->zuser, qcmd->zoptions); fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, qtrans->ilocal, qtrans->iremote); ubuffree (zsend); if (fquote) ufree_quoted_cmd (&squoted); /* If fret is FALSE, we should free qtrans here, but see the comment at the end of flocal_rec_send_request. */ return fret; } /* Get a reply to an execution request from the remote system. */ /*ARGSUSED*/ static boolean flocal_xcmd_await_reply (qtrans, qdaemon, zdata, cdata) struct stransfer *qtrans; struct sdaemon *qdaemon; const char *zdata; size_t cdata ATTRIBUTE_UNUSED; { qtrans->precfn = NULL; if (zdata[0] != 'X' || (zdata[1] != 'Y' && zdata[1] != 'N')) { ulog (LOG_ERROR, "Bad response to work request"); utransfree (qtrans); return FALSE; } if (zdata[1] == 'N') { ulog (LOG_ERROR, "%s: work request denied", qtrans->s.zfrom); (void) fmail_transfer (FALSE, qtrans->s.zuser, (const char *) NULL, "work request denied", qtrans->s.zfrom, qdaemon->qsys->uuconf_zname, qtrans->s.zto, (const char *) NULL, (const char *) NULL); } (void) fsysdep_did_work (qtrans->s.pseq); utransfree (qtrans); return TRUE; } /* Handle a remote work request. This just queues up the requests for later processing. */ boolean fremote_xcmd_init (qdaemon, qcmd, iremote) struct sdaemon *qdaemon; struct scmd *qcmd; int iremote; { const struct uuconf_system *qsys; const char *zexclam; const struct uuconf_system *qdestsys; struct uuconf_system sdestsys; char *zdestfile; boolean fmkdirs; struct stransfer *qtrans; char *zuser; char aboptions[5]; char *zfrom; boolean fret; char *zfile; ulog (LOG_NORMAL, "Work requested: %s to %s", qcmd->zfrom, qcmd->zto); qsys = qdaemon->qsys; zexclam = strchr (qcmd->zto, '!'); if (zexclam == NULL || zexclam == qcmd->zto || strncmp (qdaemon->zlocalname, qcmd->zto, (size_t) (zexclam - qcmd->zto)) == 0) { const char *zconst; /* The files are supposed to be copied to the local system. */ qdestsys = NULL; if (zexclam == NULL) zconst = qcmd->zto; else zconst = zexclam + 1; zdestfile = zsysdep_local_file (zconst, qsys->uuconf_zpubdir, (boolean *) NULL); if (zdestfile == NULL) return FALSE; zuser = NULL; fmkdirs = strchr (qcmd->zoptions, 'f') != NULL; } else { size_t clen; char *zcopy; int iuuconf; char *zoptions; clen = zexclam - qcmd->zto; zcopy = zbufalc (clen + 1); memcpy (zcopy, qcmd->zto, clen); zcopy[clen] = '\0'; iuuconf = uuconf_system_info (qdaemon->puuconf, zcopy, &sdestsys); if (iuuconf == UUCONF_NOT_FOUND) { if (! funknown_system (qdaemon->puuconf, zcopy, &sdestsys)) { ulog (LOG_ERROR, "%s: System not found", zcopy); ubuffree (zcopy); qtrans = qtransalc (qcmd); qtrans->psendfn = fremote_xcmd_reply; qtrans->pinfo = (pointer) "XN"; qtrans->iremote = iremote; return fqueue_remote (qdaemon, qtrans); } } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, qdaemon->puuconf, iuuconf); ubuffree (zcopy); return FALSE; } ubuffree (zcopy); qdestsys = &sdestsys; zdestfile = zbufcpy (zexclam + 1); zuser = zbufalc (strlen (qdestsys->uuconf_zname) + strlen (qcmd->zuser) + sizeof "!"); sprintf (zuser, "%s!%s", qdestsys->uuconf_zname, qcmd->zuser); zoptions = aboptions; *zoptions++ = 'C'; if (strchr (qcmd->zoptions, 'd') != NULL) *zoptions++ = 'd'; if (strchr (qcmd->zoptions, 'm') != NULL) *zoptions++ = 'm'; *zoptions = '\0'; fmkdirs = TRUE; } /* At this point we prepare to confirm the remote request. We could actually fork here and let the child spool up the requests. */ qtrans = qtransalc (qcmd); qtrans->psendfn = fremote_xcmd_reply; qtrans->pinfo = (pointer) "XY"; qtrans->iremote = iremote; if (! fqueue_remote (qdaemon, qtrans)) { ubuffree (zdestfile); ubuffree (zuser); return FALSE; } /* Now we have to process each source file. The source specification may or may use wildcards. */ zfrom = zsysdep_local_file (qcmd->zfrom, qsys->uuconf_zpubdir, (boolean *) NULL); if (zfrom == NULL) { ubuffree (zdestfile); ubuffree (zuser); return FALSE; } if (! fsysdep_wildcard_start (zfrom)) { ubuffree (zfrom); ubuffree (zdestfile); ubuffree (zuser); return FALSE; } fret = TRUE; while ((zfile = zsysdep_wildcard (zfrom)) != NULL) { char *zto; char abtname[CFILE_NAME_LEN]; if (! fsysdep_file_exists (zfile)) { ulog (LOG_ERROR, "%s: no such file", zfile); continue; } /* Make sure the remote system is permitted to read the specified file. */ if (! fin_directory_list (zfile, qsys->uuconf_pzremote_send, qsys->uuconf_zpubdir, TRUE, TRUE, (const char *) NULL)) { ulog (LOG_ERROR, "%s: not permitted to send", zfile); break; } if (qdestsys != NULL) { /* We really should get the original grade here. */ zto = zsysdep_data_file_name (qdestsys, qdaemon->zlocalname, BDEFAULT_UUCP_GRADE, FALSE, abtname, (char *) NULL, (char *) NULL); if (zto == NULL) { fret = FALSE; break; } } else { zto = zsysdep_add_base (zdestfile, zfile); if (zto == NULL) { fret = FALSE; break; } /* We only accept a local destination if the remote system has the right to create files there. */ if (! fin_directory_list (zto, qsys->uuconf_pzremote_receive, qsys->uuconf_zpubdir, TRUE, FALSE, (const char *) NULL)) { ulog (LOG_ERROR, "%s: not permitted to receive", zto); ubuffree (zto); break; } } /* Copy the file either to the final destination or to the spool directory. */ if (! fcopy_file (zfile, zto, qdestsys == NULL, fmkdirs, FALSE)) { ubuffree (zto); break; } ubuffree (zto); /* If there is a destination system, queue it up. */ if (qdestsys != NULL) { struct scmd ssend; char *zjobid; ssend.bcmd = 'S'; ssend.bgrade = BDEFAULT_UUCP_GRADE; ssend.pseq = NULL; ssend.zfrom = zfile; ssend.zto = zdestfile; ssend.zuser = zuser; ssend.zoptions = aboptions; ssend.ztemp = abtname; ssend.imode = ixsysdep_file_mode (zfile); ssend.znotify = ""; ssend.cbytes = -1; ssend.zcmd = NULL; ssend.ipos = 0; zjobid = zsysdep_spool_commands (qdestsys, BDEFAULT_UUCP_GRADE, 1, &ssend, (boolean *) NULL); if (zjobid == NULL) break; ubuffree (zjobid); } ubuffree (zfile); } if (zfile != NULL) ubuffree (zfile); (void) fsysdep_wildcard_end (); ubuffree (zdestfile); if (qdestsys != NULL) (void) uuconf_system_free (qdaemon->puuconf, &sdestsys); ubuffree (zfrom); ubuffree (zuser); return fret; } /* Reply to a remote work request. */ static boolean fremote_xcmd_reply (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { boolean fret; fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, (const char *) qtrans->pinfo, qtrans->ilocal, qtrans->iremote); utransfree (qtrans); return fret; } uucp-1.07/prot.h0000664000076400007640000002637707665321755007304 /* prot.h Protocol header file. Copyright (C) 1991, 1992, 1993, 1994, 1995 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ /* We need the definition of uuconf_cmdtab to declare the protocol parameter arrays. */ #ifndef UUCONF_H #include "uuconf.h" #endif #if ANSI_C /* These structures are used in prototypes but are not defined in this header file. */ struct sdaemon; struct sconnection; struct stransfer; #endif /* The sprotocol structure holds information and functions for a specific protocol (e.g. the 'g' protocol). */ struct sprotocol { /* The name of the protocol (e.g. 'g'). */ char bname; /* Reliability requirements, an or of UUCONF_RELIABLE_xxx defines from uuconf.h. */ int ireliable; /* The maximum number of channels this protocol can support. */ int cchans; /* Whether files may be reliably restarted using this protocol. */ boolean frestart; /* Protocol parameter commands. */ struct uuconf_cmdtab *qcmds; /* A routine to start the protocol. If *pzlog is set to be non-NULL, it is an informative message to be logged; it should then be passed to ubuffree. */ boolean (*pfstart) P((struct sdaemon *qdaemon, char **pzlog)); /* Shutdown the protocol. */ boolean (*pfshutdown) P((struct sdaemon *qdaemon)); /* Send a command to the other side. */ boolean (*pfsendcmd) P((struct sdaemon *qdaemon, const char *z, int ilocal, int iremote)); /* Get buffer to space to fill with data. This should set *pcdata to the amount of data desired. */ char *(*pzgetspace) P((struct sdaemon *qdaemon, size_t *pcdata)); /* Send data to the other side. The argument z must be a return value of pzgetspace. The ipos argument is the file position, and is ignored by most protocols. */ boolean (*pfsenddata) P((struct sdaemon *qdaemon, char *z, size_t c, int ilocal, int iremote, long ipos)); /* Wait for data to come in and call fgot_data with it until fgot_data sets *pfexit. */ boolean (*pfwait) P((struct sdaemon *qdaemon)); /* Handle any file level actions that need to be taken. If a file transfer is starting rather than ending, fstart is TRUE. If the file is being sent rather than received, fsend is TRUE. If fstart and fsend are both TRUE, cbytes holds the size of the file. If *pfhandled is set to TRUE, then the protocol routine has taken care of queueing up qtrans for the next action. */ boolean (*pffile) P((struct sdaemon *qdaemon, struct stransfer *qtrans, boolean fstart, boolean fsend, long cbytes, boolean *pfhandled)); }; /* Send data to the other system. If the fread argument is TRUE, this will also receive data into the receive buffer abPrecbuf; fread is passed as TRUE if the protocol expects data to be coming back, to make sure the input buffer does not fill up. Returns FALSE on error. */ extern boolean fsend_data P((struct sconnection *qconn, const char *zsend, size_t csend, boolean fdoread)); /* Receive data from the other system when there is no data to send. The cneed argument is the amount of data desired and the ctimeout argument is the timeout in seconds. This will set *pcrec to the amount of data received. It will return FALSE on error. If a timeout occurs, it will return TRUE with *pcrec set to zero. */ extern boolean freceive_data P((struct sconnection *qconn, size_t cneed, size_t *pcrec, int ctimeout, boolean freport)); /* Get one character from the remote system, going through the procotol buffering. The ctimeout argument is the timeout in seconds, and the freport argument is TRUE if errors should be reported (when closing a connection it is pointless to report errors). This returns a character or -1 on a timeout or -2 on an error. */ extern int breceive_char P((struct sconnection *qconn, int ctimeout, boolean freport)); /* Compute a 32 bit CRC of a data buffer, given an initial CRC. */ extern unsigned long icrc P((const char *z, size_t c, unsigned long ick)); /* The initial CRC value to use for a new buffer. */ #if ANSI_C #define ICRCINIT (0xffffffffUL) #else #define ICRCINIT ((unsigned long) 0xffffffffL) #endif /* The size of the receive buffer. */ #define CRECBUFLEN (16384) /* Buffer to hold received data. */ extern char abPrecbuf[CRECBUFLEN]; /* Index of start of data in abPrecbuf. */ extern int iPrecstart; /* Index of end of data (first byte not included in data) in abPrecbuf. */ extern int iPrecend; /* There are a couple of variables and functions that are shared by the 'i' and 'j' protocols (the 'j' protocol is just a wrapper around the 'i' protocol). These belong in a separate header file, protij.h, but I don't want to create one for just a couple of things. */ /* An escape sequence of characters for the 'j' protocol to avoid (protocol parameter ``avoid''). */ extern const char *zJavoid_parameter; /* Timeout to use when sending the 'i' protocol SYNC packet (protocol parameter ``sync-timeout''). */ extern int cIsync_timeout; /* Shared startup routine for the 'i' and 'j' protocols. */ extern boolean fijstart P((struct sdaemon *qdaemon, char **pzlog, int imaxpacksize, boolean (*pfsend) P((struct sconnection *qconn, const char *zsend, size_t csend, boolean fdoread)), boolean (*pfreceive) P((struct sconnection *qconn, size_t cneed, size_t *pcrec, int ctimeout, boolean freport)))); /* Prototypes for 'g' protocol functions. */ extern struct uuconf_cmdtab asGproto_params[]; extern boolean fgstart P((struct sdaemon *qdaemon, char **pzlog)); extern boolean fbiggstart P((struct sdaemon *qdaemon, char **pzlog)); extern boolean fvstart P((struct sdaemon *qdaemon, char **pzlog)); extern boolean fgshutdown P((struct sdaemon *qdaemon)); extern boolean fgsendcmd P((struct sdaemon *qdaemon, const char *z, int ilocal, int iremote)); extern char *zggetspace P((struct sdaemon *qdaemon, size_t *pcdata)); extern boolean fgsenddata P((struct sdaemon *qdaemon, char *z, size_t c, int ilocal, int iremote, long ipos)); extern boolean fgwait P((struct sdaemon *qdaemon)); /* Prototypes for 'f' protocol functions. */ extern struct uuconf_cmdtab asFproto_params[]; extern boolean ffstart P((struct sdaemon *qdaemon, char **pzlog)); extern boolean ffshutdown P((struct sdaemon *qdaemon)); extern boolean ffsendcmd P((struct sdaemon *qdaemon, const char *z, int ilocal, int iremote)); extern char *zfgetspace P((struct sdaemon *qdaemon, size_t *pcdata)); extern boolean ffsenddata P((struct sdaemon *qdaemon, char *z, size_t c, int ilocal, int iremote, long ipos)); extern boolean ffwait P((struct sdaemon *qdaemon)); extern boolean fffile P((struct sdaemon *qdaemon, struct stransfer *qtrans, boolean fstart, boolean fsend, long cbytes, boolean *pfhandled)); /* Prototypes for 't' protocol functions. */ extern struct uuconf_cmdtab asTproto_params[]; extern boolean ftstart P((struct sdaemon *qdaemon, char **pzlog)); extern boolean ftshutdown P((struct sdaemon *qdaemon)); extern boolean ftsendcmd P((struct sdaemon *qdaemon, const char *z, int ilocal, int iremote)); extern char *ztgetspace P((struct sdaemon *qdaemon, size_t *pcdata)); extern boolean ftsenddata P((struct sdaemon *qdaemon, char *z, size_t c, int ilocal, int iremote, long ipos)); extern boolean ftwait P((struct sdaemon *qdaemon)); extern boolean ftfile P((struct sdaemon *qdaemon, struct stransfer *qtrans, boolean fstart, boolean fsend, long cbytes, boolean *pfhandled)); /* Prototypes for 'e' protocol functions. */ extern struct uuconf_cmdtab asEproto_params[]; extern boolean festart P((struct sdaemon *qdaemon, char **pzlog)); extern boolean feshutdown P((struct sdaemon *qdaemon)); extern boolean fesendcmd P((struct sdaemon *qdaemon, const char *z, int ilocal, int iremote)); extern char *zegetspace P((struct sdaemon *qdaemon, size_t *pcdata)); extern boolean fesenddata P((struct sdaemon *qdaemon, char *z, size_t c, int ilocal, int iremote, long ipos)); extern boolean fewait P((struct sdaemon *qdaemon)); extern boolean fefile P((struct sdaemon *qdaemon, struct stransfer *qtrans, boolean fstart, boolean fsend, long cbytes, boolean *pfhandled)); /* Prototypes for 'i' protocol functions. */ extern struct uuconf_cmdtab asIproto_params[]; extern boolean fistart P((struct sdaemon *qdaemon, char **pzlog)); extern boolean fishutdown P((struct sdaemon *qdaemon)); extern boolean fisendcmd P((struct sdaemon *qdaemon, const char *z, int ilocal, int iremote)); extern char *zigetspace P((struct sdaemon *qdaemon, size_t *pcdata)); extern boolean fisenddata P((struct sdaemon *qdaemon, char *z, size_t c, int ilocal, int iremote, long ipos)); extern boolean fiwait P((struct sdaemon *qdaemon)); /* Prototypes for 'j' protocol functions. The 'j' protocol mostly uses the 'i' protocol functions, but it has a couple of functions of its own. */ extern boolean fjstart P((struct sdaemon *qdaemon, char **pzlog)); extern boolean fjshutdown P((struct sdaemon *qdaemon)); /* Prototypes for 'a' protocol functions (these use 'z' as the second character because 'a' is a modified Zmodem protocol). */ extern struct uuconf_cmdtab asZproto_params[]; extern boolean fzstart P((struct sdaemon *qdaemon, char **pzlog)); extern boolean fzshutdown P((struct sdaemon *qdaemon)); extern boolean fzsendcmd P((struct sdaemon *qdaemon, const char *z, int ilocal, int iremote)); extern char *zzgetspace P((struct sdaemon *qdaemon, size_t *pcdata)); extern boolean fzsenddata P((struct sdaemon *qdaemon, char *z, size_t c, int ilocal, int iremote, long ipos)); extern boolean fzwait P((struct sdaemon *qdaemon)); extern boolean fzfile P((struct sdaemon *qdaemon, struct stransfer *qtrans, boolean fstart, boolean fsend, long cbytes, boolean *pfhandled)); /* Prototypes for 'y' protocol functions. */ extern struct uuconf_cmdtab asYproto_params[]; extern boolean fystart P((struct sdaemon *qdaemon, char **pzlog)); extern boolean fyshutdown P((struct sdaemon *qdaemon)); extern boolean fysendcmd P((struct sdaemon *qdaemon, const char *z, int ilocal, int iremote)); extern char *zygetspace P((struct sdaemon *qdaemon, size_t *pcdata)); extern boolean fysenddata P((struct sdaemon *qdaemon, char *z, size_t c, int ilocal, int iremote, long ipos)); extern boolean fywait P((struct sdaemon *qdaemon)); extern boolean fyfile P((struct sdaemon *qdaemon, struct stransfer *qtrans, boolean fstart, boolean fsend, long cbytes, boolean *pfhandled)); uucp-1.07/protg.c0000664000076400007640000016146507665321755007444 /* protg.c The 'g' protocol. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char protg_rcsid[] = "$Id: protg.c,v 1.71 2002/03/05 19:10:41 ian Rel $"; #endif #include #include #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "trans.h" #include "system.h" #include "prot.h" /* Each 'g' protocol packet begins with six bytes. They are: is the ASCII DLE character (^P or '\020'). if 1 <= <= 8, the packet is followed by 2 ** (k + 4) bytes of data; if == 9, these six bytes are a complete control packet; other value of are illegal. is the low byte of a checksum. is the high byte of a checksum. is a control byte (see below). is ^ ^ ^ . The control byte is divided into three bitfields: t t x x x y y y The two bit field tt is the packet type. The three bit field xxx is the control type for a control packet, or the sequence number for a data packet. The three bit field yyy is a value for a control packet, or the sequence number of the last packet received for a data packet. For all successfully recieved packets, the control byte is stored into iGpacket_control. */ /* Names for the bytes in the frame header. */ #define IFRAME_DLE (0) #define IFRAME_K (1) #define IFRAME_CHECKLOW (2) #define IFRAME_CHECKHIGH (3) #define IFRAME_CONTROL (4) #define IFRAME_XOR (5) /* Length of the frame header. */ #define CFRAMELEN (6) /* Macros to break apart the control bytes. */ #define CONTROL_TT(b) ((int)(((b) >> 6) & 03)) #define CONTROL_XXX(b) ((int)(((b) >> 3) & 07)) #define CONTROL_YYY(b) ((int)((b) & 07)) /* DLE value. */ #define DLE ('\020') /* Get the length of a packet given a pointer to the header. */ #define CPACKLEN(z) ((size_t) (1 << ((z)[IFRAME_K] + 4))) /* field value for a control message. */ #define KCONTROL (9) /* Get the next sequence number given a sequence number. */ #define INEXTSEQ(i) ((i + 1) & 07) /* Compute i1 - i2 modulo 8. */ #define CSEQDIFF(i1, i2) (((i1) + 8 - (i2)) & 07) /* Packet types. These are from the tt field. CONTROL -- control packet ALTCHAN -- alternate channel; not used by UUCP DATA -- full data segment SHORTDATA -- less than full data segment (all the bytes specified by the packet length are always transferred). Let be the number of bytes in the data segment not to be used. If <= 0x7f, the first byte of the data segment is and the data follows. If > 0x7f, the first byte of the data segment is 0x80 | ( & 0x7f), the second byte of the data segment is >> 7, and the data follows. The maximum possible data segment size is 2**12, so this handles all possible cases. */ #define CONTROL (0) #define ALTCHAN (1) #define DATA (2) #define SHORTDATA (3) /* Control types. These are from the xxx field if the type (tt field) is CONTROL. CLOSE -- close the connection RJ -- reject; packet yyy last to be received correctly SRJ -- selective reject; reject only packet yyy (not used by UUCP) RR -- receiver ready; packet yyy received correctly INITC -- third step of initialization; yyy holds window size INITB -- second step of initialization; yyy holds maximum value - 1 INITA -- first step of initialization; yyy holds window size. The yyy value for RR is the same as the yyy value for an ordinary data packet. */ #define CLOSE (1) #define RJ (2) #define SRJ (3) #define RR (4) #define INITC (5) #define INITB (6) #define INITA (7) /* Maximum amount of data in a single packet. This is set by the field in the header; the amount of data in a packet is 2 ** ( + 4). ranges from 1 to 8. */ #define CMAXDATAINDEX (8) #define CMAXDATA (1 << (CMAXDATAINDEX + 4)) /* Maximum window size. */ #define CMAXWINDOW (7) /* Defaults for the protocol parameters. These may all be changed by using the ``protocol-parameter g'' command, so there is no particular reason to change the values given here. */ /* The desired window size. This is what we tell the other system to use. It must be between 1 and 7, and there's no reason to use less than 7. Protocol parameter ``window''. */ #define IWINDOW (7) /* The desired packet size. Many implementations only support 64 byte packets. Protocol parameter ``packet-size''. */ #define IPACKSIZE (64) /* The number of times to retry the exchange of INIT packets when starting the protocol. Protocol parameter ``startup-retries''. */ #define CSTARTUP_RETRIES (8) /* The timeout to use when waiting for an INIT packet when starting up the protocol. Protocol parameter ``init-timeout''. */ #define CEXCHANGE_INIT_TIMEOUT (10) /* The number of times to retry sending and waiting for a single INIT packet when starting the protocol. This controls a single INIT packet, while CSTARTUP_RETRIES controls how many times to try the entire INIT sequence. Protocol parameter ``init-retries''. */ #define CEXCHANGE_INIT_RETRIES (4) /* The timeout to use when waiting for a packet. Protocol parameter ``timeout''. */ #define CTIMEOUT (10) /* The number of times to retry waiting for a packet. Each time the timeout fails we send a copy of our last data packet or a reject message for the packet we expect from the other side, depending on whether we are waiting for an acknowledgement or a data packet. This is the number of times we try doing that and then waiting again. Protocol parameter ``retries''. */ #define CRETRIES (6) /* If we see more than this much unrecognized data, we drop the connection. This must be larger than a single packet size, which means it must be larger than 4096 (the largest possible packet size). Protocol parameter ``garbage''. */ #define CGARBAGE (10000) /* If we see more than this many protocol errors, we drop the connection. Protocol parameter ``errors''. */ #define CERRORS (100) /* Default decay rate. Each time we send or receive this many packets succesfully, we decrement the error level by one (protocol parameter ``error-decay''). */ #define CERROR_DECAY (10) /* If this value is non-zero, it will be used as the remote window size regardless of what the other side requested. This can be useful for dealing with some particularly flawed packages. This default value should always be 0, and protocol parameter ``remote-window'' should be used for the affected systems. */ #define IREMOTE_WINDOW (0) /* If this value is non-zero, it will be used as the packet size to send to the remote system regardless of what it requested. It's difficult to imagine any circumstances where you would want to set this. Protocol parameter ``remote-packet-size''. */ #define IREMOTE_PACKSIZE (0) /* Local variables. */ /* Next sequence number to send. */ static int iGsendseq; /* Last sequence number that has been acked. */ static int iGremote_ack; /* Last sequence number to be retransmitted. */ static int iGretransmit_seq; /* Last sequence number we have received. */ static int iGrecseq; /* Last sequence number we have acked. */ static int iGlocal_ack; /* Window size to request (protocol parameter ``window''). */ static int iGrequest_winsize = IWINDOW; /* Packet size to request (protocol parameter ``packet-size''). */ static int iGrequest_packsize = IPACKSIZE; /* Remote window size (set during handshake). */ static int iGremote_winsize; /* Forced remote window size (protocol parameter ``remote-window''). */ static int iGforced_remote_winsize = IREMOTE_WINDOW; /* Remote segment size (set during handshake). This is one less than the value in a packet header. */ static int iGremote_segsize; /* Remote packet size (set based on iGremote_segsize). */ static size_t iGremote_packsize; /* Forced remote packet size (protocol parameter ``remote-packet-size''). */ static int iGforced_remote_packsize = IREMOTE_PACKSIZE; /* Recieved control byte. */ static int iGpacket_control; /* Number of times to retry the initial handshake. Protocol parameter ``startup-retries''. */ static int cGstartup_retries = CSTARTUP_RETRIES; /* Number of times to retry sending an initial control packet. Protocol parameter ``init-retries''. */ static int cGexchange_init_retries = CEXCHANGE_INIT_RETRIES; /* Timeout (seconds) for receiving an initial control packet. Protocol parameter ``init-timeout''. */ static int cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT; /* Timeout (seconds) for receiving a data packet. Protocol parameter ``timeout''. */ static int cGtimeout = CTIMEOUT; /* Maximum number of timeouts when receiving a data packet or acknowledgement. Protocol parameter ``retries''. */ static int cGretries = CRETRIES; /* Amount of garbage data we are prepared to see before giving up. Protocol parameter ``garbage''. */ static int cGgarbage_data = CGARBAGE; /* Maximum number of errors we are prepared to see before giving up. Protocol parameter ``errors''. */ static int cGmax_errors = CERRORS; /* Each time we receive this many packets succesfully, we decrement the error level by one (protocol parameter ``error-decay''). */ static int cGerror_decay = CERROR_DECAY; /* Whether to use shorter packets when possible. Protocol parameter ``short-packets''. */ static boolean fGshort_packets = TRUE; /* Protocol parameter commands. */ struct uuconf_cmdtab asGproto_params[] = { { "window", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_winsize, NULL }, { "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_packsize, NULL }, { "startup-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGstartup_retries, NULL }, { "init-timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_timeout, NULL }, { "init-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_retries, NULL }, { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGtimeout, NULL }, { "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGretries, NULL }, { "garbage", UUCONF_CMDTABTYPE_INT, (pointer) &cGgarbage_data, NULL }, { "errors", UUCONF_CMDTABTYPE_INT, (pointer) &cGmax_errors, NULL }, { "error-decay", UUCONF_CMDTABTYPE_INT, (pointer) &cGerror_decay, NULL }, { "remote-window", UUCONF_CMDTABTYPE_INT, (pointer) &iGforced_remote_winsize, NULL }, { "remote-packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iGforced_remote_packsize, NULL }, { "short-packets", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fGshort_packets, NULL }, { NULL, 0, NULL, NULL } }; /* Statistics. */ /* Number of packets we have sent. */ static long cGsent_packets; /* Number of packets we have resent (these are not included in cGsent_packets). */ static long cGresent_packets; /* Number of packets we have delayed sending (these should not be counted in cGresent_packets). */ static long cGdelayed_packets; /* Number of packets we have received. */ static long cGrec_packets; /* Number of packets rejected because the header was bad. */ static long cGbad_hdr; /* Number of packets rejected because the checksum was bad. */ static long cGbad_checksum; /* Number of packets received out of order. */ static long cGbad_order; /* Number of packets rejected by receiver (number of RJ packets received). */ static long cGremote_rejects; /* Number of duplicate RR packets treated as RJ packets. Some UUCP packages appear to never send RJ packets, but only RR packets. If no RJ has been seen, fgprocess_data treats a duplicate RR as an RJ and increments this variable. */ static long cGremote_duprrs; /* The error level. This is the total number of errors as adjusted by cGerror_decay. */ static long cGerror_level; /* Each time we send an RJ, we can expect several out of order of packets, because the other side will probably have sent a full window by the time it sees the RJ. This variable keeps track of the number of out of order packets we expect to see. We don't count expected out of order packets against the error level. This is reset to 0 when an in order packet is received. */ static int cGexpect_bad_order; #if DEBUG > 1 /* Control packet names used for debugging. */ static const char * const azGcontrol[] = {"?0?", "CLOSE", "RJ", "SRJ", "RR", "INITC", "INITB", "INITA"}; #endif /* Local functions. */ static boolean fgexchange_init P((struct sdaemon *qdaemon, int ictl, int ival, int *piset)); static boolean fgsend_control P((struct sdaemon *qdaemon, int ictl, int ival)); static char *zgadjust_ack P((int iseq)); static boolean fgwait_for_packet P((struct sdaemon *qdaemon, boolean freturncontrol, int ctimeout, int cretries)); static boolean fgsend_acks P((struct sdaemon *qdaemon)); static boolean fggot_ack P((struct sdaemon *qdaemon, int iack)); static boolean fgprocess_data P((struct sdaemon *qdaemon, boolean fdoacks, boolean freturncontrol, boolean *pfexit, size_t *pcneed, boolean *pffound)); static boolean fginit_sendbuffers P((boolean fallocate)); static boolean fgcheck_errors P((struct sdaemon *qdaemon)); static int igchecksum P((const char *zdata, size_t clen)); static int igchecksum2 P((const char *zfirst, size_t cfirst, const char *zsecond, size_t csecond)); /* Start the protocol. This requires a three way handshake. Both sides must send and receive an INITA packet, an INITB packet, and an INITC packet. The INITA and INITC packets contain the window size, and the INITB packet contains the packet size. */ boolean fgstart (qdaemon, pzlog) struct sdaemon *qdaemon; char **pzlog; { int iseg; int i; boolean fgota, fgotb; *pzlog = NULL; /* The 'g' protocol requires a full eight bit interface. */ if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, STRIPSETTING_EIGHTBITS, XONXOFF_OFF)) return FALSE; iGsendseq = 1; iGremote_ack = 0; iGretransmit_seq = -1; iGrecseq = 0; iGlocal_ack = 0; cGsent_packets = 0; cGresent_packets = 0; cGdelayed_packets = 0; cGrec_packets = 0; cGbad_hdr = 0; cGbad_checksum = 0; cGbad_order = 0; cGremote_rejects = 0; cGremote_duprrs = 0; cGerror_level = 0; cGexpect_bad_order = 0; /* We must determine the segment size based on the packet size which may have been modified by a protocol parameter command. A segment size of 2^n is passed as n - 5. */ i = iGrequest_packsize; iseg = -1; while (i > 0) { ++iseg; i >>= 1; } iseg -= 5; if (iseg < 0 || iseg > 7) { ulog (LOG_ERROR, "Illegal packet size %d for '%c' protocol", iGrequest_packsize, qdaemon->qproto->bname); iseg = 1; } if (iGrequest_winsize <= 0 || iGrequest_winsize > 7) { ulog (LOG_ERROR, "Illegal window size %d for '%c' protocol", iGrequest_winsize, qdaemon->qproto->bname); iGrequest_winsize = IWINDOW; } fgota = FALSE; fgotb = FALSE; for (i = 0; i < cGstartup_retries; i++) { if (fgota) { if (! fgsend_control (qdaemon, INITA, iGrequest_winsize)) return FALSE; } else { if (! fgexchange_init (qdaemon, INITA, iGrequest_winsize, &iGremote_winsize)) continue; } fgota = TRUE; if (fgotb) { if (! fgsend_control (qdaemon, INITB, iseg)) return FALSE; } else { if (! fgexchange_init (qdaemon, INITB, iseg, &iGremote_segsize)) continue; } fgotb = TRUE; if (! fgexchange_init (qdaemon, INITC, iGrequest_winsize, &iGremote_winsize)) continue; /* We have succesfully connected. Determine the remote packet size. */ iGremote_packsize = 1 << (iGremote_segsize + 5); /* If the user requested us to force specific remote window and packet sizes, do so now. */ if (iGforced_remote_winsize > 0 && iGforced_remote_winsize <= CMAXWINDOW) iGremote_winsize = iGforced_remote_winsize; if (iGforced_remote_packsize >= 32 && iGforced_remote_packsize <= 4096) { /* Force the value to a power of two. */ i = iGforced_remote_packsize; iseg = -1; while (i > 0) { ++iseg; i >>= 1; } iGremote_packsize = 1 << iseg; iGremote_segsize = iseg - 5; } /* Set up packet buffers to use. We don't do this until we know the maximum packet size we are going to send. */ if (! fginit_sendbuffers (TRUE)) return FALSE; *pzlog = zbufalc (sizeof "protocol '' sending packet/window / receiving /" + 64); sprintf (*pzlog, "protocol '%c' sending packet/window %d/%d receiving %d/%d", qdaemon->qproto->bname, (int) iGremote_packsize, (int) iGremote_winsize, (int) iGrequest_packsize, (int) iGrequest_winsize); return TRUE; } DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL, "fgstart: Protocol startup failed"); return FALSE; } /* The 'G' protocol is identical to the 'g' protocol, except that short packets are never supported. */ boolean fbiggstart (qdaemon, pzlog) struct sdaemon *qdaemon; char **pzlog; { fGshort_packets = FALSE; return fgstart (qdaemon, pzlog); } /* The 'v' protocol is identical to the 'g' protocol, except that the packet size defaults to 512 bytes. Rather than really get it right, we automatically switch from the usual default of 64 to 512. This won't work correctly if somebody does protocol-parameter v packet-size 64. */ boolean fvstart (qdaemon, pzlog) struct sdaemon *qdaemon; char **pzlog; { if (iGrequest_packsize == IPACKSIZE) iGrequest_packsize = 1024; return fgstart (qdaemon, pzlog); } /* Exchange initialization messages with the other system. A problem: We send INITA; it gets received We receive INITA We send INITB; it gets garbled We receive INITB We have seen and sent INITB, so we start to send INITC. The other side as sent INITB but not seen it, so it times out and resends INITB. We will continue sending INITC and the other side will continue sending INITB until both sides give up and start again with INITA. It might seem as though if we are sending INITC and receive INITB, we should resend our INITB, but this could cause infinite echoing of INITB on a long-latency line. Rather than risk that, I have implemented a fast drop-back procedure. If we are sending INITB and receive INITC, the other side has gotten ahead of us. We immediately fail and begin again with INITA. For the other side, if we are sending INITC and see INITA, we also immediately fail back to INITA. Unfortunately, this doesn't work for the other case, in which we are sending INITB but the other side has not yet seen INITA. As far as I can see, if this happens we just have to wait until we time out and resend INITA. */ static boolean fgexchange_init (qdaemon, ictl, ival, piset) struct sdaemon *qdaemon; int ictl; int ival; int *piset; { int i; /* The three-way handshake should be independent of who initializes it, but it seems that some versions of uucico assume that the caller sends first and the callee responds. This only matters if we are the callee and the first packet is garbled. If we send a packet, the other side will assume that we must have seen the packet they sent and will never time out and send it again. Therefore, if we are the callee we don't send a packet the first time through the loop. This can still fail, but should usually work, and, after all, if the initialization packets are received correctly there will be no problem no matter what we do. */ for (i = 0; i < cGexchange_init_retries; i++) { long itime; int ctimeout; if (qdaemon->fcaller || i > 0) { if (! fgsend_control (qdaemon, ictl, ival)) return FALSE; } itime = ixsysdep_time ((long *) NULL); ctimeout = cGexchange_init_timeout; do { long inewtime; /* We pass 0 as the retry count to fgwait_for_packet because we want to handle retries here and because if it retried it would send a packet, which would be bad. */ if (! fgwait_for_packet (qdaemon, TRUE, ctimeout, 0)) break; if (CONTROL_TT (iGpacket_control) == CONTROL) { if (CONTROL_XXX (iGpacket_control) == ictl) { *piset = CONTROL_YYY (iGpacket_control); /* If we didn't already send our initialization packet, send it now. */ if (! qdaemon->fcaller && i == 0) { if (! fgsend_control (qdaemon, ictl, ival)) return FALSE; } return TRUE; } /* If the other side is farther along than we are, we have lost a packet. Fail immediately back to INITA (but don't fail if we are already doing INITA, since that would count against cStart_retries more than it should). */ if (CONTROL_XXX (iGpacket_control) < ictl && ictl != INITA) return FALSE; /* If we are sending INITC and we receive an INITA, the other side has failed back (we know this because we have seen an INITB from them). Fail back ourselves to start the whole handshake over again. */ if (CONTROL_XXX (iGpacket_control) == INITA && ictl == INITC) return FALSE; /* As a special hack, if we are sending INITC and we receive INITB, we update the segment size from the packet. This permits a second INITB to override the first one. It would be nice to do this in a cleaner way. */ if (CONTROL_XXX (iGpacket_control) == INITB && ictl == INITC) iGremote_segsize = CONTROL_YYY (iGpacket_control); } inewtime = ixsysdep_time ((long *) NULL); ctimeout -= inewtime - itime; } while (ctimeout > 0); } return FALSE; } /* Shut down the protocol. */ boolean fgshutdown (qdaemon) struct sdaemon *qdaemon; { (void) fgsend_control (qdaemon, CLOSE, 0); (void) fgsend_control (qdaemon, CLOSE, 0); (void) fginit_sendbuffers (FALSE); /* The count of sent packets may not be accurate, because some of them may have not been sent yet if the connection failed in the middle (the ones that counted for cGdelayed_packets). I don't think it's worth being precise. */ ulog (LOG_NORMAL, "Protocol '%c' packets: sent %ld, resent %ld, received %ld", qdaemon->qproto->bname, cGsent_packets, cGresent_packets - cGdelayed_packets, cGrec_packets); if (cGbad_hdr != 0 || cGbad_checksum != 0 || cGbad_order != 0 || cGremote_rejects != 0 || cGremote_duprrs != 0) ulog (LOG_NORMAL, "Errors: header %ld, checksum %ld, order %ld, remote rejects %ld", cGbad_hdr, cGbad_checksum, cGbad_order, cGremote_duprrs + cGremote_rejects); /* Reset all the parameters to their default values, so that the protocol parameters used for this connection do not affect the next one. */ iGrequest_winsize = IWINDOW; iGrequest_packsize = IPACKSIZE; cGstartup_retries = CSTARTUP_RETRIES; cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT; cGexchange_init_retries = CEXCHANGE_INIT_RETRIES; cGtimeout = CTIMEOUT; cGretries = CRETRIES; cGgarbage_data = CGARBAGE; cGmax_errors = CERRORS; cGerror_decay = CERROR_DECAY; iGforced_remote_winsize = IREMOTE_WINDOW; iGforced_remote_packsize = IREMOTE_PACKSIZE; fGshort_packets = TRUE; return TRUE; } /* Send a command string. We send packets containing the string until the entire string has been sent. Each packet is full. */ /*ARGSUSED*/ boolean fgsendcmd (qdaemon, z, ilocal, iremote) struct sdaemon *qdaemon; const char *z; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; { size_t clen; boolean fagain; DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fgsendcmd: Sending command \"%s\"", z); clen = strlen (z); do { char *zpacket; size_t cdummy; zpacket = zggetspace (qdaemon, &cdummy); if (clen < iGremote_packsize) { size_t csize; /* If the remote packet size is larger than 64 (the default, which may indicate an older UUCP package), try to fit this command into a smaller packet. We still always send a complete packet, though. */ if (iGremote_packsize <= 64 || ! fGshort_packets) csize = iGremote_packsize; else { csize = 32; while (csize <= clen) csize <<= 1; } memcpy (zpacket, z, clen); if (csize > clen) bzero (zpacket + clen, csize - clen); fagain = FALSE; if (! fgsenddata (qdaemon, zpacket, csize, 0, 0, (long) 0)) return FALSE; } else { memcpy (zpacket, z, iGremote_packsize); z += iGremote_packsize; clen -= iGremote_packsize; fagain = TRUE; if (! fgsenddata (qdaemon, zpacket, iGremote_packsize, 0, 0, (long) 0)) return FALSE; } } while (fagain); return TRUE; } /* We keep an array of buffers to retransmit as necessary. Rather than waste static space on large buffer sizes, we allocate the buffers once we know how large the other system expects them to be. The sequence numbers used in the 'g' protocol are only three bits long, so we allocate eight buffers and maintain a correspondence between buffer index and sequence number. This always wastes some buffer space, but it's easy to implement. We leave room at the front of the buffer for the frame header and two additional bytes. The two extra bytes are used for short packets, which essentially use a longer header and shorter data. We do this to avoid moving the data. We zero out any unused bytes before the frame, so we can locate the real header given a buffer by finding the first non-zero byte (which will be one of the first three bytes in the buffer). */ #define CSENDBUFFERS (CMAXWINDOW + 1) static char *azGsendbuffers[CSENDBUFFERS]; static boolean fginit_sendbuffers (fallocate) boolean fallocate; { int i; /* Free up any remaining old buffers. */ for (i = 0; i < CSENDBUFFERS; i++) { xfree ((pointer) azGsendbuffers[i]); if (fallocate) { azGsendbuffers[i] = (char *) malloc (CFRAMELEN + 2 + iGremote_packsize); if (azGsendbuffers[i] == NULL) return FALSE; /* This bzero might not seem necessary, since before we send out each packet we zero out any non-data bytes. However, if we receive an SRJ at the start of the conversation, we will send out the packet before it has been set to anything, thus sending the contents of our heap. We avoid this by using bzero. */ bzero (azGsendbuffers[i], CFRAMELEN + 2 + iGremote_packsize); } else azGsendbuffers[i] = NULL; } return TRUE; } /* Allocate a packet to send out. The return value of this function must be filled in and passed to fgsenddata, or discarded. This will ensure that the buffers and iGsendseq stay in synch. Set *pclen to the amount of data to place in the buffer. */ /*ARGSUSED*/ char * zggetspace (qdaemon, pclen) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; size_t *pclen; { *pclen = iGremote_packsize; return azGsendbuffers[iGsendseq] + CFRAMELEN + 2; } /* Send out a data packet. This computes the checksum, sets up the header, and sends the packet out. The argument zdata should point to the return value of zggetspace. */ /*ARGSIGNORED*/ boolean fgsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) struct sdaemon *qdaemon; char *zdata; size_t cdata; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; long ipos ATTRIBUTE_UNUSED; { char *z; int itt, iseg; size_t csize; int iclr1, iclr2; unsigned short icheck; /* Set the initial length bytes. See the description at the definition of SHORTDATA, above. */ itt = DATA; csize = iGremote_packsize; iseg = iGremote_segsize + 1; #if DEBUG > 0 if (cdata > csize) ulog (LOG_FATAL, "fgsend_packet: Packet size too large"); #endif iclr1 = -1; iclr2 = -2; if (cdata < csize) { /* If the remote packet size is larger than 64, the default, we can assume they can handle a smaller packet as well, which will be more efficient to send. */ if (iGremote_packsize > 64 && fGshort_packets) { /* The packet size is 1 << (iseg + 4). */ iseg = 1; csize = 32; while (csize < cdata) { csize <<= 1; ++iseg; } } if (csize != cdata) { size_t cshort; /* We have to add bytes which indicate how short the packet is. We do this by pushing the header backward, which we can do because we allocated two extra bytes for this purpose. */ iclr2 = 0; itt = SHORTDATA; cshort = csize - cdata; if (cshort <= 127) { --zdata; zdata[0] = (char) cshort; zdata[-1] = '\0'; if (cshort > 1) bzero (zdata + cdata + 1, cshort - 1); } else { zdata -= 2; zdata[0] = (char) (0x80 | (cshort & 0x7f)); zdata[1] = (char) (cshort >> 7); bzero (zdata + cdata + 2, cshort - 2); iclr1 = 0; } } } z = zdata - CFRAMELEN; /* Zero out the preceding bytes, in case the last time this buffer was used those bytes were used. We need to zero out the initial bytes so that we can find the true start of the packet in zgadjust_ack. */ z[iclr1] = '\0'; z[iclr2] = '\0'; z[IFRAME_DLE] = DLE; z[IFRAME_K] = (char) iseg; icheck = (unsigned short) igchecksum (zdata, csize); /* We're just about ready to go. Wait until there is room in the receiver's window for us to send the packet. We do this now so that we send the correct value for the last packet received. Note that if iGsendseq == iGremote_ack, this means that the sequence numbers are actually 8 apart, since the packet could not have been acknowledged before it was sent; this can happen when the window size is 7. */ while (iGsendseq == iGremote_ack || CSEQDIFF (iGsendseq, iGremote_ack) > iGremote_winsize) { if (! fgwait_for_packet (qdaemon, TRUE, cGtimeout, cGretries)) return FALSE; } /* Ack all packets up to the next one, since the UUCP protocol requires that all packets be acked in order. */ while (CSEQDIFF (iGrecseq, iGlocal_ack) > 1) { iGlocal_ack = INEXTSEQ (iGlocal_ack); if (! fgsend_control (qdaemon, RR, iGlocal_ack)) return FALSE; } iGlocal_ack = iGrecseq; z[IFRAME_CONTROL] = (char) ((itt << 6) | (iGsendseq << 3) | iGrecseq); iGsendseq = INEXTSEQ (iGsendseq); icheck = ((unsigned short) ((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff)); z[IFRAME_CHECKLOW] = (char) (icheck & 0xff); z[IFRAME_CHECKHIGH] = (char) (icheck >> 8); z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW] ^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]); /* If we're waiting for acks of retransmitted packets, then don't send this packet yet. The other side may not be ready for it yet. Instead, code in fggot_ack will send the outstanding packets when an ack is received. */ ++cGsent_packets; if (iGretransmit_seq != -1) { ++cGdelayed_packets; return TRUE; } DEBUG_MESSAGE2 (DEBUG_PROTO, "fgsenddata: Sending packet %d (%d bytes)", CONTROL_XXX (z[IFRAME_CONTROL]), cdata); return fsend_data (qdaemon->qconn, z, CFRAMELEN + csize, TRUE); } /* Recompute the control byte and checksum of a packet so that it includes the correct packet acknowledgement. This is called when a packet is retransmitted to make sure the retransmission does not confuse the other side. It returns a pointer to the start of the packet, skipping the bytes that may be unused at the start of azGsendbuffers[iseq]. */ static char * zgadjust_ack (iseq) int iseq; { register char *z; unsigned short icheck; z = azGsendbuffers[iseq]; if (*z == '\0') ++z; if (*z == '\0') ++z; /* If the received packet number is the same, there is nothing to do. */ if (CONTROL_YYY (z[IFRAME_CONTROL]) == iGrecseq) return z; /* Get the old checksum. */ icheck = (unsigned short) (((z[IFRAME_CHECKHIGH] & 0xff) << 8) | (z[IFRAME_CHECKLOW] & 0xff)); icheck = ((unsigned short) (((0xaaaa - icheck) ^ (z[IFRAME_CONTROL] & 0xff)) & 0xffff)); /* Update the control byte. */ z[IFRAME_CONTROL] = (char) ((z[IFRAME_CONTROL] &~ 07) | iGrecseq); /* Create the new checksum. */ icheck = ((unsigned short) ((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff)); z[IFRAME_CHECKLOW] = (char) (icheck & 0xff); z[IFRAME_CHECKHIGH] = (char) (icheck >> 8); /* Update the XOR byte. */ z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW] ^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]); return z; } /* Send a control packet. These are fairly simple to construct. It seems reasonable to me that we should be able to send a control packet at any time, even if the receive window is closed. In particular, we don't want to delay when sending a CLOSE control message. If I'm wrong, it can be changed easily enough. */ static boolean fgsend_control (qdaemon, ixxx, iyyy) struct sdaemon *qdaemon; int ixxx; int iyyy; { char ab[CFRAMELEN]; int ictl; unsigned short icheck; #if DEBUG > 1 if (FDEBUGGING (DEBUG_PROTO) || (FDEBUGGING (DEBUG_ABNORMAL) && ixxx != RR)) ulog (LOG_DEBUG, "fgsend_control: Sending control %s %d", azGcontrol[ixxx], iyyy); #endif ab[IFRAME_DLE] = DLE; ab[IFRAME_K] = KCONTROL; ictl = (CONTROL << 6) | (ixxx << 3) | iyyy; icheck = (unsigned short) (0xaaaa - ictl); ab[IFRAME_CHECKLOW] = (char) (icheck & 0xff); ab[IFRAME_CHECKHIGH] = (char) (icheck >> 8); ab[IFRAME_CONTROL] = (char) ictl; ab[IFRAME_XOR] = (char) (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW] ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL]); return fsend_data (qdaemon->qconn, ab, (size_t) CFRAMELEN, TRUE); } /* Wait for data to come in. This continues processing until a complete file or command has been received. */ boolean fgwait (qdaemon) struct sdaemon *qdaemon; { return fgwait_for_packet (qdaemon, FALSE, cGtimeout, cGretries); } /* Get a packet. This is called when we have nothing to send, but want to wait for a packet to come in. If freturncontrol is TRUE, this will return after getting any control packet. Otherwise, it will continue to receive packets until a complete file or a complete command has been received. The timeout and the number of retries are specified as arguments. The function returns FALSE if an error occurs or if cretries timeouts of ctimeout seconds were exceeded. */ static boolean fgwait_for_packet (qdaemon, freturncontrol, ctimeout, cretries) struct sdaemon *qdaemon; boolean freturncontrol; int ctimeout; int cretries; { int ctimeouts; int cgarbage; int cshort; ctimeouts = 0; cgarbage = 0; cshort = 0; while (TRUE) { boolean fexit; size_t cneed; boolean ffound; size_t crec; if (! fgprocess_data (qdaemon, TRUE, freturncontrol, &fexit, &cneed, &ffound)) return FALSE; if (fexit) return TRUE; DEBUG_MESSAGE1 (DEBUG_PROTO, "fgwait_for_packet: Need %lu bytes", (unsigned long) cneed); if (ffound) { ctimeouts = 0; cgarbage = 0; } else { if (cgarbage > cGgarbage_data) { ulog (LOG_ERROR, "Too much unrecognized data"); return FALSE; } } if (! freceive_data (qdaemon->qconn, cneed, &crec, ctimeout, TRUE)) return FALSE; cgarbage += crec; if (crec != 0) { /* If we don't get enough data twice in a row, we may have dropped some data and still be looking for the end of a large packet. Incrementing iPrecstart will force fgprocess_data to skip that packet and look through the rest of the data. In some situations, this will be a mistake. */ if (crec >= cneed) cshort = 0; else { ++cshort; if (cshort > 1) { iPrecstart = (iPrecstart + 1) % CRECBUFLEN; cshort = 0; } } } else { /* The read timed out. If we have an unacknowledged packet, send it again. Otherwise, send an RJ with the last packet we received correctly. */ ++ctimeouts; if (ctimeouts > cretries) { if (cretries > 0) ulog (LOG_ERROR, "Timed out waiting for packet"); return FALSE; } if (INEXTSEQ (iGremote_ack) != iGsendseq) { int inext; char *zsend; inext = INEXTSEQ (iGremote_ack); DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, "fgwait_for_packet: Resending packet %d", inext); ++cGresent_packets; zsend = zgadjust_ack (inext); if (! fsend_data (qdaemon->qconn, zsend, CFRAMELEN + CPACKLEN (zsend), TRUE)) return FALSE; iGretransmit_seq = inext; } else { /* Send all pending acks first, to avoid confusing the other side. */ if (iGlocal_ack != iGrecseq) { if (! fgsend_acks (qdaemon)) return FALSE; } if (! fgsend_control (qdaemon, RJ, iGrecseq)) return FALSE; } } } } /* Send acks for all packets we haven't acked yet. */ static boolean fgsend_acks (qdaemon) struct sdaemon *qdaemon; { while (iGlocal_ack != iGrecseq) { iGlocal_ack = INEXTSEQ (iGlocal_ack); if (! fgsend_control (qdaemon, RR, iGlocal_ack)) return FALSE; } return TRUE; } /* Handle an ack of a packet. According to Hanrahan's paper, this acknowledges all previous packets. If this is an ack for a retransmitted packet, continue by resending up to two more packets following the retransmitted one. This should recover quickly from a line glitch, while avoiding the problem of continual retransmission. */ static boolean fggot_ack (qdaemon, iack) struct sdaemon *qdaemon; int iack; { int inext; char *zsend; /* We only decrement the error level if we are not retransmitting packets. We want to catch a sudden downgrade in line quality as fast as possible. */ if (cGerror_level > 0 && iGretransmit_seq == -1 && cGsent_packets % cGerror_decay == 0) --cGerror_level; cGexpect_bad_order = 0; /* Each time packet 0 is acknowledged, we call uwindow_acked since a new window has been acked. */ if (iack < iGremote_ack) uwindow_acked (qdaemon, FALSE); iGremote_ack = iack; if (iGretransmit_seq == -1) return TRUE; inext = INEXTSEQ (iGretransmit_seq); if (inext == iGsendseq) iGretransmit_seq = -1; else { DEBUG_MESSAGE1 (DEBUG_PROTO, "fggot_ack: Sending packet %d", inext); ++cGresent_packets; zsend = zgadjust_ack (inext); if (! fsend_data (qdaemon->qconn, zsend, CFRAMELEN + CPACKLEN (zsend), TRUE)) return FALSE; inext = INEXTSEQ (inext); if (inext == iGsendseq) iGretransmit_seq = -1; else { DEBUG_MESSAGE1 (DEBUG_PROTO, "fggot_ack: Sending packet %d", inext); ++cGresent_packets; zsend = zgadjust_ack (inext); if (! fsend_data (qdaemon->qconn, zsend, CFRAMELEN + CPACKLEN (zsend), TRUE)) return FALSE; iGretransmit_seq = inext; } } return TRUE; } /* See if we've received more than the permitted number of errors. If we receive a bad packet, we can expect a window full (less one) of out of order packets to follow, so we discount cGbad_order accordingly. */ static boolean fgcheck_errors (qdaemon) struct sdaemon *qdaemon; { if (cGerror_level > cGmax_errors && cGmax_errors >= 0) { ulog (LOG_ERROR, "Too many '%c' protocol errors", qdaemon->qproto->bname); return FALSE; } return TRUE; } /* Process the receive buffer into a data packet, if possible. All control packets are handled here. When a data packet is received, fgprocess_data calls fgot_data with the data; if that sets its pfexit argument to TRUE fgprocess_data will set *pfexit to TRUE and return TRUE. Also, if the freturncontrol argument is TRUE fgprocess_data will set *pfexit to TRUE and return TRUE. Otherwise fgprocess_data will continue trying to process data. If some error occurs, fgprocess_data will return FALSE. If there is not enough data to form a complete packet, then *pfexit will be set to FALSE, *pcneed will be set to the number of bytes needed to form a complete packet (unless pcneed is NULL) and fgprocess_data will return TRUE. If this function found a data packet, and pffound is not NULL, it will set *pffound to TRUE; this can be used to tell valid data from an endless stream of garbage and control packets. If fdoacks is TRUE, received packets will be acknowledged; otherwise they must be acknowledged later. */ static boolean fgprocess_data (qdaemon, fdoacks, freturncontrol, pfexit, pcneed, pffound) struct sdaemon *qdaemon; boolean fdoacks; boolean freturncontrol; boolean *pfexit; size_t *pcneed; boolean *pffound; { *pfexit = FALSE; if (pffound != NULL) *pffound = FALSE; while (iPrecstart != iPrecend) { char ab[CFRAMELEN]; int i, iget, cwant; unsigned short ihdrcheck, idatcheck; const char *zfirst, *zsecond; int cfirst, csecond; boolean fduprr; /* Look for the DLE which must start a packet. */ if (abPrecbuf[iPrecstart] != DLE) { char *zdle; cfirst = iPrecend - iPrecstart; if (cfirst < 0) cfirst = CRECBUFLEN - iPrecstart; zdle = memchr (abPrecbuf + iPrecstart, DLE, (size_t) cfirst); if (zdle == NULL) { iPrecstart = (iPrecstart + cfirst) % CRECBUFLEN; continue; } /* We don't need % CRECBUFLEN here because zdle - (abPrecbuf + iPrecstart) < cfirst <= CRECBUFLEN - iPrecstart. */ iPrecstart += zdle - (abPrecbuf + iPrecstart); } /* Get the first six bytes into ab. */ for (i = 0, iget = iPrecstart; i < CFRAMELEN && iget != iPrecend; i++, iget = (iget + 1) % CRECBUFLEN) ab[i] = abPrecbuf[iget]; /* If there aren't six bytes, there is no packet. */ if (i < CFRAMELEN) { if (pcneed != NULL) *pcneed = CFRAMELEN - i; return TRUE; } /* Make sure these six bytes start a packet. The check on IFRAME_DLE is basically a debugging check, since the above code should have ensured that it will never fail. If this is not the start of a packet, bump iPrecstart and loop around to look for another DLE. */ if (ab[IFRAME_DLE] != DLE || ab[IFRAME_K] < 1 || ab[IFRAME_K] > 9 || ab[IFRAME_XOR] != (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW] ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL]) || CONTROL_TT (ab[IFRAME_CONTROL]) == ALTCHAN) { ++cGbad_hdr; ++cGerror_level; DEBUG_MESSAGE4 (DEBUG_PROTO | DEBUG_ABNORMAL, "fgprocess_data: Bad header: K %d TT %d XOR byte %d calc %d", ab[IFRAME_K] & 0xff, CONTROL_TT (ab[IFRAME_CONTROL]), ab[IFRAME_XOR] & 0xff, (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW] ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL]) & 0xff); if (! fgcheck_errors (qdaemon)) return FALSE; iPrecstart = (iPrecstart + 1) % CRECBUFLEN; continue; } /* The zfirst and cfirst pair point to the first set of data for this packet; the zsecond and csecond point to the second set, in case the packet wraps around the end of the buffer. */ zfirst = abPrecbuf + iPrecstart + CFRAMELEN; cfirst = 0; zsecond = NULL; csecond = 0; if (ab[IFRAME_K] == KCONTROL) { /* This is a control packet. It should not have any data. */ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL) { ++cGbad_hdr; ++cGerror_level; DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL, "fgprocess_data: Bad header: control packet with data"); if (! fgcheck_errors (qdaemon)) return FALSE; iPrecstart = (iPrecstart + 1) % CRECBUFLEN; continue; } idatcheck = (unsigned short) (0xaaaa - ab[IFRAME_CONTROL]); cwant = 0; } else { int cinbuf; unsigned short icheck; /* This is a data packet. It should not be type CONTROL. */ if (CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL) { ++cGbad_hdr; ++cGerror_level; DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL, "fgprocess_data: Bad header: data packet is type CONTROL"); if (! fgcheck_errors (qdaemon)) return FALSE; iPrecstart = (iPrecstart + 1) % CRECBUFLEN; continue; } cinbuf = iPrecend - iPrecstart; if (cinbuf < 0) cinbuf += CRECBUFLEN; cinbuf -= CFRAMELEN; /* Make sure we have enough data. If we don't, wait for more. */ cwant = (int) CPACKLEN (ab); if (cinbuf < cwant) { if (pcneed != NULL) *pcneed = cwant - cinbuf; return TRUE; } /* Set up the data pointers and compute the checksum. */ if (iPrecend >= iPrecstart) cfirst = cwant; else { cfirst = CRECBUFLEN - (iPrecstart + CFRAMELEN); if (cfirst >= cwant) cfirst = cwant; else if (cfirst > 0) { zsecond = abPrecbuf; csecond = cwant - cfirst; } else { /* Here cfirst is non-positive, so subtracting from abPrecbuf will actually skip the appropriate number of bytes at the start of abPrecbuf. */ zfirst = abPrecbuf - cfirst; cfirst = cwant; } } if (csecond == 0) icheck = (unsigned short) igchecksum (zfirst, (size_t) cfirst); else icheck = (unsigned short) igchecksum2 (zfirst, (size_t) cfirst, zsecond, (size_t) csecond); idatcheck = ((unsigned short) (((0xaaaa - (icheck ^ (ab[IFRAME_CONTROL] & 0xff))) & 0xffff))); } ihdrcheck = (unsigned short) (((ab[IFRAME_CHECKHIGH] & 0xff) << 8) | (ab[IFRAME_CHECKLOW] & 0xff)); if (ihdrcheck != idatcheck) { DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "fgprocess_data: Bad checksum: header 0x%x, data 0x%x", ihdrcheck, idatcheck); ++cGbad_checksum; ++cGerror_level; if (! fgcheck_errors (qdaemon)) return FALSE; /* If the checksum failed for a data packet, then if it was the one we were expecting send an RJ, otherwise ignore it. Previously if this code got the wrong packet number it would send an RR, but that may confuse some Telebit modems and it doesn't help in any case since the receiver will probably just ignore the RR as a duplicate (that's basically what this code does). If we totally missed the packet we will time out and send an RJ in the function fgwait_for_packet above. */ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL) { /* Make sure we've acked everything up to this point. */ if (iGrecseq != iGlocal_ack) { if (! fgsend_acks (qdaemon)) return FALSE; } /* If this is the packet we wanted, tell the sender that it failed. */ if (CONTROL_XXX (ab[IFRAME_CONTROL]) == INEXTSEQ (iGrecseq)) { if (! fgsend_control (qdaemon, RJ, iGrecseq)) return FALSE; cGexpect_bad_order += iGrequest_winsize - 1; } } /* We can't skip the packet data after this, because if we have lost incoming bytes the next DLE will be somewhere in what we thought was the packet data. */ iPrecstart = (iPrecstart + 1) % CRECBUFLEN; continue; } /* We have a packet; remove the processed bytes from the receive buffer. */ iPrecstart = (iPrecstart + cwant + CFRAMELEN) % CRECBUFLEN; /* Store the control byte for the handshake routines. */ iGpacket_control = ab[IFRAME_CONTROL] & 0xff; /* Annoyingly, some UUCP packages appear to send an RR packet rather than an RJ packet when they want a packet to be resent. If we get a duplicate RR and we've never seen an RJ, we treat the RR as an RJ. */ fduprr = FALSE; if (cGremote_rejects == 0 && CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL && CONTROL_XXX (ab[IFRAME_CONTROL]) == RR && iGremote_ack == CONTROL_YYY (ab[IFRAME_CONTROL]) && INEXTSEQ (iGremote_ack) != iGsendseq && iGretransmit_seq == -1) { DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL, "fgprocess_data: Treating duplicate RR as RJ"); fduprr = TRUE; } /* Update the received sequence number from the yyy field of a data packet (if it is the one we are expecting) or an RR control packet. If we've been delaying sending packets until we received an ack, this may send out some packets. */ if ((CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL && CONTROL_XXX (ab[IFRAME_CONTROL]) == INEXTSEQ (iGrecseq)) || (CONTROL_XXX (ab[IFRAME_CONTROL]) == RR && ! fduprr)) { if (! fggot_ack (qdaemon, CONTROL_YYY (ab[IFRAME_CONTROL]))) return FALSE; } /* If this isn't a control message, make sure we have received the expected packet sequence number, acknowledge the packet if it's the right one, and process the data. */ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL) { if (CONTROL_XXX (ab[IFRAME_CONTROL]) != INEXTSEQ (iGrecseq)) { /* We got the wrong packet number. */ DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "fgprocess_data: Got packet %d; expected %d", CONTROL_XXX (ab[IFRAME_CONTROL]), INEXTSEQ (iGrecseq)); if (cGexpect_bad_order > 0) --cGexpect_bad_order; else { ++cGbad_order; ++cGerror_level; if (! fgcheck_errors (qdaemon)) return FALSE; } /* This code used to send an RR to encourage the other side to get back in synch, but that may confuse some Telebit modems and does little good in any case, since the other side will probably just ignore it anyhow (that's what this code does). */ continue; } /* We got the packet we expected. */ ++cGrec_packets; if (cGerror_level > 0 && cGrec_packets % cGerror_decay == 0) --cGerror_level; cGexpect_bad_order = 0; iGrecseq = INEXTSEQ (iGrecseq); DEBUG_MESSAGE1 (DEBUG_PROTO, "fgprocess_data: Got packet %d", iGrecseq); /* Tell the caller that we found something. */ if (pffound != NULL) *pffound = TRUE; /* If we are supposed to do acknowledgements here, send back an RR packet. */ if (fdoacks) { if (! fgsend_acks (qdaemon)) return FALSE; } /* If this is a short data packet, adjust the data pointers and lengths. */ if (CONTROL_TT (ab[IFRAME_CONTROL]) == SHORTDATA) { int cshort, cmove; if ((zfirst[0] & 0x80) == 0) { cshort = zfirst[0] & 0xff; cmove = 1; } else { int cbyte2; if (cfirst > 1) cbyte2 = zfirst[1] & 0xff; else cbyte2 = zsecond[0] & 0xff; cshort = (zfirst[0] & 0x7f) + (cbyte2 << 7); cmove = 2; } DEBUG_MESSAGE1 (DEBUG_PROTO, "fgprocess_data: Packet short by %d", cshort); /* Adjust the start of the buffer for the bytes used by the count. */ if (cfirst > cmove) { zfirst += cmove; cfirst -= cmove; } else { zfirst = zsecond + (cmove - cfirst); cfirst = csecond - (cmove - cfirst); csecond = 0; } /* Adjust the length of the buffer for the bytes we are not supposed to consider. */ cshort -= cmove; if (csecond >= cshort) csecond -= cshort; else { cfirst -= cshort - csecond; csecond = 0; } #if DEBUG > 0 /* This should not happen, but just in case. */ if (cfirst < 0) cfirst = 0; #endif } if (! fgot_data (qdaemon, zfirst, (size_t) cfirst, zsecond, (size_t) csecond, -1, -1, (long) -1, INEXTSEQ (iGremote_ack) == iGsendseq, pfexit)) return FALSE; /* If fgot_data told us that we were finished, get out. */ if (*pfexit) return TRUE; /* If we've been asked to return control packets, get out now. */ if (freturncontrol) { *pfexit = TRUE; return TRUE; } continue; } /* Handle control messages here. */ #if DEBUG > 1 if (FDEBUGGING (DEBUG_PROTO) || (FDEBUGGING (DEBUG_ABNORMAL) && CONTROL_XXX (ab[IFRAME_CONTROL]) != RR)) ulog (LOG_DEBUG, "fgprocess_data: Got control %s %d", azGcontrol[CONTROL_XXX (ab[IFRAME_CONTROL])], CONTROL_YYY (ab[IFRAME_CONTROL])); #endif switch (CONTROL_XXX (ab[IFRAME_CONTROL])) { case CLOSE: /* The other side has closed the connection. */ if (fLog_sighup) { ulog (LOG_ERROR, "Received unexpected CLOSE packet"); (void) fgsend_control (qdaemon, CLOSE, 0); } return FALSE; case RR: /* Acknowledge receipt of a packet. This was already handled above, unless we are treating it as RJ. */ if (! fduprr) break; /* Fall through. */ case RJ: /* The other side dropped a packet. Begin retransmission with the packet following the one acknowledged. We don't retransmit the packets immediately, but instead wait for the first one to be acked. This prevents us from sending an entire window several times if we get several RJ packets. */ iGremote_ack = CONTROL_YYY (ab[IFRAME_CONTROL]); iGretransmit_seq = INEXTSEQ (iGremote_ack); if (iGretransmit_seq == iGsendseq) iGretransmit_seq = -1; else { char *zpack; DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "fgprocess_data: Remote reject: next %d resending %d", iGsendseq, iGretransmit_seq); ++cGresent_packets; if (fduprr) ++cGremote_duprrs; else ++cGremote_rejects; ++cGerror_level; if (! fgcheck_errors (qdaemon)) return FALSE; zpack = zgadjust_ack (iGretransmit_seq); if (! fsend_data (qdaemon->qconn, zpack, CFRAMELEN + CPACKLEN (zpack), TRUE)) return FALSE; } break; case SRJ: /* Selectively reject a particular packet. This is not used by UUCP, but it's easy to support. */ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, "fgprocess_data: Selective reject of %d", CONTROL_YYY (ab[IFRAME_CONTROL])); { char *zpack; ++cGresent_packets; ++cGremote_rejects; ++cGerror_level; zpack = zgadjust_ack (CONTROL_YYY (ab[IFRAME_CONTROL])); if (! fsend_data (qdaemon->qconn, zpack, CFRAMELEN + CPACKLEN (zpack), TRUE)) return FALSE; } break; case INITC: case INITB: case INITA: /* Ignore attempts to reinitialize. */ break; } /* If we've been asked to return control packets, get out. */ if (freturncontrol) { *pfexit = TRUE; return TRUE; } /* Loop around to look for the next packet, if any. */ } /* There is no data left in the receive buffer. */ if (pcneed != NULL) *pcneed = CFRAMELEN; return TRUE; } /* Compute the 'g' protocol checksum. This is unfortunately rather awkward. This is the most time consuming code in the entire program. It's also not a great checksum, since it can be fooled by some single bit errors. */ /* Sorry about this knavery, but it speeds up the VAX code significantly. It would be better to rewrite the whole routine in assembler. */ #ifdef __GNUC__ #ifdef __vax__ #define VAX_ASM 1 #endif #endif #if VAX_ASM #define ROTATE(i) \ asm ("cvtwl %1,%0\n\trotl $1,%0,%0" : "=g" (i) : "g" (i)) #else #define ROTATE(i) i += i + ((i & 0x8000) >> 15) #endif #define ITERATION \ /* Rotate ichk1 left. */ \ ROTATE (ichk1); \ \ /* The guts of the checksum. */ \ b = BUCHAR (*z++); \ if (b != 0) \ { \ ichk1 &= 0xffff; \ ichk1 += b; \ ichk2 += ichk1 ^ c; \ if ((ichk1 >> 16) != 0) \ ichk1 ^= ichk2; \ } \ else \ { \ ichk2 += ichk1 ^ c; \ ichk1 ^= ichk2; \ } \ \ --c static int igchecksum (z, c) register const char *z; register size_t c; { register unsigned long ichk1, ichk2; ichk1 = 0xffff; ichk2 = 0; do { register unsigned int b; ITERATION; ITERATION; ITERATION; ITERATION; } while (c > 0); return ichk1 & 0xffff; } /* We use a separate function compute the checksum if the block is split around the end of the receive buffer since it occurs much less frequently and the checksum is already high up in the profiles. These functions are almost identical, and this one actually only has a few more instructions in the inner loop. */ static int igchecksum2 (zfirst, cfirst, zsecond, csecond) const char *zfirst; size_t cfirst; const char *zsecond; size_t csecond; { register unsigned long ichk1, ichk2; register const char *z; register size_t c; z = zfirst; c = cfirst + csecond; ichk1 = 0xffff; ichk2 = 0; do { register unsigned int b; ITERATION; /* If the first buffer has been finished, switch to the second. */ --cfirst; if (cfirst == 0) z = zsecond; } while (c > 0); return ichk1 & 0xffff; } uucp-1.07/protf.c0000664000076400007640000005044007665321755007431 /* protf.c The 'f' protocol. Copyright (C) 1991, 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char protf_rcsid[] = "$Id: protf.c,v 1.36 2002/03/05 19:10:41 ian Rel $"; #endif #include #include #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "trans.h" #include "system.h" #include "prot.h" /* This implementation is based on code by Piet Beertema, CWI, Amsterdam, Sep 1984. This code implements the 'f' protocol, which requires a flow-controlled error-free seven-bit data path. It does check for errors, but only at the end of each file transmission, so a noisy line without error correcting modems will be unusable. The conversion to seven bit data is done as follows, where b represents the character to convert: 0 <= b <= 037: 0172, b + 0100 (0100 to 0137) 040 <= b <= 0171: b ( 040 to 0171) 0172 <= b <= 0177: 0173, b - 0100 ( 072 to 077) 0200 <= b <= 0237: 0174, b - 0100 (0100 to 0137) 0240 <= b <= 0371: 0175, b - 0200 ( 040 to 0171) 0372 <= b <= 0377: 0176, b - 0300 ( 072 to 077) This causes all output bytes to be in the range 040 to 0176; these are the printable ASCII characters. */ /* This structure is used to hold information when dealing with the end of file acknowledgement. */ struct sfinfo { /* The functions from the generic code. */ boolean (*psendfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon)); boolean (*precfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon, const char *zdata, size_t cdata)); /* The info pointer from the generic code. */ pointer pinfo; /* The character to send after receiving the checksum. */ char bsend; }; /* Internal functions. */ static boolean ffprocess_data P((struct sdaemon *qdaemon, boolean *pfexit, size_t *pcneed)); static boolean ffawait_ack P((struct stransfer *qtrans, struct sdaemon *qdaemon, const char *zdata, size_t cdata)); static boolean ffawait_cksum P((struct stransfer *qtrans, struct sdaemon *qdaemon, const char *zdata, size_t cdata)); static boolean ffsend_ack P((struct stransfer *qtrans, struct sdaemon *qdaemon)); /* The size of the buffer we allocate to store outgoing data in. */ #define CFBUFSIZE (256) /* The timeout to wait for data to arrive before giving up. */ static int cFtimeout = 120; /* The maximum number of retries. */ static int cFmaxretries = 2; /* The buffer we allocate for outgoing data. */ static char *zFbuf; /* TRUE if we are receiving a file rather than a command. */ static boolean fFfile; /* The checksum so far. */ static unsigned int iFcheck; /* The last special byte (0172 to 0176) or 0 if none. */ static char bFspecial; /* The number of times we have retried this file. */ static int cFretries; /* Whether this file has been acknowledged. */ static boolean fFacked; struct uuconf_cmdtab asFproto_params[] = { { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cFtimeout, NULL }, { "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cFmaxretries, NULL }, { NULL, 0, NULL, NULL } }; /* Statistics. */ /* The number of data bytes sent in files. */ static long cFsent_data; /* The number of actual bytes sent in files. */ static long cFsent_bytes; /* The number of data bytes received in files. */ static long cFrec_data; /* The number of actual bytes received in files. */ static long cFrec_bytes; /* The number of file retries when sending. */ static long cFsend_retries; /* The number of file retries when receiving. */ static long cFrec_retries; /* Start the protocol. */ boolean ffstart (qdaemon, pzlog) struct sdaemon *qdaemon; char **pzlog; { *pzlog = NULL; cFsent_data = 0; cFsent_bytes = 0; cFrec_data = 0; cFrec_bytes = 0; cFsend_retries = 0; cFrec_retries = 0; /* Use XON/XOFF handshaking. */ if (! fconn_set (qdaemon->qconn, PARITYSETTING_DEFAULT, STRIPSETTING_SEVENBITS, XONXOFF_ON)) return FALSE; /* We sleep to allow the other side to reset the terminal; this is what Mr. Beertema's code does. */ usysdep_sleep (2); return TRUE; } /* Shutdown the protocol. */ /*ARGSIGNORED*/ boolean ffshutdown (qdaemon) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; { xfree ((pointer) zFbuf); zFbuf = NULL; ulog (LOG_NORMAL, "Protocol 'f': sent %ld bytes for %ld, received %ld bytes for %ld", cFsent_bytes, cFsent_data, cFrec_bytes, cFrec_data); if (cFsend_retries != 0 || cFrec_retries != 0) ulog (LOG_NORMAL, "Protocol 'f' file retries: %ld sending, %ld receiving", cFsend_retries, cFrec_retries); cFtimeout = 120; cFmaxretries = 2; return TRUE; } /* Send a command string. We just send the string followed by a carriage return. */ /*ARGSUSED*/ boolean ffsendcmd (qdaemon, z, ilocal, iremote) struct sdaemon *qdaemon; const char *z; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; { size_t clen; char *zalc; boolean fret; DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "ffsendcmd: Sending command \"%s\"", z); clen = strlen (z); zalc = zbufalc (clen + 2); memcpy (zalc, z, clen); zalc[clen] = '\r'; zalc[clen + 1] = '\0'; fret = fsend_data (qdaemon->qconn, zalc, clen + 1, TRUE); ubuffree (zalc); return fret; } /* Get space to be filled with data. We allocate the space from the heap. */ /*ARGSIGNORED*/ char * zfgetspace (qdaemon, pclen) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; size_t *pclen; { *pclen = CFBUFSIZE; if (zFbuf == NULL) zFbuf = (char *) xmalloc (CFBUFSIZE); return zFbuf; } /* Send out a data packet. We have to encode the data into seven bits and accumulate a checksum. */ /*ARGSIGNORED*/ boolean ffsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) struct sdaemon *qdaemon; char *zdata; size_t cdata; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; long ipos ATTRIBUTE_UNUSED; { char ab[CFBUFSIZE * 2]; char *ze; register unsigned int itmpchk; cFsent_data += cdata; ze = ab; itmpchk = iFcheck; while (cdata-- > 0) { register int b; /* Rotate the checksum left. */ if ((itmpchk & 0x8000) == 0) itmpchk <<= 1; else { itmpchk <<= 1; ++itmpchk; } /* Add the next byte into the checksum. */ b = *zdata++ & 0xff; itmpchk += b; /* Encode the byte. */ if (b <= 0177) { if (b <= 037) { *ze++ = '\172'; *ze++ = (char) (b + 0100); } else if (b <= 0171) *ze++ = (char) b; else { *ze++ = '\173'; *ze++ = (char) (b - 0100); } } else { if (b <= 0237) { *ze++ = '\174'; *ze++ = (char) (b - 0100); } else if (b <= 0371) { *ze++ = '\175'; *ze++ = (char) (b - 0200); } else { *ze++ = '\176'; *ze++ = (char) (b - 0300); } } } iFcheck = itmpchk; cFsent_bytes += ze - ab; /* Passing FALSE tells fsend_data not to bother looking for incoming information, since we really don't expect any. */ return fsend_data (qdaemon->qconn, ab, (size_t) (ze - ab), FALSE); } /* Process data and return the amount of data we are looking for in *pcneed. The 'f' protocol doesn't really reveal this, but when transferring file we know that we need at least seven characters for the checksum. */ static boolean ffprocess_data (qdaemon, pfexit, pcneed) struct sdaemon *qdaemon; boolean *pfexit; size_t *pcneed; { int i; register unsigned int itmpchk; *pfexit = FALSE; if (pcneed != NULL) *pcneed = 1; if (! fFfile) { /* A command continues until a '\r' character, which we turn into '\0' before calling fgot_data. */ while (iPrecstart != iPrecend) { for (i = iPrecstart; i < CRECBUFLEN && i != iPrecend; i++) { /* Some systems seem to send characters with parity, so strip the parity bit. */ abPrecbuf[i] &= 0x7f; if (abPrecbuf[i] == '\r') { int istart; DEBUG_MESSAGE1 (DEBUG_PROTO, "ffprocess_data: Got %d command bytes", i - iPrecstart + 1); abPrecbuf[i] = '\0'; istart = iPrecstart; iPrecstart = (i + 1) % CRECBUFLEN; if (pcneed != NULL) *pcneed = 0; return fgot_data (qdaemon, abPrecbuf + istart, (size_t) (i - istart + 1), (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, pfexit); } } DEBUG_MESSAGE1 (DEBUG_PROTO, "ffprocess_data: Got %d command bytes", i - iPrecstart); if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, (size_t) (i - iPrecstart), (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, pfexit)) return FALSE; iPrecstart = i % CRECBUFLEN; } return TRUE; } /* Here the data is destined for a file, and we must decode it. */ itmpchk = iFcheck; while (iPrecstart != iPrecend) { char *zstart, *zto, *zfrom; int c; zto = zfrom = zstart = abPrecbuf + iPrecstart; c = iPrecend - iPrecstart; if (c < 0) c = CRECBUFLEN - iPrecstart; while (c-- != 0) { int b; /* Some systems seem to send characters with parity, so strip the parity bit. */ b = *zfrom++ & 0x7f; if (b < 040 || b > 0176) { ulog (LOG_ERROR, "Illegal byte %d", b); continue; } /* Characters >= 0172 are always special characters. The only legal pair of consecutive special characters are 0176 0176 which immediately precede the four digit checksum. */ if (b >= 0172) { if (bFspecial != 0) { if (bFspecial != 0176 || b != 0176) { ulog (LOG_ERROR, "Illegal bytes %d %d", bFspecial, b); bFspecial = 0; continue; } /* Pass any initial data. */ if (zto != zstart) { /* Don't count the checksum in the received bytes. */ cFrec_bytes += zfrom - zstart - 2; cFrec_data += zto - zstart; if (! fgot_data (qdaemon, zstart, (size_t) (zto - zstart), (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, pfexit)) return FALSE; } /* The next characters we want to read are the checksum, so skip the second 0176. */ iPrecstart = (iPrecstart + zfrom - zstart) % CRECBUFLEN; iFcheck = itmpchk; /* Tell fgot_data that we've read the entire file by passing 0 length data. This will wind up calling fffile to verify the checksum. We set *pcneed to 0 because we don't want to read any more data from the port, since we may have already read the checksum. */ if (pcneed != NULL) *pcneed = 0; return fgot_data (qdaemon, (const char *) NULL, (size_t) 0, (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, pfexit); } /* Here we have encountered a special character that does not follow another special character. */ bFspecial = (char) b; } else { int bnext; /* Here we have encountered a nonspecial character. */ switch (bFspecial) { default: bnext = b; break; case 0172: bnext = b - 0100; break; case 0173: case 0174: bnext = b + 0100; break; case 0175: bnext = b + 0200; break; case 0176: bnext = b + 0300; break; } *zto++ = (char) bnext; bFspecial = 0; /* Rotate the checksum left. */ if ((itmpchk & 0x8000) == 0) itmpchk <<= 1; else { itmpchk <<= 1; ++itmpchk; } /* Add the next byte into the checksum. */ itmpchk += bnext; } } if (zto != zstart) { DEBUG_MESSAGE1 (DEBUG_PROTO, "ffprocess_data: Got %d bytes", zto - zstart); cFrec_data += zto - zstart; if (! fgot_data (qdaemon, zstart, (size_t) (zto - zstart), (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, pfexit)) return FALSE; } cFrec_bytes += zfrom - zstart; iPrecstart = (iPrecstart + zfrom - zstart) % CRECBUFLEN; } iFcheck = itmpchk; if (pcneed != NULL) { /* At this point we may have seen the first 0176 in the checksum but not the second. The checksum is at least seven characters long (0176 0176 a b c d \r). This won't help much, but reading seven characters is a lot better than reading two, which is what I saw in a 2400 baud log file. */ if (bFspecial == 0176) *pcneed = 6; else *pcneed = 7; } return TRUE; } /* Wait for data to come in and process it until we've finished a command or a file. */ boolean ffwait (qdaemon) struct sdaemon *qdaemon; { while (TRUE) { boolean fexit; size_t cneed, crec; if (! ffprocess_data (qdaemon, &fexit, &cneed)) return FALSE; if (fexit) return TRUE; if (cneed > 0) { /* We really want to do something like get all available characters, then sleep for half a second and get all available characters again, and keep this up until we don't get anything after sleeping. */ if (! freceive_data (qdaemon->qconn, cneed, &crec, cFtimeout, TRUE)) return FALSE; if (crec == 0) { ulog (LOG_ERROR, "Timed out waiting for data"); return FALSE; } } } } /* File level operations. Reset the checksums when starting to send or receive a file, and output the checksum when we've finished sending a file. */ /*ARGSUSED*/ boolean fffile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled) struct sdaemon *qdaemon; struct stransfer *qtrans; boolean fstart; boolean fsend; long cbytes ATTRIBUTE_UNUSED; boolean *pfhandled; { DEBUG_MESSAGE3 (DEBUG_PROTO, "fffile: fstart %s; fsend %s; fFacked %s", fstart ? "true" : "false", fsend ? "true" : "false", fFacked ? "true" : "false"); *pfhandled = FALSE; if (fstart) { iFcheck = 0xffff; cFretries = 0; fFacked = FALSE; if (! fsend) { bFspecial = 0; fFfile = TRUE; } return TRUE; } else { struct sfinfo *qinfo; /* We need to handle the checksum and the acknowledgement. If we get a successful ACK, we set fFacked to TRUE and call the send or receive function by hand. This will wind up calling here again, so if fFacked is TRUE we just return out and let the send or receive function do whatever it does. This is a bit of a hack. */ if (fFacked) { fFacked = FALSE; return TRUE; } if (fsend) { char ab[sizeof "\176\176ABCD\r"]; /* Send the final checksum. */ sprintf (ab, "\176\176%04x\r", iFcheck & 0xffff); if (! fsend_data (qdaemon->qconn, ab, (size_t) 7, TRUE)) return FALSE; /* Now wait for the acknowledgement. */ fFfile = FALSE; qinfo = (struct sfinfo *) xmalloc (sizeof (struct sfinfo)); qinfo->psendfn = qtrans->psendfn; qinfo->precfn = qtrans->precfn; qinfo->pinfo = qtrans->pinfo; qtrans->psendfn = NULL; qtrans->precfn = ffawait_ack; qtrans->pinfo = (pointer) qinfo; qtrans->fcmd = TRUE; *pfhandled = TRUE; return fqueue_receive (qdaemon, qtrans); } else { /* Wait for the checksum. */ fFfile = FALSE; qinfo = (struct sfinfo *) xmalloc (sizeof (struct sfinfo)); qinfo->psendfn = qtrans->psendfn; qinfo->precfn = qtrans->precfn; qinfo->pinfo = qtrans->pinfo; qtrans->psendfn = NULL; qtrans->precfn = ffawait_cksum; qtrans->pinfo = (pointer) qinfo; qtrans->fcmd = TRUE; *pfhandled = TRUE; return fqueue_receive (qdaemon, qtrans); } } } /* Wait for the ack after sending a file and the checksum. */ static boolean ffawait_ack (qtrans, qdaemon, zdata, cdata) struct stransfer *qtrans; struct sdaemon *qdaemon; const char *zdata; size_t cdata ATTRIBUTE_UNUSED; { struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo; qtrans->precfn = NULL; /* An R means to retry sending the file. */ if (*zdata == 'R') { if (! ffileisopen (qtrans->e)) { ulog (LOG_ERROR, "Request to resent non-file"); return FALSE; } ++cFretries; if (cFretries > cFmaxretries) { ulog (LOG_ERROR, "Too many retries"); return FALSE; } ulog (LOG_NORMAL, "Resending file"); if (! ffilerewind (qtrans->e)) { ulog (LOG_ERROR, "rewind: %s", strerror (errno)); return FALSE; } qtrans->ipos = (long) 0; iFcheck = 0xffff; ++cFsend_retries; qtrans->psendfn = qinfo->psendfn; qtrans->precfn = qinfo->precfn; qtrans->pinfo = qinfo->pinfo; xfree ((pointer) qinfo); qtrans->fsendfile = TRUE; return fqueue_send (qdaemon, qtrans); } if (*zdata != 'G') { DEBUG_MESSAGE1 (DEBUG_PROTO, "fffile: Got \"%s\"", zdata); ulog (LOG_ERROR, "File send failed"); return FALSE; } qtrans->psendfn = qinfo->psendfn; qtrans->precfn = qinfo->precfn; qtrans->pinfo = qinfo->pinfo; xfree ((pointer) qinfo); /* Now call the send function by hand after setting fFacked to TRUE. Since fFacked is true fffile will simply return out, and the send function can do whatever it what was going to do. */ fFacked = TRUE; return (*qtrans->psendfn) (qtrans, qdaemon); } /* This function is called when the checksum arrives. */ /*ARGSUSED*/ static boolean ffawait_cksum (qtrans, qdaemon, zdata, cdata) struct stransfer *qtrans; struct sdaemon *qdaemon; const char *zdata; size_t cdata ATTRIBUTE_UNUSED; { struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo; unsigned int icheck; qtrans->precfn = NULL; if (! isxdigit (zdata[0]) || ! isxdigit (zdata[1]) || ! isxdigit (zdata[2]) || ! isxdigit (zdata[3]) || zdata[4] != '\0') { ulog (LOG_ERROR, "Bad checksum format"); xfree (qtrans->pinfo); return FALSE; } icheck = (unsigned int) strtol ((char *) zdata, (char **) NULL, 16); if (icheck != (iFcheck & 0xffff)) { DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "Checksum failed; calculated 0x%x, got 0x%x", iFcheck & 0xffff, icheck); if (! ffileisopen (qtrans->e)) { ulog (LOG_ERROR, "Failed to get non-file"); return FALSE; } ++cFretries; if (cFretries > cFmaxretries) { ulog (LOG_ERROR, "Too many retries"); qinfo->bsend = 'Q'; } else { ulog (LOG_NORMAL, "File being resent"); /* This bit of code relies on the receive code setting qtrans->s.ztemp to the full name of the temporary file being used. */ qtrans->e = esysdep_truncate (qtrans->e, qtrans->s.ztemp); if (! ffileisopen (qtrans->e)) return FALSE; qtrans->ipos = (long) 0; iFcheck = 0xffff; bFspecial = 0; fFfile = TRUE; ++cFrec_retries; /* Send an R to tell the other side to resend the file. */ qinfo->bsend = 'R'; } } else { /* Send a G to tell the other side the file was received correctly. */ qinfo->bsend = 'G'; } qtrans->psendfn = ffsend_ack; return fqueue_send (qdaemon, qtrans); } /* Send the acknowledgement, and then possible wait for the resent file. */ static boolean ffsend_ack (qtrans, qdaemon) struct stransfer *qtrans; struct sdaemon *qdaemon; { struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo; char ab[2]; ab[0] = qinfo->bsend; ab[1] = '\0'; if (! ffsendcmd (qdaemon, ab, 0, 0)) return FALSE; qtrans->psendfn = qinfo->psendfn; qtrans->precfn = qinfo->precfn; qtrans->pinfo = qinfo->pinfo; xfree ((pointer) qinfo); if (ab[0] == 'Q') return FALSE; if (ab[0] == 'R') { qtrans->frecfile = TRUE; return fqueue_receive (qdaemon, qtrans); } fFacked = TRUE; return (*qtrans->precfn) (qtrans, qdaemon, (const char *) NULL, (size_t) 0); } uucp-1.07/prott.c0000664000076400007640000002027107665321755007446 /* prott.c The 't' protocol. Copyright (C) 1991, 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char prott_rcsid[] = "$Id: prott.c,v 1.32 2002/03/05 19:10:41 ian Rel $"; #endif #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "trans.h" #include "system.h" #include "prot.h" /* This implementation is based on code written by Rick Adams. This code implements the 't' protocol, which does no error checking whatsoever and thus requires an end-to-end verified eight bit communication line, such as is provided by TCP. Using it with a modem is unadvisable, since errors can occur between the modem and the computer. */ /* The buffer size we use. */ #define CTBUFSIZE (1024) /* The offset in the buffer to the data. */ #define CTFRAMELEN (4) /* Commands are sent in multiples of this size. */ #define CTPACKSIZE (512) /* A pointer to the buffer we will use. */ static char *zTbuf; /* True if we are receiving a file. */ static boolean fTfile; /* The timeout we use. */ static int cTtimeout = 120; struct uuconf_cmdtab asTproto_params[] = { { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cTtimeout, NULL }, { NULL, 0, NULL, NULL } }; /* Local function. */ static boolean ftprocess_data P((struct sdaemon *qdaemon, boolean *pfexit, size_t *pcneed)); /* Start the protocol. */ boolean ftstart (qdaemon, pzlog) struct sdaemon *qdaemon; char **pzlog; { *pzlog = NULL; if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, STRIPSETTING_EIGHTBITS, XONXOFF_OFF)) return FALSE; zTbuf = (char *) xmalloc (CTBUFSIZE + CTFRAMELEN); /* The first two bytes of the buffer are always zero. */ zTbuf[0] = 0; zTbuf[1] = 0; fTfile = FALSE; usysdep_sleep (2); return TRUE; } /* Stop the protocol. */ /*ARGSUSED*/ boolean ftshutdown (qdaemon) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; { xfree ((pointer) zTbuf); zTbuf = NULL; cTtimeout = 120; return TRUE; } /* Send a command string. We send everything up to and including the null byte. The number of bytes we send must be a multiple of TPACKSIZE. */ /*ARGSUSED*/ boolean ftsendcmd (qdaemon, z, ilocal, iremote) struct sdaemon *qdaemon; const char *z; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; { size_t clen, csend; char *zalc; boolean fret; DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "ftsendcmd: Sending command \"%s\"", z); clen = strlen (z); /* We need to send the smallest multiple of CTPACKSIZE which is greater than clen (not equal to clen, since we need room for the null byte). */ csend = ((clen / CTPACKSIZE) + 1) * CTPACKSIZE; zalc = zbufalc (csend); memcpy (zalc, z, clen); if (csend > clen) bzero (zalc + clen, csend - clen); fret = fsend_data (qdaemon->qconn, zalc, csend, TRUE); ubuffree (zalc); return fret; } /* Get space to be filled with data. We provide a buffer which has four bytes at the start available to hold the length. */ /*ARGSIGNORED*/ char * ztgetspace (qdaemon, pclen) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; size_t *pclen; { *pclen = CTBUFSIZE; return zTbuf + CTFRAMELEN; } /* Send out some data. We are allowed to modify the four bytes preceding the buffer. This allows us to send the entire block with header bytes in a single call. */ /*ARGSIGNORED*/ boolean ftsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) struct sdaemon *qdaemon; char *zdata; size_t cdata; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; long ipos ATTRIBUTE_UNUSED; { /* Here we do htonl by hand, since it doesn't exist everywhere. We know that the amount of data cannot be greater than CTBUFSIZE, so the first two bytes of this value will always be 0. They were set to 0 in ftstart so we don't touch them here. This is useful because we cannot portably right shift by 24 or 16, since we might be dealing with sixteen bit integers. */ zdata[-2] = (char) ((cdata >> 8) & 0xff); zdata[-1] = (char) (cdata & 0xff); /* We pass FALSE to fsend_data since we don't expect the other side to be sending us anything just now. */ return fsend_data (qdaemon->qconn, zdata - CTFRAMELEN, cdata + CTFRAMELEN, FALSE); } /* Process data and return the amount we need in *pfneed. */ static boolean ftprocess_data (qdaemon, pfexit, pcneed) struct sdaemon *qdaemon; boolean *pfexit; size_t *pcneed; { int cinbuf, cfirst, clen; *pfexit = FALSE; cinbuf = iPrecend - iPrecstart; if (cinbuf < 0) cinbuf += CRECBUFLEN; if (! fTfile) { /* We are not receiving a file. Commands are read in chunks of CTPACKSIZE. */ while (cinbuf >= CTPACKSIZE) { cfirst = CRECBUFLEN - iPrecstart; if (cfirst > CTPACKSIZE) cfirst = CTPACKSIZE; DEBUG_MESSAGE1 (DEBUG_PROTO, "ftprocess_data: Got %d command bytes", cfirst); if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, (size_t) cfirst, abPrecbuf, (size_t) CTPACKSIZE - cfirst, -1, -1, (long) -1, TRUE, pfexit)) return FALSE; iPrecstart = (iPrecstart + CTPACKSIZE) % CRECBUFLEN; if (*pfexit) return TRUE; cinbuf -= CTPACKSIZE; } if (pcneed != NULL) *pcneed = CTPACKSIZE - cinbuf; return TRUE; } /* Here we are receiving a file. The data comes in blocks. The first four bytes contain the length, followed by that amount of data. */ while (cinbuf >= CTFRAMELEN) { /* The length is stored in network byte order, MSB first. */ clen = (((((((abPrecbuf[iPrecstart] & 0xff) << 8) + (abPrecbuf[(iPrecstart + 1) % CRECBUFLEN] & 0xff)) << 8) + (abPrecbuf[(iPrecstart + 2) % CRECBUFLEN] & 0xff)) << 8) + (abPrecbuf[(iPrecstart + 3) % CRECBUFLEN] & 0xff)); if (cinbuf < clen + CTFRAMELEN) { if (pcneed != NULL) *pcneed = clen + CTFRAMELEN - cinbuf; return TRUE; } iPrecstart = (iPrecstart + CTFRAMELEN) % CRECBUFLEN; cfirst = CRECBUFLEN - iPrecstart; if (cfirst > clen) cfirst = clen; DEBUG_MESSAGE1 (DEBUG_PROTO, "ftprocess_data: Got %d data bytes", clen); if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, (size_t) cfirst, abPrecbuf, (size_t) (clen - cfirst), -1, -1, (long) -1, TRUE, pfexit)) return FALSE; iPrecstart = (iPrecstart + clen) % CRECBUFLEN; if (*pfexit) return TRUE; cinbuf -= clen + CTFRAMELEN; } if (pcneed != NULL) *pcneed = CTFRAMELEN - cinbuf; return TRUE; } /* Wait for data to come in and process it until we've reached the end of a command or a file. */ boolean ftwait (qdaemon) struct sdaemon *qdaemon; { while (TRUE) { boolean fexit; size_t cneed, crec; if (! ftprocess_data (qdaemon, &fexit, &cneed)) return FALSE; if (fexit) return TRUE; if (! freceive_data (qdaemon->qconn, cneed, &crec, cTtimeout, TRUE)) return FALSE; if (crec == 0) { ulog (LOG_ERROR, "Timed out waiting for data"); return FALSE; } } } /* File level routine, to set fTfile correctly. */ /*ARGSUSED*/ boolean ftfile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; struct stransfer *qtrans ATTRIBUTE_UNUSED; boolean fstart; boolean fsend; long cbytes ATTRIBUTE_UNUSED; boolean *pfhandled; { *pfhandled = FALSE; if (! fsend) fTfile = fstart; return TRUE; } uucp-1.07/prote.c0000664000076400007640000002205507665321755007431 /* prote.c The 'e' protocol. Copyright (C) 1991, 1992, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char prote_rcsid[] = "$Id: prote.c,v 1.23 2002/03/05 19:10:41 ian Rel $"; #endif #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "trans.h" #include "system.h" #include "prot.h" /* This implementation is based on my implementation of the 't' protocol, which is fairly similar. The main difference between the protocols seems to be that 't' breaks the file into packets and transmits the size of the packet with each packet, whereas 'e' sends the size of the entire file and then sends all the data in a single enormous packet. The 'e' protocol does no error checking whatsoever and thus requires an end-to-end verified eight bit communication line, such as is provided by TCP. Using it with a modem is inadvisable, since errors can occur between the modem and the computer. */ /* The buffer size we use. */ #define CEBUFSIZE (CRECBUFLEN / 2) /* The size of the initial file size message. */ #define CEFRAMELEN (20) /* A pointer to the buffer we will use. */ static char *zEbuf; /* True if we are receiving a file. */ static boolean fEfile; /* The number of bytes we have left to send or receive. */ static long cEbytes; /* The timeout we use. */ static int cEtimeout = 120; struct uuconf_cmdtab asEproto_params[] = { { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cEtimeout, NULL }, { NULL, 0, NULL, NULL } }; /* Local function. */ static boolean feprocess_data P((struct sdaemon *qdaemon, boolean *pfexit, size_t *pcneed)); /* Start the protocol. */ boolean festart (qdaemon, pzlog) struct sdaemon *qdaemon; char **pzlog; { *pzlog = NULL; if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, STRIPSETTING_EIGHTBITS, XONXOFF_OFF)) return FALSE; zEbuf = (char *) xmalloc (CEBUFSIZE); fEfile = FALSE; usysdep_sleep (2); return TRUE; } /* Stop the protocol. */ /*ARGSUSED*/ boolean feshutdown (qdaemon) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; { xfree ((pointer) zEbuf); zEbuf = NULL; cEtimeout = 120; return TRUE; } /* Send a command string. We send everything up to and including the null byte. */ /*ARGSUSED*/ boolean fesendcmd (qdaemon, z, ilocal, iremote) struct sdaemon *qdaemon; const char *z; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; { DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fesendcmd: Sending command \"%s\"", z); return fsend_data (qdaemon->qconn, z, strlen (z) + 1, TRUE); } /* Get space to be filled with data. We provide a buffer which has 20 bytes at the start available to hold the length. */ /*ARGSUSED*/ char * zegetspace (qdaemon, pclen) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; size_t *pclen; { *pclen = CEBUFSIZE; return zEbuf; } /* Send out some data. We are allowed to modify the 20 bytes preceding the buffer. This allows us to send the entire block with header bytes in a single call. */ /*ARGSIGNORED*/ boolean fesenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) struct sdaemon *qdaemon; char *zdata; size_t cdata; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; long ipos ATTRIBUTE_UNUSED; { #if DEBUG > 0 /* Keep track of the number of bytes we send out to make sure it all adds up. */ cEbytes -= cdata; if (cEbytes < 0) { ulog (LOG_ERROR, "Protocol 'e' internal error"); return FALSE; } #endif /* We pass FALSE to fsend_data since we don't expect the other side to be sending us anything just now. */ return fsend_data (qdaemon->qconn, zdata, cdata, FALSE); } /* Process data and return the amount we need in *pfneed. */ static boolean feprocess_data (qdaemon, pfexit, pcneed) struct sdaemon *qdaemon; boolean *pfexit; size_t *pcneed; { int cinbuf, cfirst, clen; *pfexit = FALSE; cinbuf = iPrecend - iPrecstart; if (cinbuf < 0) cinbuf += CRECBUFLEN; if (! fEfile) { /* We are not receiving a file. Commands continue up to a null byte. */ while (cinbuf > 0) { char *pnull; cfirst = CRECBUFLEN - iPrecstart; if (cfirst > cinbuf) cfirst = cinbuf; pnull = memchr (abPrecbuf + iPrecstart, '\0', (size_t) cfirst); if (pnull != NULL) cfirst = pnull - (abPrecbuf + iPrecstart) + 1; DEBUG_MESSAGE1 (DEBUG_PROTO, "feprocess_data: Got %d command bytes", cfirst); if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, (size_t) cfirst, (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, pfexit)) return FALSE; iPrecstart = (iPrecstart + cfirst) % CRECBUFLEN; if (*pfexit) return TRUE; cinbuf = iPrecend - iPrecstart; if (cinbuf < 0) cinbuf += CRECBUFLEN; } if (pcneed != NULL) *pcneed = 1; return TRUE; } /* Here we are receiving a file. We want cEbytes in total. If we don't have cEbytes yet, we have to get it first. */ if (cEbytes == -1) { char ab[CEFRAMELEN + 1]; if (cinbuf < CEFRAMELEN) { if (pcneed != NULL) *pcneed = CEFRAMELEN - cinbuf; return TRUE; } cfirst = CRECBUFLEN - iPrecstart; if (cfirst >= CEFRAMELEN) memcpy (ab, abPrecbuf + iPrecstart, (size_t) CEFRAMELEN); else { memcpy (ab, abPrecbuf + iPrecstart, (size_t) cfirst); memcpy (ab + cfirst, abPrecbuf, (size_t) CEFRAMELEN - cfirst); } ab[CEFRAMELEN] = '\0'; cEbytes = strtol (ab, (char **) NULL, 10); iPrecstart = (iPrecstart + CEFRAMELEN) % CRECBUFLEN; cinbuf = iPrecend - iPrecstart; if (cinbuf < 0) cinbuf += CRECBUFLEN; if (cEbytes == 0) { if (! fgot_data (qdaemon, abPrecbuf, (size_t) 0, (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, pfexit)) return FALSE; if (*pfexit) return TRUE; } } /* Here we can read real data for the file. */ while (cinbuf > 0) { clen = cinbuf; if ((long) clen > cEbytes) clen = (int) cEbytes; cfirst = CRECBUFLEN - iPrecstart; if (cfirst > clen) cfirst = clen; DEBUG_MESSAGE1 (DEBUG_PROTO, "feprocess_data: Got %d data bytes", clen); if (! fgot_data (qdaemon, abPrecbuf + iPrecstart, (size_t) cfirst, abPrecbuf, (size_t) (clen - cfirst), -1, -1, (long) -1, TRUE, pfexit)) return FALSE; iPrecstart = (iPrecstart + clen) % CRECBUFLEN; cEbytes -= clen; if (cEbytes == 0) { if (! fgot_data (qdaemon, abPrecbuf, (size_t) 0, (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, pfexit)) return FALSE; if (*pfexit) return TRUE; } cinbuf -= clen; } if (pcneed != NULL) { if (cEbytes > CRECBUFLEN / 2) *pcneed = CRECBUFLEN / 2; else *pcneed = (int) cEbytes; } return TRUE; } /* Wait for data to come in and process it until we've reached the end of a command or a file. */ boolean fewait (qdaemon) struct sdaemon *qdaemon; { while (TRUE) { boolean fexit; size_t cneed, crec; if (! feprocess_data (qdaemon, &fexit, &cneed)) return FALSE; if (fexit) return TRUE; if (! freceive_data (qdaemon->qconn, cneed, &crec, cEtimeout, TRUE)) return FALSE; if (crec == 0) { ulog (LOG_ERROR, "Timed out waiting for data"); return FALSE; } } } /* File level routine, to handle transferring the amount of data and to set fEfile correctly. */ boolean fefile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled) struct sdaemon *qdaemon; struct stransfer *qtrans ATTRIBUTE_UNUSED; boolean fstart; boolean fsend; long cbytes; boolean *pfhandled; { *pfhandled = FALSE; if (fstart) { if (fsend) { char ab[CEFRAMELEN]; DEBUG_MESSAGE1 (DEBUG_PROTO, "Protocol 'e' starting to send %ld bytes", cbytes); bzero (ab, (size_t) CEFRAMELEN); sprintf (ab, "%ld", cbytes); if (! fsend_data (qdaemon->qconn, ab, (size_t) CEFRAMELEN, TRUE)) return FALSE; cEbytes = cbytes; } else { cEbytes = -1; fEfile = TRUE; } } else { if (! fsend) fEfile = FALSE; #if DEBUG > 0 if (cEbytes != 0) { ulog (LOG_ERROR, "Protocol 'e' internal error: %ld bytes left over", cEbytes); return FALSE; } #endif } return TRUE; } uucp-1.07/proti.c0000664000076400007640000013664307665321755007446 /* proti.c The 'i' protocol. Copyright (C) 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char proti_rcsid[] = "$Id: proti.c,v 1.36 2002/03/05 19:10:41 ian Rel $"; #endif #include #include #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "trans.h" #include "system.h" #include "prot.h" /* The 'i' protocol is a simple sliding window protocol, created by me. It is in many ways similar to the 'g' protocol. Several ideas are also taken from the paper ``A High-Throughput Message Transport System'' by P. Lauder. I don't know where the paper was published, but the author's e-mail address is piers@cs.su.oz.au. However, I haven't adopted his main idea, which is to dispense with windows entirely. This is because some links still do require flow control and, more importantly, because I want to have a limit to the amount of data I must be able to resend upon request. To reduce the costs of window acknowledgements, I use a large window and only require an ack at the halfway point. Each packet starts with a header containing the following information: Intro byte 8 bits byte 1 Packet number 5 bits byte 2 Local channel 3 bits Packet ack 5 bits byte 3 Remote channel 3 bits Packet type 3 bits bytes 4-5 Direction 1 bit Data length 12 bits Header check 8 bits byte 6 If the data length is not 0, this is followed by the data and a 32 bit CRC checksum. The following packet types are defined: SYNC Initialize the connection DATA Data packet ACK Simple acknowledgement with no data NAK Negative acknowledgement; requests resend of single packet SPOS Set file position CLOSE Close the connection */ /* The offsets of the bytes in the packet header. */ #define IHDR_INTRO (0) #define IHDR_LOCAL (1) #define IHDR_REMOTE (2) #define IHDR_CONTENTS1 (3) #define IHDR_CONTENTS2 (4) #define IHDR_CHECK (5) /* Macros to set and extract values of IHDR_LOCAL and IHDR_REMOTE. */ #define IHDRWIN_SET(iseq, ichan) (((iseq) << 3) | (ichan)) #define IHDRWIN_GETSEQ(ival) (((ival) >> 3) & 0x1f) #define IHDRWIN_GETCHAN(ival) ((ival) & 0x07) /* Macros to set and extract values of IHDR_CONTENTS fields. */ #define IHDRCON_SET1(ttype, fcaller, cbytes) \ (((ttype) << 5) | ((fcaller) ? (1 << 4) : 0) | (((cbytes) >> 8) & 0x0f)) #define IHDRCON_SET2(ttype, fcaller, cbytes) ((cbytes) & 0xff) #define THDRCON_GETTYPE(i1, i2) (((i1) >> 5) & 0x07) #define FHDRCON_GETCALLER(i1, i2) (((i1) & (1 << 4)) != 0) #define CHDRCON_GETBYTES(i1, i2) ((((i1) & 0x0f) << 8) | ((i2) & 0xff)) /* Macros for the IHDR_CHECK field. */ #define IHDRCHECK_VAL(zhdr) \ ((zhdr[IHDR_LOCAL] \ ^ zhdr[IHDR_REMOTE] \ ^ zhdr[IHDR_CONTENTS1] \ ^ zhdr[IHDR_CONTENTS2]) \ & 0xff) /* Length of the packet header. */ #define CHDRLEN (6) /* Amount of space to skip between start of packet and actual data. This is used to make the actual data longword aligned, to encourage good performance when copying data into the buffer. */ #define CHDRSKIPLEN (CHDRLEN + (sizeof (long) - CHDRLEN % sizeof (long))) /* Amount of space to skip between memory buffer and header. */ #define CHDROFFSET (CHDRSKIPLEN - CHDRLEN) /* Length of the trailing checksum. */ #define CCKSUMLEN (4) /* Macros to set and get the checksum. These multiply evaluate their arguments. */ #define ICKSUM_GET(z) \ ((((((((unsigned long) ((z)[0] & 0xff)) << 8) \ | (unsigned long) ((z)[1] & 0xff)) << 8) \ | (unsigned long) ((z)[2] & 0xff)) << 8) \ | (unsigned long) ((z)[3] & 0xff)) #define UCKSUM_SET(z, i) \ (void) ((z)[0] = (((i) >> 24) & 0xff), \ (z)[1] = (((i) >> 16) & 0xff), \ (z)[2] = (((i) >> 8) & 0xff), \ (z)[3] = ((i) & 0xff)) /* The header introduction character. */ #define IINTRO ('\007') /* The packet types. */ #define DATA (0) #define SYNC (1) #define ACK (2) #define NAK (3) #define SPOS (4) #define CLOSE (5) /* Largest possible packet size. */ #define IMAXPACKSIZE ((1 << 12) - 1) /* Largest possible sequence number (plus 1). */ #define IMAXSEQ 32 /* Get the next sequence number given a sequence number. */ #define INEXTSEQ(i) (((i) + 1) & (IMAXSEQ - 1)) /* Get the previous sequence number given a sequence number. */ #define IPREVSEQ(i) (((i) + IMAXSEQ - 1) & (IMAXSEQ - 1)) /* Compute i1 - i2 in sequence space (i.e., the number of packets from i2 to i1). */ #define CSEQDIFF(i1, i2) (((i1) + IMAXSEQ - (i2)) & (IMAXSEQ - 1)) /* Largest possible channel number (plus 1). */ #define IMAXICHAN (8) /* Default packet size to request (protocol parameter ``packet-size''). */ #define IREQUEST_PACKSIZE (1024) /* Default window size to request (protocol parameter ``window''). */ #define IREQUEST_WINSIZE (16) /* Default timeout to use when sending the SYNC packet (protocol parameter ``sync-timeout''). */ #define CSYNC_TIMEOUT (10) /* Default number of times to retry sending the SYNC packet (protocol parameter ``sync-retries''). */ #define CSYNC_RETRIES (6) /* Default timeout to use when waiting for a packet (protocol parameter ``timeout''). */ #define CTIMEOUT (10) /* Default number of times to retry sending a packet before giving up (protocol parameter ``retries''). */ #define CRETRIES (6) /* Default maximum level of errors to accept before giving up (protocol parameter ``errors''). */ #define CERRORS (100) /* Default decay rate. Each time we receive this many packets succesfully, we decrement the error level by one (protocol parameter ``error-decay''). */ #define CERROR_DECAY (10) /* The default list of characters to avoid: XON and XOFF. This string is processed as an escape sequence. This is 'j' protocol parameter ``avoid''; it is defined in this file because the 'i' and 'j' protocols share protocol parameters. */ #define ZAVOID "\\021\\023" /* Local variables. */ /* Packet size to request (protocol parameter ``packet-size''). */ static int iIrequest_packsize = IREQUEST_PACKSIZE; /* Window size to request (protocol parameter ``window''). */ static int iIrequest_winsize = IREQUEST_WINSIZE; /* Remote packet size (set from SYNC packet or from iIforced_remote_packsize). */ static int iIremote_packsize; /* Size which buffers were allocated for. */ static int iIalc_packsize; /* Forced remote packet size, used if non-zero (protocol parameter ``remote-packet-size''). There is no forced remote window size because the ACK strategy requires that both sides agree on the window size. */ static int iIforced_remote_packsize = 0; /* Remote window size (set from SYNC packet). */ static int iIremote_winsize; /* Timeout to use when sending the SYNC packet (protocol parameter ``sync-timeout''). */ int cIsync_timeout = CSYNC_TIMEOUT; /* Number of times to retry sending the SYNC packet (protocol parameter ``sync-retries''). */ static int cIsync_retries = CSYNC_RETRIES; /* Timeout to use when waiting for a packet (protocol parameter ``timeout''). */ static int cItimeout = CTIMEOUT; /* Timeout to use when waiting for an acknowledgement to open up space in the window. This is computed based on the window size and the connection speed. */ static int cIwindow_timeout = CTIMEOUT; /* Number of times to retry sending a packet before giving up (protocol parameter ``retries''). */ static int cIretries = CRETRIES; /* Maximum level of errors to accept before giving up (protocol parameter ``errors''). */ static int cIerrors = CERRORS; /* Each time we receive this many packets succesfully, we decrement the error level by one (protocol parameter ``error-decay''). */ static int cIerror_decay = CERROR_DECAY; /* The number of packets we should wait to receive before sending an ACK; this is set by default to half the window size we have requested (protocol parameter ``ack-frequency''). */ static int cIack_frequency = 0; /* The set of characters to avoid (protocol parameter ``avoid''). This is actually part of the 'j' protocol; it is defined in this file because the 'i' and 'j' protocols use the same protocol parameters. */ const char *zJavoid_parameter = ZAVOID; /* Routine to use when sending data. This is a hook for the 'j' protocol. */ static boolean (*pfIsend) P((struct sconnection *qconn, const char *zsend, size_t csend, boolean fdoread)); /* Routine to use to use when reading data. This is a hook for the 'j' protocol. */ static boolean (*pfIreceive) P((struct sconnection *qconn, size_t cneed, size_t *pcrec, int ctimeout, boolean freport)); /* Next sequence number to send. */ static int iIsendseq; /* Last sequence number received. */ static int iIrecseq; /* Last sequence number we have acknowledged. */ static int iIlocal_ack; /* Last sequence number remote system has acknowledged. */ static int iIremote_ack; /* File position we are sending from. */ static long iIsendpos; /* File position we are receiving to. */ static long iIrecpos; /* TRUE if closing the connection. */ static boolean fIclosing; /* Array of sent packets indexed by packet number. */ static char *azIsendbuffers[IMAXSEQ]; /* Array of received packets that we aren't ready to process yet, indexed by packet number. */ static char *azIrecbuffers[IMAXSEQ]; /* For each packet sequence number, record whether we sent a NAK for the packet. */ static boolean afInaked[IMAXSEQ]; /* Number of SYNC packets received (used only to detect whether one was received). */ static int cIsyncs; /* Number of packets sent. */ static long cIsent_packets; /* Number of packets received. */ static long cIreceived_packets; /* Number of packets resent. */ static long cIresent_packets; /* Number of bad packet headers received. */ static long cIbad_hdr; /* Number of out of order packets received. */ static long cIbad_order; /* Number of bad checksums received. */ static long cIbad_cksum; /* Number of packets rejected by remote system. */ static long cIremote_rejects; /* Protocol parameter commands. */ struct uuconf_cmdtab asIproto_params[] = { { "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iIrequest_packsize, NULL }, { "window", UUCONF_CMDTABTYPE_INT, (pointer) &iIrequest_winsize, NULL }, { "remote-packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iIforced_remote_packsize, NULL }, { "sync-timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cIsync_timeout, NULL }, { "sync-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cIsync_retries, NULL }, { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cItimeout, NULL }, { "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cIretries, NULL }, { "errors", UUCONF_CMDTABTYPE_INT, (pointer) &cIerrors, NULL }, { "error-decay", UUCONF_CMDTABTYPE_INT, (pointer) &cIerror_decay, NULL }, { "ack-frequency", UUCONF_CMDTABTYPE_INT, (pointer) &cIack_frequency, NULL }, /* The ``avoid'' protocol parameter is part of the 'j' protocol, but it is convenient for the 'i' and 'j' protocols to share the same protocol parameter table. */ { "avoid", UUCONF_CMDTABTYPE_STRING, (pointer) &zJavoid_parameter, NULL }, { NULL, 0, NULL, NULL } }; /* Local functions. */ static boolean finak P((struct sdaemon *qdaemon, int iseq)); static boolean firesend P((struct sdaemon *qdaemon)); static boolean fiwindow_wait P((struct sdaemon *qdaemon)); static boolean fiwait_for_packet P((struct sdaemon *qdaemon, int ctimeout, int cretries, boolean fone, boolean *ftimedout)); static boolean ficheck_errors P((struct sdaemon *qdaemon)); static boolean fiprocess_data P((struct sdaemon *qdaemon, boolean *pfexit, boolean *pffound, size_t *pcneed)); static boolean fiprocess_packet P((struct sdaemon *qdaemon, const char *zhdr, const char *zfirst, int cfirst, const char *zsecond, int csecond, boolean *pfexit)); /* The 'i' protocol start routine. The work is done in a routine which is also called by the 'j' protocol start routine. */ boolean fistart (qdaemon, pzlog) struct sdaemon *qdaemon; char **pzlog; { return fijstart (qdaemon, pzlog, IMAXPACKSIZE, fsend_data, freceive_data); } /* Start the protocol. This routine is called by both the 'i' and 'j' protocol start routines. We keep transmitting a SYNC packet containing the window and packet size we would like to receive until we receive a SYNC packet from the remote system. The first two bytes of the data contents of a SYNC packet are the maximum packet size we want to receive (high byte, low byte), and the next byte is the maximum window size we want to use. */ boolean fijstart (qdaemon, pzlog, imaxpacksize, pfsend, pfreceive) struct sdaemon *qdaemon; char **pzlog; int imaxpacksize; boolean (*pfsend) P((struct sconnection *qconn, const char *zsend, size_t csend, boolean fdoread)); boolean (*pfreceive) P((struct sconnection *qconn, size_t cneed, size_t *pcrec, int ctimeout, boolean freport)); { char ab[CHDRLEN + 4 + CCKSUMLEN]; unsigned long icksum; int ctries; int csyncs; long ibaud; *pzlog = NULL; pfIsend = pfsend; pfIreceive = pfreceive; if (iIforced_remote_packsize <= 0 || iIforced_remote_packsize > imaxpacksize) iIforced_remote_packsize = 0; else iIremote_packsize = iIforced_remote_packsize; iIalc_packsize = 0; iIsendseq = 1; iIrecseq = 0; iIlocal_ack = 0; iIremote_ack = 0; iIsendpos = 0; iIrecpos = 0; fIclosing = FALSE; cIsent_packets = 0; cIreceived_packets = 0; cIresent_packets = 0; cIbad_hdr = 0; cIbad_order = 0; cIbad_cksum = 0; cIremote_rejects = 0; if (iIrequest_packsize < 0 || iIrequest_packsize > imaxpacksize) { ulog (LOG_ERROR, "Illegal protocol '%c' packet size; using %d", qdaemon->qproto->bname, imaxpacksize); iIrequest_packsize = imaxpacksize; } /* The maximum permissible window size is 16. Otherwise the protocol can get confused because a duplicated packet may arrive out of order. If the window size is large in such a case, the duplicate packet may be treated as a packet in the upcoming window, causing the protocol to assume that all intermediate packets have been lost, leading to immense confusion. */ if (iIrequest_winsize < 0 || iIrequest_winsize > IMAXSEQ / 2) { ulog (LOG_ERROR, "Illegal protocol '%c' window size; using %d", qdaemon->qproto->bname, IREQUEST_WINSIZE); iIrequest_winsize = IREQUEST_WINSIZE; } /* The default for the ACK frequency is half the window size. */ if (cIack_frequency <= 0 || cIack_frequency >= iIrequest_winsize) cIack_frequency = iIrequest_winsize / 2; ab[IHDR_INTRO] = IINTRO; ab[IHDR_LOCAL] = ab[IHDR_REMOTE] = IHDRWIN_SET (0, 0); ab[IHDR_CONTENTS1] = IHDRCON_SET1 (SYNC, qdaemon->fcaller, 4); ab[IHDR_CONTENTS2] = IHDRCON_SET2 (SYNC, qdaemon->fcaller, 4); ab[IHDR_CHECK] = IHDRCHECK_VAL (ab); ab[CHDRLEN + 0] = (iIrequest_packsize >> 8) & 0xff; ab[CHDRLEN + 1] = iIrequest_packsize & 0xff; ab[CHDRLEN + 2] = iIrequest_winsize; ab[CHDRLEN + 3] = qdaemon->cchans; icksum = icrc (ab + CHDRLEN, 4, ICRCINIT); UCKSUM_SET (ab + CHDRLEN + 4, icksum); /* The static cIsyncs is incremented each time a SYNC packet is received. */ csyncs = cIsyncs; ctries = 0; while (TRUE) { boolean ftimedout; DEBUG_MESSAGE3 (DEBUG_PROTO, "fistart: Sending SYNC packsize %d winsize %d channels %d", iIrequest_packsize, iIrequest_winsize, qdaemon->cchans); if (! (*pfIsend) (qdaemon->qconn, ab, CHDRLEN + 4 + CCKSUMLEN, TRUE)) return FALSE; if (fiwait_for_packet (qdaemon, cIsync_timeout, 0, FALSE, &ftimedout)) { if (csyncs != cIsyncs) break; } else { if (! ftimedout) return FALSE; ++ctries; if (ctries > cIsync_retries) { ulog (LOG_ERROR, "Protocol startup failed"); return FALSE; } } } /* Calculate the window timeout. */ ibaud = iconn_baud (qdaemon->qconn); if (ibaud == 0) cIwindow_timeout = cItimeout; else { /* We expect to receive an ACK about halfway through each window. In principle, an entire window might be sitting in a modem buffer while we are waiting for an ACK. Therefore, the minimum time we should wait for an ACK is (1/2 window size) * (seconds / byte) + (roundtrip time) == (1/2 window size) * (1 / (baud / 10)) + (roundtrip time) == (1/2 window size) * (10 / baud) + (roundtrip time) == (5 * (window size)) / baud + (roundtrip time) The window size is iIremote_packsize * iIremote_winsize. For typical settings of packsize == 1024, winsize == 16, baud == 9600, this equation works out to (5 * 1024 * 16) / 9600 == 8 seconds We then take cItimeout as the round trip time, which gives us some flexibility. We get more flexibility because it is quite likely that by the time we have finished sending out the last packet in a window, the first one has already been received by the remote system. */ cIwindow_timeout = ((5 * iIremote_packsize * iIremote_winsize) / ibaud + cItimeout); } /* If we are the callee, bump both timeouts by one, to make it less likely that both systems will timeout simultaneously. */ if (! qdaemon->fcaller) { ++cItimeout; ++cIwindow_timeout; } /* We got a SYNC packet; set up packet buffers to use. */ if (iIremote_packsize > imaxpacksize) iIremote_packsize = imaxpacksize; do { int iseq; for (iseq = 0; iseq < IMAXSEQ; iseq++) { azIrecbuffers[iseq] = NULL; afInaked[iseq] = FALSE; azIsendbuffers[iseq] = (char *) malloc (iIremote_packsize + CHDRSKIPLEN + CCKSUMLEN); if (azIsendbuffers[iseq] == NULL) { int ifree; for (ifree = 0; ifree < iseq; ifree++) free ((pointer) azIsendbuffers[ifree]); break; } } if (iseq >= IMAXSEQ) { *pzlog = zbufalc (sizeof "protocol '' sending packet/window / receiving /" + 64); sprintf (*pzlog, "protocol '%c' sending packet/window %d/%d receiving %d/%d", qdaemon->qproto->bname, (int) iIremote_packsize, (int) iIremote_winsize, (int) iIrequest_packsize, (int) iIrequest_winsize); iIalc_packsize = iIremote_packsize; return TRUE; } iIremote_packsize >>= 1; } while (iIremote_packsize > 200); ulog (LOG_ERROR, "'%c' protocol startup failed; insufficient memory for packets", qdaemon->qproto->bname); return FALSE; } /* Shut down the protocol. We can be fairly informal about this, since we know that the upper level protocol has already exchanged hangup messages. If we didn't know that, we would have to make sure that all packets before the CLOSE had been received. */ boolean fishutdown (qdaemon) struct sdaemon *qdaemon; { char *z; size_t clen; fIclosing = TRUE; z = zigetspace (qdaemon, &clen) - CHDRLEN; z[IHDR_INTRO] = IINTRO; z[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, 0); z[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); iIlocal_ack = iIrecseq; z[IHDR_CONTENTS1] = IHDRCON_SET1 (CLOSE, qdaemon->fcaller, 0); z[IHDR_CONTENTS2] = IHDRCON_SET2 (CLOSE, qdaemon->fcaller, 0); z[IHDR_CHECK] = IHDRCHECK_VAL (z); DEBUG_MESSAGE0 (DEBUG_PROTO, "fishutdown: Sending CLOSE"); if (! (*pfIsend) (qdaemon->qconn, z, CHDRLEN, FALSE)) return FALSE; ulog (LOG_NORMAL, "Protocol '%c' packets: sent %ld, resent %ld, received %ld", qdaemon->qproto->bname, cIsent_packets, cIresent_packets, cIreceived_packets); if (cIbad_hdr != 0 || cIbad_cksum != 0 || cIbad_order != 0 || cIremote_rejects != 0) ulog (LOG_NORMAL, "Errors: header %ld, checksum %ld, order %ld, remote rejects %ld", cIbad_hdr, cIbad_cksum, cIbad_order, cIremote_rejects); /* Reset the protocol parameters to their default values. */ iIrequest_packsize = IREQUEST_PACKSIZE; iIrequest_winsize = IREQUEST_WINSIZE; iIforced_remote_packsize = 0; cIsync_timeout = CSYNC_TIMEOUT; cIsync_retries = CSYNC_RETRIES; cItimeout = CTIMEOUT; cIwindow_timeout = CTIMEOUT; cIretries = CRETRIES; cIerrors = CERRORS; cIerror_decay = CERROR_DECAY; cIack_frequency = 0; zJavoid_parameter = ZAVOID; return TRUE; } /* Send a command string. These are just sent as normal packets, ending in a packet containing a null byte. */ boolean fisendcmd (qdaemon, z, ilocal, iremote) struct sdaemon *qdaemon; const char *z; int ilocal; int iremote; { size_t clen; DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fisendcmd: Sending command \"%s\"", z); clen = strlen (z); while (TRUE) { char *zpacket; size_t csize; zpacket = zigetspace (qdaemon, &csize); if (clen < csize) { memcpy (zpacket, z, clen + 1); return fisenddata (qdaemon, zpacket, clen + 1, ilocal, iremote, (long) -1); } memcpy (zpacket, z, csize); z += csize; clen -= csize; if (! fisenddata (qdaemon, zpacket, csize, ilocal, iremote, (long) -1)) return FALSE; } /*NOTREACHED*/ } /* Send a NAK. */ static boolean finak (qdaemon, iseq) struct sdaemon *qdaemon; int iseq; { char abnak[CHDRLEN]; abnak[IHDR_INTRO] = IINTRO; abnak[IHDR_LOCAL] = IHDRWIN_SET (iseq, 0); abnak[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); iIlocal_ack = iIrecseq; abnak[IHDR_CONTENTS1] = IHDRCON_SET1 (NAK, qdaemon->fcaller, 0); abnak[IHDR_CONTENTS2] = IHDRCON_SET2 (NAK, qdaemon->fcaller, 0); abnak[IHDR_CHECK] = IHDRCHECK_VAL (abnak); afInaked[iseq] = TRUE; DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, "finak: Sending NAK %d", iseq); return (*pfIsend) (qdaemon->qconn, abnak, CHDRLEN, TRUE); } /* Resend the latest packet the remote has not acknowledged. */ static boolean firesend (qdaemon) struct sdaemon *qdaemon; { int iseq; char *zhdr; size_t clen; iseq = INEXTSEQ (iIremote_ack); if (iseq == iIsendseq) { /* Everything has been ACKed and there is nothing to resend. */ return TRUE; } DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, "firesend: Resending packet %d", iseq); /* Update the received sequence number. */ zhdr = azIsendbuffers[iseq] + CHDROFFSET; if (IHDRWIN_GETSEQ (zhdr[IHDR_REMOTE]) != iIrecseq) { int iremote; iremote = IHDRWIN_GETCHAN (zhdr[IHDR_REMOTE]); zhdr[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote); zhdr[IHDR_CHECK] = IHDRCHECK_VAL (zhdr); iIlocal_ack = iIrecseq; } ++cIresent_packets; clen = CHDRCON_GETBYTES (zhdr[IHDR_CONTENTS1], zhdr[IHDR_CONTENTS2]); return (*pfIsend) (qdaemon->qconn, zhdr, CHDRLEN + clen + (clen > 0 ? CCKSUMLEN : 0), TRUE); } /* Wait until there is an opening in the receive window of the remote system. */ static boolean fiwindow_wait (qdaemon) struct sdaemon *qdaemon; { /* iIsendseq is the sequence number we are sending, and iIremote_ack is the last sequence number acknowledged by the remote. */ while (CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize) { /* If a NAK is lost, it is possible for the other side to be sending a stream of packets while we are waiting for an ACK. This should be caught in fiprocess_data; if it is about to send an ACK, but it has an unacknowledged packet to send, it sends the entire packet. Hopefully that will trigger an ACK or a NAK and get us going again. */ DEBUG_MESSAGE0 (DEBUG_PROTO, "fiwindow_wait: Waiting for ACK"); if (! fiwait_for_packet (qdaemon, cIwindow_timeout, cIretries, TRUE, (boolean *) NULL)) return FALSE; } return TRUE; } /* Get buffer space to use for packet data. We return a pointer to the space to be used for the actual data, leaving room for the header. */ /*ARGSUSED*/ char * zigetspace (qdaemon, pclen) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; size_t *pclen; { *pclen = iIremote_packsize; return azIsendbuffers[iIsendseq] + CHDRSKIPLEN; } /* Send a data packet. The zdata argument will always point to value returned by zigetspace, so we know that we have room before it for the header information. */ boolean fisenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) struct sdaemon *qdaemon; char *zdata; size_t cdata; int ilocal; int iremote; long ipos; { char *zhdr; unsigned long icksum; boolean fret; #if DEBUG > 0 if (ilocal < 0 || ilocal >= IMAXICHAN || iremote < 0 || iremote >= IMAXICHAN) ulog (LOG_FATAL, "fisenddata: ilocal %d iremote %d", ilocal, iremote); #endif /* If we are changing the file position, we must send an SPOS packet. */ if (ipos != iIsendpos && ipos != (long) -1) { int inext; char *zspos; /* We need to get a buffer to hold the SPOS packet, and it needs to be next sequence number. However, the data we have been given is currently in the next sequence number buffer. So we shuffle the buffers around. */ inext = INEXTSEQ (iIsendseq); zspos = azIsendbuffers[inext]; azIsendbuffers[inext] = zdata - CHDRSKIPLEN; azIsendbuffers[iIsendseq] = zspos; zspos += CHDROFFSET; zspos[IHDR_INTRO] = IINTRO; zspos[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, 0); zspos[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); iIlocal_ack = iIrecseq; zspos[IHDR_CONTENTS1] = IHDRCON_SET1 (SPOS, qdaemon->fcaller, CCKSUMLEN); zspos[IHDR_CONTENTS2] = IHDRCON_SET2 (SPOS, qdaemon->fcaller, CCKSUMLEN); zspos[IHDR_CHECK] = IHDRCHECK_VAL (zspos); UCKSUM_SET (zspos + CHDRLEN, (unsigned long) ipos); icksum = icrc (zspos + CHDRLEN, CCKSUMLEN, ICRCINIT); UCKSUM_SET (zspos + CHDRLEN + CCKSUMLEN, icksum); /* Wait for an opening in the window. */ if (iIremote_winsize > 0 && CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize) { if (! fiwindow_wait (qdaemon)) return FALSE; } DEBUG_MESSAGE1 (DEBUG_PROTO, "fisenddata: Sending SPOS %ld", ipos); if (! (*pfIsend) (qdaemon->qconn, zspos, CHDRLEN + CCKSUMLEN + CCKSUMLEN, TRUE)) return FALSE; iIsendseq = INEXTSEQ (iIsendseq); iIsendpos = ipos; } zhdr = zdata - CHDRLEN; zhdr[IHDR_INTRO] = IINTRO; zhdr[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, ilocal); zhdr[IHDR_CONTENTS1] = IHDRCON_SET1 (DATA, qdaemon->fcaller, cdata); zhdr[IHDR_CONTENTS2] = IHDRCON_SET2 (DATA, qdaemon->fcaller, cdata); /* Compute and set the checksum. */ if (cdata > 0) { icksum = icrc (zdata, cdata, ICRCINIT); UCKSUM_SET (zdata + cdata, icksum); } /* Wait until there is an opening in the window (we hope to not have to wait here at all, actually; ideally the window should be large enough to avoid a wait). */ if (iIremote_winsize > 0 && CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize) { if (! fiwindow_wait (qdaemon)) return FALSE; } /* We only fill in IHDR_REMOTE now, since only now do know the correct value of iIrecseq. */ zhdr[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote); iIlocal_ack = iIrecseq; zhdr[IHDR_CHECK] = IHDRCHECK_VAL (zhdr); DEBUG_MESSAGE4 (DEBUG_PROTO, "fisenddata: Sending packet %d size %d local %d remote %d", iIsendseq, (int) cdata, ilocal, iremote); iIsendseq = INEXTSEQ (iIsendseq); ++cIsent_packets; fret = (*pfIsend) (qdaemon->qconn, zhdr, cdata + CHDRLEN + (cdata > 0 ? CCKSUMLEN : 0), TRUE); iIsendpos += cdata; if (fret && iPrecstart != iPrecend) { boolean fexit; fret = fiprocess_data (qdaemon, &fexit, (boolean *) NULL, (size_t *) NULL); } return fret; } /* Wait for data to come in. */ boolean fiwait (qdaemon) struct sdaemon *qdaemon; { return fiwait_for_packet (qdaemon, cItimeout, cIretries, FALSE, (boolean *) NULL); } /* Wait for a packet. Either there is no data to send, or the remote window is full. */ static boolean fiwait_for_packet (qdaemon, ctimeout, cretries, fone, pftimedout) struct sdaemon *qdaemon; int ctimeout; int cretries; boolean fone; boolean *pftimedout; { int cshort; int ctimeouts; if (pftimedout != NULL) *pftimedout = FALSE; cshort = 0; ctimeouts = 0; while (TRUE) { boolean fexit, ffound; size_t cneed; size_t crec; if (! fiprocess_data (qdaemon, &fexit, &ffound, &cneed)) return FALSE; if (fexit || (fone && ffound)) return TRUE; if (cneed == 0) continue; DEBUG_MESSAGE1 (DEBUG_PROTO, "fiwait_for_packet: Need %d bytes", (int) cneed); if (! (*pfIreceive) (qdaemon->qconn, cneed, &crec, ctimeout, TRUE)) return FALSE; if (crec != 0) { /* If we didn't get enough data twice in a row, we may have dropped some data and be waiting for the end of a large packet. Incrementing iPrecstart will force fiprocess_data to skip the current packet and try to find the next one. */ if (crec >= cneed) cshort = 0; else { ++cshort; if (cshort > 1) { iPrecstart = (iPrecstart + 1) % CRECBUFLEN; cshort = 0; } } } else { int i; /* We timed out on the read. */ ++ctimeouts; if (ctimeouts > cretries) { if (cretries > 0) ulog (LOG_ERROR, "Timed out waiting for packet"); if (pftimedout != NULL) *pftimedout = TRUE; return FALSE; } /* Clear out the list of packets we have sent NAKs for. We should have seen some sort of response by now. */ for (i = 0; i < IMAXSEQ; i++) afInaked[i] = FALSE; /* Send a NAK for the packet we want, and, if we have an unacknowledged packet, send it again. */ if (! finak (qdaemon, INEXTSEQ (iIrecseq)) || ! firesend (qdaemon)) return FALSE; } } /*NOTREACHED*/ } /* Make sure we haven't overflowed the permissible error level. */ static boolean ficheck_errors (qdaemon) struct sdaemon *qdaemon; { if (cIerrors < 0) return TRUE; if (((cIbad_order + cIbad_hdr + cIbad_cksum + cIremote_rejects) - (cIreceived_packets / cIerror_decay)) > cIerrors) { /* Try shrinking the packet size. */ if (iIrequest_packsize > 400) { char absync[CHDRLEN + 3 + CCKSUMLEN]; unsigned long icksum; /* Don't bother sending the number of channels in this packet. */ iIrequest_packsize /= 2; absync[IHDR_INTRO] = IINTRO; absync[IHDR_LOCAL] = IHDRWIN_SET (0, 0); absync[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); iIlocal_ack = iIrecseq; absync[IHDR_CONTENTS1] = IHDRCON_SET1 (SYNC, qdaemon->fcaller, 3); absync[IHDR_CONTENTS2] = IHDRCON_SET2 (SYNC, qdaemon->fcaller, 3); absync[IHDR_CHECK] = IHDRCHECK_VAL (absync); absync[CHDRLEN + 0] = (iIrequest_packsize >> 8) & 0xff; absync[CHDRLEN + 1] = iIrequest_packsize & 0xff; absync[CHDRLEN + 2] = iIrequest_winsize; icksum = icrc (absync + CHDRLEN, 3, ICRCINIT); UCKSUM_SET (absync + CHDRLEN + 3, icksum); cIerrors *= 2; DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "ficheck_errors: Sending SYNC packsize %d winsize %d", iIrequest_packsize, iIrequest_winsize); return (*pfIsend) (qdaemon->qconn, absync, CHDRLEN + 3 + CCKSUMLEN, TRUE); } ulog (LOG_ERROR, "Too many '%c' protocol errors", qdaemon->qproto->bname); return FALSE; } return TRUE; } /* Process data waiting in the receive buffer, passing to the fgot_data function. */ static boolean fiprocess_data (qdaemon, pfexit, pffound, pcneed) struct sdaemon *qdaemon; boolean *pfexit; boolean *pffound; size_t *pcneed; { boolean fbadhdr; if (pfexit != NULL) *pfexit = FALSE; if (pffound != NULL) *pffound = FALSE; fbadhdr = FALSE; while (iPrecstart != iPrecend) { char ab[CHDRLEN]; int cfirst, csecond; char *zfirst, *zsecond; int i; int iget; int ttype; int iseq; int csize; int iack; /* If we're closing the connection, ignore any data remaining in the input buffer. */ if (fIclosing) { if (pfexit != NULL) *pfexit = TRUE; if (pcneed != NULL) *pcneed = 0; return TRUE; } /* Look for the IINTRO character. */ if (abPrecbuf[iPrecstart] != IINTRO) { char *zintro; int cintro; cintro = iPrecend - iPrecstart; if (cintro < 0) cintro = CRECBUFLEN - iPrecstart; zintro = memchr (abPrecbuf + iPrecstart, IINTRO, (size_t) cintro); if (zintro == NULL) { iPrecstart = (iPrecstart + cintro) % CRECBUFLEN; continue; } /* We don't need % CRECBUFLEN here because zintro - (abPrecbuf + iPrecstart) < cintro <= CRECBUFLEN - iPrecstart. */ iPrecstart += zintro - (abPrecbuf + iPrecstart); } /* Get the header into ab. */ for (i = 0, iget = iPrecstart; i < CHDRLEN && iget != iPrecend; i++, iget = (iget + 1) % CRECBUFLEN) ab[i] = abPrecbuf[iget]; if (i < CHDRLEN) { if (pcneed != NULL) *pcneed = CHDRLEN - i; return TRUE; } if ((ab[IHDR_CHECK] & 0xff) != IHDRCHECK_VAL (ab) || (FHDRCON_GETCALLER (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2]) ? qdaemon->fcaller : ! qdaemon->fcaller)) { /* We only report a single bad header message per call, to avoid generating many errors if we get many INTRO bytes in a row. */ if (! fbadhdr) { DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL, "fiprocess_data: Bad header"); ++cIbad_hdr; if (! ficheck_errors (qdaemon)) return FALSE; fbadhdr = TRUE; } iPrecstart = (iPrecstart + 1) % CRECBUFLEN; continue; } zfirst = zsecond = NULL; cfirst = csecond = 0; ttype = THDRCON_GETTYPE (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2]); if (ttype == DATA || ttype == SPOS || ttype == CLOSE) iseq = IHDRWIN_GETSEQ (ab[IHDR_LOCAL]); else iseq = -1; csize = CHDRCON_GETBYTES (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2]); if (iseq != -1) { /* Make sure this packet is in our receive window. The last packet we have acked is iIlocal_ack. */ if (iIrequest_winsize > 0 && CSEQDIFF (iseq, iIlocal_ack) > iIrequest_winsize) { DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "fiprocess_data: Out of order packet %d (ack %d)", iseq, iIlocal_ack); ++cIbad_order; if (! ficheck_errors (qdaemon)) return FALSE; iPrecstart = (iPrecstart + 1) % CRECBUFLEN; continue; } } if (csize > 0) { int cinbuf; char abcksum[CCKSUMLEN]; unsigned long ickdata; cinbuf = iPrecend - iPrecstart; if (cinbuf < 0) cinbuf += CRECBUFLEN; if (cinbuf < CHDRLEN + csize + CCKSUMLEN) { if (pcneed != NULL) *pcneed = CHDRLEN + csize + CCKSUMLEN - cinbuf; return TRUE; } if (iPrecend > iPrecstart) { cfirst = csize; zfirst = abPrecbuf + iPrecstart + CHDRLEN; } else { cfirst = CRECBUFLEN - (iPrecstart + CHDRLEN); if (cfirst <= 0) { /* Here cfirst is non-positive, so subtracting from abPrecbuf will actually skip the appropriate number of bytes at the start of abPrecbuf. */ zfirst = abPrecbuf - cfirst; cfirst = csize; } else { if (cfirst >= csize) cfirst = csize; else { zsecond = abPrecbuf; csecond = csize - cfirst; } zfirst = abPrecbuf + iPrecstart + CHDRLEN; } } /* Get the checksum into abcksum. */ for (i = 0, iget = (iPrecstart + CHDRLEN + csize) % CRECBUFLEN; i < CCKSUMLEN; i++, iget = (iget + 1) % CRECBUFLEN) abcksum[i] = abPrecbuf[iget]; ickdata = icrc (zfirst, (size_t) cfirst, ICRCINIT); if (csecond > 0) ickdata = icrc (zsecond, (size_t) csecond, ickdata); if (ICKSUM_GET (abcksum) != ickdata) { DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "fiprocess_data: Bad checksum; data %lu, frame %lu", ickdata, ICKSUM_GET (abcksum)); ++cIbad_cksum; if (! ficheck_errors (qdaemon)) return FALSE; /* If this sequence number is in our receive window, send a NAK. iIrecseq is the last sequence number we have succesfully received. */ if (iseq != -1 && iseq != iIrecseq && (iIrequest_winsize <= 0 || CSEQDIFF (iseq, iIrecseq) <= iIrequest_winsize) && azIrecbuffers[iseq] == NULL) { if (! finak (qdaemon, iseq)) return FALSE; } iPrecstart = (iPrecstart + 1) % CRECBUFLEN; continue; } } /* Here we know that this is a valid packet, so we can adjust iPrecstart accordingly. */ if (csize == 0) iPrecstart = (iPrecstart + CHDRLEN) % CRECBUFLEN; else { iPrecstart = ((iPrecstart + CHDRLEN + csize + CCKSUMLEN) % CRECBUFLEN); ++cIreceived_packets; } /* Get the ack from the packet, if appropriate. iIsendseq is the next sequence number we are going to send, and iIremote_ack is the last sequence number acknowledged by the remote system. */ iack = IHDRWIN_GETSEQ (ab[IHDR_REMOTE]); if (iIremote_winsize > 0 && iack != iIsendseq && CSEQDIFF (iack, iIremote_ack) <= iIremote_winsize && CSEQDIFF (iIsendseq, iack) <= iIremote_winsize) { /* Call uwindow_acked each time packet 0 is acked. */ if (iack < iIremote_ack) uwindow_acked (qdaemon, FALSE); iIremote_ack = iack; } if (iseq != -1) { /* If we already sent a NAK for this packet, and we have not seen the previous packet, then forget that we sent a NAK for this and any preceding packets. This is to handle the following sequence: receive packet 0 packets 1 and 2 lost receive packet 3 send NAK 1 send NAK 2 packet 1 lost receive packet 2 At this point we want to send NAK 1. */ if (afInaked[iseq] && azIrecbuffers[IPREVSEQ (iseq)] == NULL) { for (i = INEXTSEQ (iIrecseq); i != iseq; i = INEXTSEQ (i)) afInaked[i] = FALSE; afInaked[iseq] = FALSE; } /* If we haven't handled all previous packets, we must save off this packet and deal with it later. */ if (iseq != INEXTSEQ (iIrecseq)) { if (iseq == iIrecseq || (iIrequest_winsize > 0 && CSEQDIFF (iseq, iIrecseq) > iIrequest_winsize)) { DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "fiprocess_data: Ignoring out of order packet %d (recseq %d)", iseq, iIrecseq); continue; } else { DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "fiprocess_data: Saving unexpected packet %d (recseq %d)", iseq, iIrecseq); if (azIrecbuffers[iseq] == NULL) { azIrecbuffers[iseq] = zbufalc ((size_t) (CHDRLEN + csize)); memcpy (azIrecbuffers[iseq], ab, CHDRLEN); if (csize > 0) { memcpy (azIrecbuffers[iseq] + CHDRLEN, zfirst, (size_t) cfirst); if (csecond > 0) memcpy (azIrecbuffers[iseq] + CHDRLEN + cfirst, zsecond, (size_t) csecond); } } } /* Send NAK's for each packet between the last one we received and this one, avoiding any packets for which we've already sent NAK's or which we've already received. */ for (i = INEXTSEQ (iIrecseq); i != iseq; i = INEXTSEQ (i)) { if (! afInaked[i] && azIrecbuffers[i] == NULL) { if (! finak (qdaemon, i)) return FALSE; } } continue; } iIrecseq = iseq; } if (pffound != NULL) *pffound = TRUE; if (! fiprocess_packet (qdaemon, ab, zfirst, cfirst, zsecond, csecond, pfexit)) return FALSE; if (iseq != -1) { int inext; /* If we've already received the next packet(s), process them. */ inext = INEXTSEQ (iIrecseq); while (azIrecbuffers[inext] != NULL) { char *z; int c; z = azIrecbuffers[inext]; c = CHDRCON_GETBYTES (z[IHDR_CONTENTS1], z[IHDR_CONTENTS2]); iIrecseq = inext; if (! fiprocess_packet (qdaemon, z, z + CHDRLEN, c, (char *) NULL, 0, pfexit)) return FALSE; ubuffree (azIrecbuffers[inext]); azIrecbuffers[inext] = NULL; inext = INEXTSEQ (inext); } } /* If we have received half of our window size or more since the last ACK, send one now. Sending an ACK for half the window at a time should significantly cut the acknowledgement traffic when only one side is sending. We should normally not have to send an ACK if we have data to send, since each packet sent will ACK the most recently received packet. However, it can happen if we receive a burst of short packets, such as a set of command acknowledgements. */ if (iIrequest_winsize > 0 && CSEQDIFF (iIrecseq, iIlocal_ack) >= cIack_frequency) { char aback[CHDRLEN]; aback[IHDR_INTRO] = IINTRO; aback[IHDR_LOCAL] = IHDRWIN_SET (0, 0); aback[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); iIlocal_ack = iIrecseq; aback[IHDR_CONTENTS1] = IHDRCON_SET1 (ACK, qdaemon->fcaller, 0); aback[IHDR_CONTENTS2] = IHDRCON_SET2 (ACK, qdaemon->fcaller, 0); aback[IHDR_CHECK] = IHDRCHECK_VAL (aback); DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_data: Sending ACK %d", iIrecseq); if (! (*pfIsend) (qdaemon->qconn, aback, CHDRLEN, TRUE)) return FALSE; } } if (pcneed != NULL) *pcneed = CHDRLEN; return TRUE; } /* Process a single packet. */ static boolean fiprocess_packet (qdaemon, zhdr, zfirst, cfirst, zsecond, csecond, pfexit) struct sdaemon *qdaemon; const char *zhdr; const char *zfirst; int cfirst; const char *zsecond; int csecond; boolean *pfexit; { int ttype; ttype = THDRCON_GETTYPE (zhdr[IHDR_CONTENTS1], zhdr[IHDR_CONTENTS2]); switch (ttype) { case DATA: { int iseq; boolean fret; iseq = IHDRWIN_GETSEQ (zhdr[IHDR_LOCAL]); DEBUG_MESSAGE4 (DEBUG_PROTO, "fiprocess_packet: Got DATA packet %d size %d local %d remote %d", iseq, cfirst + csecond, IHDRWIN_GETCHAN (zhdr[IHDR_REMOTE]), IHDRWIN_GETCHAN (zhdr[IHDR_LOCAL])); fret = fgot_data (qdaemon, zfirst, (size_t) cfirst, zsecond, (size_t) csecond, IHDRWIN_GETCHAN (zhdr[IHDR_REMOTE]), IHDRWIN_GETCHAN (zhdr[IHDR_LOCAL]), iIrecpos, INEXTSEQ (iIremote_ack) == iIsendseq, pfexit); iIrecpos += cfirst + csecond; return fret; } case SYNC: { int ipack, iwin, cchans; /* We accept a SYNC packet to adjust the packet and window sizes at any time. */ if (cfirst + csecond < 3) { ulog (LOG_ERROR, "Bad SYNC packet"); return FALSE; } ipack = (zfirst[0] & 0xff) << 8; if (cfirst > 1) ipack |= zfirst[1] & 0xff; else ipack |= zsecond[0]; if (cfirst > 2) iwin = zfirst[2]; else iwin = zsecond[2 - cfirst]; /* The fourth byte in a SYNC packet is the number of channels to use. This is optional. Switching the number of channels in the middle of a conversation may cause problems. */ if (cfirst + csecond <= 3) cchans = 0; else { if (cfirst > 3) cchans = zfirst[3]; else cchans = zsecond[3 - cfirst]; if (cchans > 0 && cchans < 8) qdaemon->cchans = cchans; } DEBUG_MESSAGE3 (DEBUG_PROTO, "fiprocess_packet: Got SYNC packsize %d winsize %d channels %d", ipack, iwin, cchans); if (iIforced_remote_packsize == 0 && (iIalc_packsize == 0 || ipack <= iIalc_packsize)) iIremote_packsize = ipack; iIremote_winsize = iwin; /* We increment a static variable to tell the initialization code that a SYNC was received, and we set *pfexit to TRUE to get out to the initialization code (this will do no harm if we are called from elsewhere). */ ++cIsyncs; *pfexit = TRUE; return TRUE; } case ACK: /* There is nothing to do here, since the ack was already handled in fiprocess_data. */ DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_packet: Got ACK %d", IHDRWIN_GETSEQ (zhdr[IHDR_REMOTE])); return TRUE; case NAK: /* We must resend the requested packet. */ { int iseq; char *zsend; size_t clen; ++cIremote_rejects; if (! ficheck_errors (qdaemon)) return FALSE; iseq = IHDRWIN_GETSEQ (zhdr[IHDR_LOCAL]); /* If the remote side times out while waiting for a packet, it will send a NAK for the next packet it wants to see. If we have not sent that packet yet, and we have no unacknowledged data, it implies that the remote side has a window full of data to send, which implies that our ACK has been lost. Therefore, we send an ACK. */ if (iseq == iIsendseq && INEXTSEQ (iIremote_ack) == iIsendseq) { char aback[CHDRLEN]; aback[IHDR_INTRO] = IINTRO; aback[IHDR_LOCAL] = IHDRWIN_SET (0, 0); aback[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0); iIlocal_ack = iIrecseq; aback[IHDR_CONTENTS1] = IHDRCON_SET1 (ACK, qdaemon->fcaller, 0); aback[IHDR_CONTENTS2] = IHDRCON_SET2 (ACK, qdaemon->fcaller, 0); aback[IHDR_CHECK] = IHDRCHECK_VAL (aback); DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_packet: Sending ACK %d", iIrecseq); return (*pfIsend) (qdaemon->qconn, aback, CHDRLEN, TRUE); } else { if (iseq == iIsendseq || (iIremote_winsize > 0 && (CSEQDIFF (iseq, iIremote_ack) > iIremote_winsize || CSEQDIFF (iIsendseq, iseq) > iIremote_winsize))) { DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "fiprocess_packet: Ignoring out of order NAK %d (sendseq %d)", iseq, iIsendseq); return TRUE; } DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, "fiprocess_packet: Got NAK %d; resending packet", iseq); /* Update the received sequence number. */ zsend = azIsendbuffers[iseq] + CHDROFFSET; if (IHDRWIN_GETSEQ (zsend[IHDR_REMOTE]) != iIrecseq) { int iremote; iremote = IHDRWIN_GETCHAN (zsend[IHDR_REMOTE]); zsend[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote); zsend[IHDR_CHECK] = IHDRCHECK_VAL (zsend); iIlocal_ack = iIrecseq; } ++cIresent_packets; clen = CHDRCON_GETBYTES (zsend[IHDR_CONTENTS1], zsend[IHDR_CONTENTS2]); return (*pfIsend) (qdaemon->qconn, zsend, CHDRLEN + clen + (clen > 0 ? CCKSUMLEN : 0), TRUE); } } case SPOS: /* Set the file position. */ { char abpos[CCKSUMLEN]; const char *zpos; if (cfirst >= CCKSUMLEN) zpos = zfirst; else { memcpy (abpos, zfirst, (size_t) cfirst); memcpy (abpos + cfirst, zsecond, (size_t) (CCKSUMLEN - cfirst)); zpos = abpos; } iIrecpos = (long) ICKSUM_GET (zpos); DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_packet: Got SPOS %ld", iIrecpos); return TRUE; } case CLOSE: { boolean fexpected; fexpected = ! fLog_sighup || fIclosing; if (! fexpected) ulog (LOG_ERROR, "Received unexpected CLOSE packet"); else DEBUG_MESSAGE0 (DEBUG_PROTO, "fiprocess_packet: Got CLOSE packet"); fIclosing = TRUE; *pfexit = TRUE; return fexpected; } default: /* Just ignore unrecognized packet types, for future protocol enhancements. */ DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_packet: Got packet type %d", ttype); return TRUE; } /*NOTREACHED*/ } uucp-1.07/protj.c0000664000076400007640000004520507665321755007440 /* protj.c The 'j' protocol. Copyright (C) 1992, 1994, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char protj_rcsid[] = "$Id: protj.c,v 1.9 2002/03/05 19:10:41 ian Rel $"; #endif #include #include #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "trans.h" #include "system.h" #include "prot.h" /* The 'j' protocol. The 'j' protocol is a wrapper around the 'i' protocol, which avoids the use of certain characters, such as XON and XOFF. Each 'j' protocol packet begins with a '^' character, followed by a two byte encoded size giving the total number of bytes in the packet. The first byte is HIGH, the second byte is LOW, and the number of bytes is (HIGH - 32) * 64 + (LOW - 32), where 32 <= HIGH < 127 and 32 <= LOW < 96 (i.e., HIGH and LOW are printable ASCII characters). This is followed by a '=' character. The next two bytes are the number of data bytes in the packet, using the same encoding. This is followed by a '@' character, and then that number of data bytes. The remaining bytes in the packet are indices of bytes which must be transformed, followed by a trailing '~' character. The indices are encoded in the following overly complex format. Each byte index is two bytes long. The first byte in the index is INDEX-HIGH and the second is INDEX-LOW. If 32 <= INDEX-HIGH < 126, the byte index refers to the byte at position (INDEX-HIGH - 32) * 32 + INDEX-LOW % 32 in the actual data, where 32 <= INDEX-LOW < 127. If 32 <= INDEX-LOW < 64, then 128 must be added to the indexed byte. If 64 <= INDEX-LOW < 96, then the indexed byte must be exclusive or'red with 32. If 96 <= INDEX-LOW < 127, both operations must be performed. If INDEX-HIGH == 126, then the byte index refers to the byte at position (INDEX-LOW - 32) * 32 + 31, where 32 <= INDEX-LOW < 126. 128 must be added to the byte, and it must be exclusive or'red with 32. This unfortunately requires a special test (when encoding INDEX-LOW must be checked for 127; when decoding INDEX-HIGH must be checked for 126). It does, however, permit the byte indices field to consist exclusively of printable ASCII characters. The maximum value for a byte index is (125 - 32) * 32 + 31 == 3007, so the is the maximum number of data bytes permitted. Since it is convenient to have each 'j' protocol packet correspond to each 'i' protocol packet, we restrict the 'i' protocol accordingly. Note that this encoding method assumes that we can send all printable ASCII characters. */ /* The first byte of each packet. I just picked these values randomly, trying to get characters that were perhaps slightly less likely to appear in normal text. */ #define FIRST '\136' /* The fourth byte of each packet. */ #define FOURTH '\075' /* The seventh byte of each packet. */ #define SEVENTH '\100' /* The trailing byte of each packet. */ #define TRAILER '\176' /* The length of the header. */ #define CHDRLEN (7) /* Get a number of bytes encoded in a two byte length at the start of a packet. */ #define CGETLENGTH(b1, b2) (((b1) - 32) * 64 + ((b2) - 32)) /* Set the high and low bytes of a two byte length at the start of a packet. */ #define ISETLENGTH_FIRST(i) ((i) / 64 + 32) #define ISETLENGTH_SECOND(i) ((i) % 64 + 32) /* The maximum packet size we support, as determined by the byte indices. */ #define IMAXPACKSIZE ((125 - 32) * 32 + 31) /* Amount to offset the bytes in the byte index by. */ #define INDEX_OFFSET (32) /* Maximum value of INDEX-LOW, before offsetting. */ #define INDEX_MAX_LOW (32) /* Maximum value of INDEX-HIGH, before offsetting. */ #define INDEX_MAX_HIGH (94) /* The set of characters to avoid. */ static char *zJavoid; /* The number of characters to avoid. */ static size_t cJavoid; /* A buffer used when sending data. */ static char *zJbuf; /* The end of the undecoded data in abPrecbuf. */ static int iJrecend; /* Local functions. */ static boolean fjsend_data P((struct sconnection *qconn, const char *zsend, size_t csend, boolean fdoread)); static boolean fjreceive_data P((struct sconnection *qconn, size_t cneed, size_t *pcrec, int ctimeout, boolean freport)); static boolean fjprocess_data P((size_t *pcneed)); /* Start the protocol. We first send over the list of characters to avoid as an escape sequence, starting with FIRST and ending with TRAILER. There is no error checking done on this string. */ boolean fjstart (qdaemon, pzlog) struct sdaemon *qdaemon; char **pzlog; { size_t clen; char *zsend; int b; size_t cbuf, cgot; char *zbuf; size_t i; /* Send the characters we want to avoid to the other side. */ clen = strlen (zJavoid_parameter); zsend = zbufalc (clen + 3); zsend[0] = FIRST; memcpy (zsend + 1, zJavoid_parameter, clen); zsend[clen + 1] = TRAILER; zsend[clen + 2] = '\0'; if (! fsend_data (qdaemon->qconn, zsend, clen + 2, TRUE)) { ubuffree (zsend); return FALSE; } ubuffree (zsend); /* Read the characters the other side wants to avoid. */ while ((b = breceive_char (qdaemon->qconn, cIsync_timeout, TRUE)) != FIRST) { if (b < 0) { if (b == -1) ulog (LOG_ERROR, "Timed out in 'j' protocol startup"); return FALSE; } } cbuf = 20; zbuf = zbufalc (cbuf); cgot = 0; while ((b = breceive_char (qdaemon->qconn, cIsync_timeout, TRUE)) != TRAILER) { if (b < 0) { ubuffree (zbuf); if (b == -1) ulog (LOG_ERROR, "Timed out in 'j' protocol startup"); return FALSE; } if (cgot + 1 >= cbuf) { char *znew; cbuf += 20; znew = zbufalc (cbuf); memcpy (znew, zbuf, cgot); ubuffree (zbuf); zbuf = znew; } zbuf[cgot] = b; ++cgot; } zbuf[cgot] = '\0'; /* Merge the local and remote avoid bytes into one list, translated into bytes. */ cgot = cescape (zbuf); clen = strlen (zJavoid_parameter); zJavoid = zbufalc (clen + cgot + 1); memcpy (zJavoid, zJavoid_parameter, clen + 1); cJavoid = cescape (zJavoid); for (i = 0; i < cgot; i++) { if (memchr (zJavoid, zbuf[i], cJavoid) == NULL) { zJavoid[cJavoid] = zbuf[i]; ++cJavoid; } } ubuffree (zbuf); /* We can't avoid ASCII printable characters, since the encoding method assumes that they can always be sent. If it ever turns out to be important, a different encoding method could be used, perhaps keyed by a different FIRST character. */ if (cJavoid == 0) { ulog (LOG_ERROR, "No characters to avoid in 'j' protocol"); return FALSE; } for (i = 0; i < cJavoid; i++) { if (zJavoid[i] >= 32 && zJavoid[i] <= 126) { ulog (LOG_ERROR, "'j' protocol can't avoid character '\\%03o'", zJavoid[i]); return FALSE; } } /* If we are avoiding XON and XOFF, use XON/XOFF handshaking. */ if (memchr (zJavoid, '\021', cJavoid) != NULL && memchr (zJavoid, '\023', cJavoid) != NULL) { if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, STRIPSETTING_EIGHTBITS, XONXOFF_ON)) return FALSE; } /* Let the port settle. */ usysdep_sleep (2); /* Allocate a buffer we use when sending data. We will probably never actually need one this big; if this code is ported to a computer with small amounts of memory, this should be changed to increase the buffer size as needed. */ zJbuf = zbufalc (CHDRLEN + IMAXPACKSIZE * 3 + 1); zJbuf[0] = FIRST; zJbuf[3] = FOURTH; zJbuf[6] = SEVENTH; /* iJrecend is the end of the undecoded data, and iPrecend is the end of the decoded data. At this point there is no decoded data, and we must initialize the variables accordingly. */ iJrecend = iPrecend; iPrecend = iPrecstart; /* Now do the 'i' protocol startup. */ return fijstart (qdaemon, pzlog, IMAXPACKSIZE, fjsend_data, fjreceive_data); } /* Shut down the protocol. */ boolean fjshutdown (qdaemon) struct sdaemon *qdaemon; { boolean fret; fret = fishutdown (qdaemon); ubuffree (zJavoid); ubuffree (zJbuf); return fret; } /* Encode a packet of data and send it. This copies the data, which is a waste of time, but calling fsend_data three times (for the header, the body, and the trailer) would waste even more time. */ static boolean fjsend_data (qconn, zsend, csend, fdoread) struct sconnection *qconn; const char *zsend; size_t csend; boolean fdoread; { char *zput, *zindex; const char *zfrom, *zend; char bfirst, bsecond; int iprecendhold; boolean fret; zput = zJbuf + CHDRLEN; zindex = zput + csend; zfrom = zsend; zend = zsend + csend; /* Optimize for the common case of avoiding two characters. */ bfirst = zJavoid[0]; if (cJavoid <= 1) bsecond = bfirst; else bsecond = zJavoid[1]; while (zfrom < zend) { char b; boolean f128, f32; int i, ihigh, ilow; b = *zfrom++; if (b != bfirst && b != bsecond) { int ca; char *za; if (cJavoid <= 2) { *zput++ = b; continue; } ca = cJavoid - 2; za = zJavoid + 2; while (ca-- != 0) if (*za++ == b) break; if (ca < 0) { *zput++ = b; continue; } } if ((b & 0x80) == 0) f128 = FALSE; else { b &=~ 0x80; f128 = TRUE; } if (b >= 32 && b != 127) f32 = FALSE; else { b ^= 0x20; f32 = TRUE; } /* We must now put the byte index into the buffer. The byte index is encoded similarly to the length of the actual data, but the byte index also encodes the operations that must be performed on the byte. The first byte in the index is the most significant bits. If we only had to subtract 128 from the byte, we use the second byte directly. If we had to xor the byte with 32, we add 32 to the second byte index. If we had to perform both operations, we add 64 to the second byte index. However, if we had to perform both operations, and the second byte index was 31, then after adding 64 and offsetting by 32 we would come up with 127, which we are not permitted to use. Therefore, in this special case we set the first byte of the index to 126 and put the original first byte into the second byte position instead. This is why we could not permit the high byte of the length of the actual data to be 126. We can get away with the switch because both the value of the second byte index (31) and the operations to perform (both) are known. */ i = zput - (zJbuf + CHDRLEN); ihigh = i / INDEX_MAX_LOW; ilow = i % INDEX_MAX_LOW; if (f128 && ! f32) ; else if (f32 && ! f128) ilow += INDEX_MAX_LOW; else { /* Both operations had to be performed. */ if (ilow != INDEX_MAX_LOW - 1) ilow += 2 * INDEX_MAX_LOW; else { ilow = ihigh; ihigh = INDEX_MAX_HIGH; } } *zindex++ = ihigh + INDEX_OFFSET; *zindex++ = ilow + INDEX_OFFSET; *zput++ = b; } *zindex++ = TRAILER; /* Set the lengths into the buffer. zJbuf[0,3,6] were set when zJbuf was allocated, and are never changed thereafter. */ zJbuf[1] = ISETLENGTH_FIRST (zindex - zJbuf); zJbuf[2] = ISETLENGTH_SECOND (zindex - zJbuf); zJbuf[4] = ISETLENGTH_FIRST (csend); zJbuf[5] = ISETLENGTH_SECOND (csend); /* Send the data over the line. We must preserve iPrecend as discussed in fjreceive_data. */ iprecendhold = iPrecend; iPrecend = iJrecend; fret = fsend_data (qconn, zJbuf, (size_t) (zindex - zJbuf), fdoread); iJrecend = iPrecend; iPrecend = iprecendhold; /* Process any bytes that may have been placed in abPrecbuf. */ if (fret && iPrecend != iJrecend) { if (! fjprocess_data ((size_t *) NULL)) return FALSE; } return fret; } /* Receive and decode data. This is called by fiwait_for_packet. We need to be able to return decoded data between iPrecstart and iPrecend, while not losing any undecoded partial packets we may have read. We use iJrecend as a pointer to the end of the undecoded data, and set iPrecend for the decoded data. iPrecend points to the start of the undecoded data. */ static boolean fjreceive_data (qconn, cineed, pcrec, ctimeout, freport) struct sconnection *qconn; size_t cineed; size_t *pcrec; int ctimeout; boolean freport; { int iprecendstart; size_t cjneed; size_t crec; int cnew; iprecendstart = iPrecend; /* Figure out how many bytes we need to decode the next packet. */ if (! fjprocess_data (&cjneed)) return FALSE; /* As we long as we read some data but don't have enough to decode a packet, we try to read some more. We decrease the timeout each time so that we will not wait forever if the connection starts dribbling data. */ do { int iprecendhold; size_t cneed; if (cjneed > cineed) cneed = cjneed; else cneed = cineed; /* We are setting iPrecend to the end of the decoded data for the 'i' protocol. When we do the actual read, we have to set it to the end of the undecoded data so that any undecoded data we have received is not overwritten. */ iprecendhold = iPrecend; iPrecend = iJrecend; if (! freceive_data (qconn, cneed, &crec, ctimeout, freport)) return FALSE; iJrecend = iPrecend; iPrecend = iprecendhold; /* Process any data we have received. This will set iPrecend to the end of the new decoded data. */ if (! fjprocess_data (&cjneed)) return FALSE; cnew = iPrecend - iprecendstart; if (cnew < 0) cnew += CRECBUFLEN; if ((size_t) cnew > cineed) cineed = 0; else cineed -= cnew; --ctimeout; } while (cnew == 0 && crec > 0 && ctimeout > 0); DEBUG_MESSAGE1 (DEBUG_PROTO, "fjreceive_data: Got %d decoded bytes", cnew); *pcrec = cnew; return TRUE; } /* Decode the data in the buffer, optionally returning the number of bytes needed to complete the next packet. */ static boolean fjprocess_data (pcneed) size_t *pcneed; { int istart; istart = iPrecend; while (istart != iJrecend) { int i, iget; char ab[CHDRLEN]; int cpacket, cdata, chave; int iindex, iendindex; /* Find the next occurrence of FIRST. If we have to skip some garbage bytes to get to it, zero them out (so they don't confuse the 'i' protocol) and advance iPrecend. This will save us from looking at them again. */ if (abPrecbuf[istart] != FIRST) { int cintro; char *zintro; size_t cskipped; cintro = iJrecend - istart; if (cintro < 0) cintro = CRECBUFLEN - istart; zintro = memchr (abPrecbuf + istart, FIRST, (size_t) cintro); if (zintro == NULL) { bzero (abPrecbuf + istart, (size_t) cintro); istart = (istart + cintro) % CRECBUFLEN; iPrecend = istart; continue; } cskipped = zintro - (abPrecbuf + istart); bzero (abPrecbuf + istart, cskipped); istart += cskipped; iPrecend = istart; } for (i = 0, iget = istart; i < CHDRLEN && iget != iJrecend; ++i, iget = (iget + 1) % CRECBUFLEN) ab[i] = abPrecbuf[iget]; if (i < CHDRLEN) { if (pcneed != NULL) *pcneed = CHDRLEN - i; return TRUE; } cpacket = CGETLENGTH (ab[1], ab[2]); cdata = CGETLENGTH (ab[4], ab[5]); /* Make sure the header has the right magic characters, that the data is not larger than the packet, and that we have an even number of byte index characters. */ if (ab[3] != FOURTH || ab[6] != SEVENTH || cdata > cpacket - CHDRLEN - 1 || (cpacket - cdata - CHDRLEN - 1) % 2 == 1) { istart = (istart + 1) % CRECBUFLEN; continue; } chave = iJrecend - istart; if (chave < 0) chave += CRECBUFLEN; if (chave < cpacket) { if (pcneed != NULL) *pcneed = cpacket - chave; return TRUE; } /* Figure out where the byte indices start and end. */ iindex = (istart + CHDRLEN + cdata) % CRECBUFLEN; iendindex = (istart + cpacket - 1) % CRECBUFLEN; /* Make sure the magic trailer character is there. */ if (abPrecbuf[iendindex] != TRAILER) { istart = (istart + 1) % CRECBUFLEN; continue; } /* We have a packet to decode. The decoding process is simpler than the encoding process, since all we have to do is examine the byte indices. We zero out the byte indices as we go, so that they will not confuse the 'i' protocol. */ while (iindex != iendindex) { int ihigh, ilow; boolean f32, f128; int iset; ihigh = abPrecbuf[iindex] - INDEX_OFFSET; abPrecbuf[iindex] = 0; iindex = (iindex + 1) % CRECBUFLEN; ilow = abPrecbuf[iindex] - INDEX_OFFSET; abPrecbuf[iindex] = 0; iindex = (iindex + 1) % CRECBUFLEN; /* Now we must undo the encoding, by adding 128 and xoring with 32 as appropriate. Which to do is encoded in the low byte, except that if the high byte is the special value 126, then the low byte is actually the high byte and both operations are performed. */ f128 = TRUE; f32 = TRUE; if (ihigh == INDEX_MAX_HIGH) iset = ilow * INDEX_MAX_LOW + INDEX_MAX_LOW - 1; else { iset = ihigh * INDEX_MAX_LOW + ilow % INDEX_MAX_LOW; if (ilow < INDEX_MAX_LOW) f32 = FALSE; else if (ilow < 2 * INDEX_MAX_LOW) f128 = FALSE; } /* Now iset is the index from the start of the data to the byte to modify; adjust it to an index in abPrecbuf. */ iset = (istart + CHDRLEN + iset) % CRECBUFLEN; if (f128) abPrecbuf[iset] |= 0x80; if (f32) abPrecbuf[iset] ^= 0x20; } /* Zero out the header and trailer to avoid confusing the 'i' protocol, and update iPrecend to the end of decoded data. */ for (i = 0, iget = istart; i < CHDRLEN && iget != iJrecend; ++i, iget = (iget + 1) % CRECBUFLEN) abPrecbuf[iget] = 0; abPrecbuf[iendindex] = 0; iPrecend = (iendindex + 1) % CRECBUFLEN; istart = iPrecend; } if (pcneed != NULL) *pcneed = CHDRLEN + 1; return TRUE; } uucp-1.07/proty.c0000664000076400007640000004225207665321755007456 /* proty.c The 'y' protocol. Copyright (C) 1994, 1995, 2002, 2003 Jorge Cwik and Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include "uucp.h" #if USE_RCS_ID const char proty_id[] = "$Id: proty.c,v 1.9 2003/05/29 06:00:49 ian Rel $"; #endif #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "trans.h" #include "system.h" #include "prot.h" /* The 'y' protocol, and this implementation, was written and designed by Jorge Cwik . Some of the routines, and the coding style in general, were taken verbatim or adapted from other Taylor UUCP modules. Mark Delany made the initial testings and helped in portability issues. This protocol does not perform any kind of error correction or flow control. It does do error checking. It does not require an end to end reliable link. It is recommended for error-free (also called semi-reliable) connections as provided by error correcting modems. It needs an eight bit clean channel and some kind of flow control at the lower layers, typically RTS/CTS hardware flow control. The flow of the file transmission is completely unidirectional. There are no ACKs or NAKs outside file boundaries. This makes it very suitable for half duplex modulations (like PEP) and connections with very long delays, like multihop satellite links. */ /* This protocol uses 16 bit little-endian ints in the packet header. */ #define FROMLITTLE(p) (((p)[0] & 0xff) + (((p)[1] & 0xff) << 8)) #define TOLITTLE(p, i) ((p)[0] = (i) & 0xff, (p)[1] = ((i) >> 8) & 0xff) /* The buffer and packet size we use. */ #define CYBUFSIZE (1024) #define IYPACKSIZE (1024) /* The offset in the buffer to the data. */ #define CYFRAMELEN (6) /* Offsets in a packet header. */ #define YFRAME_SEQ_OFF (0) #define YFRAME_LEN_OFF (2) #define YFRAME_CTL_OFF (2) #define YFRAME_CHK_OFF (4) /* Offsets in a packet header viewed as an array of shorts. */ #define YFRAME_SEQ (0) #define YFRAME_LEN (1) #define YFRAME_CTL (1) #define YFRAME_CHK (2) /* The default timeout. */ #define CYTIMEOUT (60) /* Control packet types. */ #define YPKT_ACK (0xFFFE) #define YPKT_ERR (0xFFFD) #define YPKT_BAD (0xFFFC) /* The protocol version number. */ #define Y_VERSION (1) /* When the protocol starts up, it transmit the following information: 1 byte version 1 byte packet size 2 byte flags (none currently defined) Future revision may expand the structure as long as these members keep their current offset. */ #define Y_INIT_HDR_LEN (4) #define Y_INIT_HDR_VERSION_OFF (0) #define Y_INIT_HDR_PKTSIZE_OFF (1) #define Y_INIT_HDR_FLAGS_OFF (2) /* The initialization length of the lowest accepted version. */ #define MIN_Y_SYNC (4) /* Not strictly needed, but I would not want to accept a 32k sync pkt. */ #define MAX_Y_SYNC IYPACKSIZE /* Local and remote packet sizes (we actually use the same value for both). */ static size_t iYlocal_packsize = IYPACKSIZE; static size_t iYremote_packsize = IYPACKSIZE; /* Local and remote packet sequence numbers. */ static unsigned short iYlocal_pktnum; static unsigned short iYremote_pktnum; /* The timeout. */ static int cYtimeout = CYTIMEOUT; /* Transmitter buffer. */ static char *zYbuf; /* Protocol parameters. */ struct uuconf_cmdtab asYproto_params[] = { { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cYtimeout, NULL }, { "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iYlocal_packsize, NULL }, { NULL, 0, NULL, NULL } }; /* Local functions. */ static boolean fyxchg_syncs P((struct sdaemon *qdaemon)); static boolean fywait_for_packet P((struct sdaemon *qdaemon, boolean *pfexit)); static unsigned short iychecksum P((const char *z, size_t c)); static unsigned short iychecksum2 P((const char *zfirst, size_t cfirst, const char *zsecond, size_t csecond)); static boolean fywait_for_header P((struct sdaemon *qdaemon, unsigned short header[3], int timeout)); static boolean fysend_pkt P((struct sdaemon *qdaemon, const void *zdata, size_t cdata)); static boolean fysend_control P((struct sdaemon *qdaemon, int itype)); static boolean fyread_data P((struct sdaemon *qdaemon, size_t clen, int timeout)); /* Exchange sync packets at protocol startup. */ static boolean fyxchg_syncs (qdaemon) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; { char inithdr[Y_INIT_HDR_LEN]; unsigned short header[3]; unsigned short ichk; size_t clen, cfirst; int rpktsize; /* Send our configuration. We could use only one array (for local and remote). But this is safer in case the code changes and depend on separate ones. */ inithdr[Y_INIT_HDR_VERSION_OFF] = Y_VERSION; inithdr[Y_INIT_HDR_PKTSIZE_OFF] = iYlocal_packsize >> 8; TOLITTLE (inithdr + Y_INIT_HDR_FLAGS_OFF, 0); if (! fysend_pkt (qdaemon, inithdr, Y_INIT_HDR_LEN)) return FALSE; if (! fywait_for_header (qdaemon, header, cYtimeout)) return FALSE; DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fyxchg_syncs: Got sync header"); clen = header[YFRAME_LEN]; if (clen < MIN_Y_SYNC || clen > MAX_Y_SYNC) { ulog (LOG_ERROR, "Bad 'y' protocol sync packet length"); return FALSE; } /* It may be better to integrate this code with fywait_for_packet. */ if (! fyread_data (qdaemon, clen, cYtimeout)) return FALSE; cfirst = CRECBUFLEN - iPrecstart; ichk = iychecksum2 (abPrecbuf + iPrecstart, cfirst, abPrecbuf, clen - cfirst); rpktsize = BUCHAR (abPrecbuf[(iPrecstart + 1) % CRECBUFLEN]); /* Future versions of the protocol may need to check and react according to the version number. */ if (rpktsize == 0 || header[YFRAME_CHK] != ichk) { ulog (LOG_ERROR, "Bad 'y' protocol sync packet"); return FALSE; } iYremote_packsize = rpktsize << 8; /* Some may want to do this different and in effect the protocol support this. But I like the idea that the packet size would be the same in both directions. This allows the caller to select both packet sizes without changing the configuration at the server. */ if (iYremote_packsize > iYlocal_packsize) iYremote_packsize = iYlocal_packsize; iPrecstart = (iPrecstart + clen) % CRECBUFLEN; return TRUE; } /* Start the protocol. */ boolean fystart (qdaemon, pzlog) struct sdaemon *qdaemon; char **pzlog; { *pzlog = NULL; /* This should force, or at least enable if available, RTS/CTS hardware flow control !! */ /* The 'y' protocol requires an eight bit clean link */ if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, STRIPSETTING_EIGHTBITS, XONXOFF_OFF)) return FALSE; iYlocal_pktnum = iYremote_pktnum = 0; /* Only multiple of 256 sizes are allowed */ iYlocal_packsize &= ~0xff; if (iYlocal_packsize < 256 || iYlocal_packsize > (16*1024)) iYlocal_packsize = IYPACKSIZE; /* Exhange SYNC packets */ if (! fyxchg_syncs (qdaemon)) { /* Restore defaults */ cYtimeout = CYTIMEOUT; iYlocal_packsize = IYPACKSIZE; return FALSE; } zYbuf = (char *) xmalloc (CYBUFSIZE + CYFRAMELEN); return TRUE; } /* Shutdown the protocol. */ boolean fyshutdown (qdaemon) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; { xfree ((pointer) zYbuf); zYbuf = NULL; cYtimeout = CYTIMEOUT; iYlocal_packsize = IYPACKSIZE; return TRUE; } /* Send a command string. We send packets containing the string until the entire string has been sent, including the zero terminator. */ /*ARGSUSED*/ boolean fysendcmd (qdaemon, z, ilocal, iremote) struct sdaemon *qdaemon; const char *z; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; { size_t clen; DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fysendcmd: Sending command \"%s\"", z); clen = strlen (z) + 1; while (clen > 0) { size_t csize; csize = clen; if (csize > iYremote_packsize) csize = iYremote_packsize; if (! fysend_pkt (qdaemon, z, csize)) return FALSE; z += csize; clen -= csize; } return TRUE; } /* Get space to be filled with data. We always use zYbuf, which was allocated from the heap. */ char * zygetspace (qdaemon, pclen) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; size_t *pclen; { *pclen = iYremote_packsize; return zYbuf + CYFRAMELEN; } /* Send out a data packet. */ boolean fysenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos) struct sdaemon *qdaemon; char *zdata; size_t cdata; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; long ipos ATTRIBUTE_UNUSED; { #if DEBUG > 0 if (cdata > iYremote_packsize) ulog (LOG_FATAL, "fysend_packet: Packet size too large"); #endif TOLITTLE (zYbuf + YFRAME_SEQ_OFF, iYlocal_pktnum); ++iYlocal_pktnum; TOLITTLE (zYbuf + YFRAME_LEN_OFF, cdata); TOLITTLE (zYbuf + YFRAME_CHK_OFF, iychecksum (zdata, cdata)); /* We pass FALSE to fsend_data since we don't expect the other side to be sending us anything just now. */ return fsend_data (qdaemon->qconn, zYbuf, cdata + CYFRAMELEN, FALSE); } /* Wait for data to come in and process it until we've finished a command or a file. */ boolean fywait (qdaemon) struct sdaemon *qdaemon; { boolean fexit = FALSE; while (! fexit) { if (! fywait_for_packet (qdaemon, &fexit)) return FALSE; } return TRUE; } /* File level routines We could handle this inside the other public routines, but this is cleaner and better for future expansions */ boolean fyfile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled) struct sdaemon *qdaemon; struct stransfer *qtrans ATTRIBUTE_UNUSED; boolean fstart; boolean fsend; long cbytes ATTRIBUTE_UNUSED; boolean *pfhandled; { unsigned short header[3]; *pfhandled = FALSE; if (! fstart) { if (fsend) { /* It is critical that the timeout here would be long enough. We have just sent a full file without any kind of flow control at the protocol level. The traffic may be buffered in many places of the link, and the remote may take a while until cathing up. */ if (! fywait_for_header (qdaemon, header, cYtimeout * 2)) return FALSE; if (header[YFRAME_CTL] != (unsigned short) YPKT_ACK) { DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, "fyfile: Error from remote: 0x%04X", header[1]); ulog (LOG_ERROR, "Received 'y' protocol error from remote"); return FALSE; } } else { /* This is technically not requireed. But I've put this in the protocol to allow easier expansions. */ return fysend_control (qdaemon, YPKT_ACK); } } return TRUE; } /* Send a control packet, not used during the normal file transmission. */ static boolean fysend_control (qdaemon, itype) struct sdaemon *qdaemon; int itype; { char header[CYFRAMELEN]; TOLITTLE (header + YFRAME_SEQ_OFF, iYlocal_pktnum); iYlocal_pktnum++; TOLITTLE (header + YFRAME_CTL_OFF, itype); TOLITTLE (header + YFRAME_CHK_OFF, 0); return fsend_data (qdaemon->qconn, header, CYFRAMELEN, FALSE); } /* Private function to send a packet. This one doesn't need the data to be in the buffer provided by zygetspace. I've found it worth for avoiding memory copies. Somebody may want to do it otherwise */ static boolean fysend_pkt (qdaemon, zdata, cdata) struct sdaemon *qdaemon; const void *zdata; size_t cdata; { char header[CYFRAMELEN]; TOLITTLE (header + YFRAME_SEQ_OFF, iYlocal_pktnum); iYlocal_pktnum++; TOLITTLE (header + YFRAME_LEN_OFF, cdata); TOLITTLE (header + YFRAME_CHK_OFF, iychecksum (zdata, cdata)); if (! fsend_data (qdaemon->qconn, header, CYFRAMELEN, FALSE)) return FALSE; return fsend_data (qdaemon->qconn, zdata, cdata, FALSE); } /* Wait until enough data arrived from the comm line. This protocol doesn't need to perform any kind of action while waiting. */ static boolean fyread_data (qdaemon, clen, timeout) struct sdaemon *qdaemon; size_t clen; int timeout; { int cinbuf; size_t crec; cinbuf = iPrecend - iPrecstart; if (cinbuf < 0) cinbuf += CRECBUFLEN; if ((size_t) cinbuf < clen) { if (! freceive_data (qdaemon->qconn, clen - cinbuf, &crec, timeout, TRUE)) return FALSE; cinbuf += crec; if ((size_t) cinbuf < clen) { if (! freceive_data (qdaemon->qconn, clen - cinbuf, &crec, timeout, TRUE)) return FALSE; } cinbuf += crec; if ((size_t) cinbuf < clen) { ulog (LOG_ERROR, "Timed out waiting for data"); return FALSE; } } return TRUE; } /* Receive a remote packet header, check for correct sequence number. */ static boolean fywait_for_header (qdaemon, header, timeout) struct sdaemon *qdaemon; unsigned short header[3]; int timeout; { if (! fyread_data (qdaemon, CYFRAMELEN, timeout)) return FALSE; /* Somebody may want to optimize this in a portable way. I'm not sure it's worth, but the output by gcc for the portable construct is so bad (even with optimization), that I couldn't resist. */ if (iPrecstart <= (CRECBUFLEN - CYFRAMELEN)) { header[0] = FROMLITTLE (abPrecbuf + iPrecstart); header[1] = FROMLITTLE (abPrecbuf + iPrecstart + 2); header[2] = FROMLITTLE (abPrecbuf + iPrecstart + 4); } else { register int i, j; for (i = j = 0; j < CYFRAMELEN; i++, j += 2) { header[i] = (((abPrecbuf[(iPrecstart + j + 1) % CRECBUFLEN] & 0xff) << 8) + (abPrecbuf[(iPrecstart + j) % CRECBUFLEN] & 0xff)); } } iPrecstart = (iPrecstart + CYFRAMELEN) % CRECBUFLEN; DEBUG_MESSAGE3 (DEBUG_UUCP_PROTO, "fywait_for_header: Got header: 0x%04X, 0x%04X, 0x%04X", header[0], header[1], header[2]); if (header[YFRAME_SEQ] != iYremote_pktnum++) { ulog (LOG_ERROR, "Incorrect 'y' packet sequence"); fysend_control (qdaemon, YPKT_BAD); return FALSE; } return TRUE; } /* Receive a remote data packet */ static boolean fywait_for_packet (qdaemon, pfexit) struct sdaemon *qdaemon; boolean *pfexit; { unsigned short header[3], ichk; size_t clen, cfirst; if (! fywait_for_header (qdaemon, header, cYtimeout)) return FALSE; clen = header[YFRAME_LEN]; if (clen == 0 && pfexit != NULL) { /* I Suppose the pointers could be NULL ??? */ return fgot_data (qdaemon, abPrecbuf, 0, abPrecbuf, 0, -1, -1, (long) -1, TRUE, pfexit); } if (clen & 0x8000) { DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL, "fywait_for_packet: Error from remote: 0x%04X", header[YFRAME_CTL]); ulog (LOG_ERROR, "Remote error packet"); return FALSE; } /* This is really not neccessary. But if this check is removed, take in mind that the packet may be up to 32k long. */ if (clen > iYlocal_packsize) { ulog (LOG_ERROR, "Packet too large"); return FALSE; } if (! fyread_data (qdaemon, clen, cYtimeout)) return FALSE; cfirst = CRECBUFLEN - iPrecstart; if (cfirst > clen) cfirst = clen; if (cfirst == clen) ichk = iychecksum (abPrecbuf + iPrecstart, clen); else ichk = iychecksum2 (abPrecbuf + iPrecstart, cfirst, abPrecbuf, clen - cfirst); if (header[YFRAME_CHK] != ichk) { DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL, "fywait_for_packet: Bad checksum 0x%x != 0x%x", header[YFRAME_CHK], ichk); fysend_control (qdaemon, YPKT_ERR); ulog (LOG_ERROR, "Checksum error"); return FALSE; } if (pfexit != NULL && ! fgot_data (qdaemon, abPrecbuf + iPrecstart, cfirst, abPrecbuf, clen - cfirst, -1, -1, (long) -1, TRUE, pfexit)) return FALSE; iPrecstart = (iPrecstart + clen) % CRECBUFLEN; return TRUE; } /* Compute 16 bit checksum */ #ifdef __GNUC__ #ifdef __i386__ #define I386_ASM #endif #endif #ifdef I386_ASM #define ROTATE(i) \ asm ("rolw $1,%0" : "=g" (i) : "g" (i)) #else #define ROTATE(i) i += i + ((i & 0x8000) >> 15) #endif static unsigned short iychecksum (z, c) register const char *z; register size_t c; { register unsigned short ichk; ichk = 0xffff; while (c-- > 0) { ROTATE (ichk); ichk += BUCHAR (*z++); } return ichk; } static unsigned short iychecksum2 (zfirst, cfirst, zsecond, csecond) const char *zfirst; size_t cfirst; const char *zsecond; size_t csecond; { register unsigned short ichk; register const char *z; register size_t c; z = zfirst; c = cfirst + csecond; ichk = 0xffff; while (c-- > 0) { ROTATE (ichk); ichk += BUCHAR (*z++); /* If the first buffer has been finished, switch to the second. */ if (--cfirst == 0) z = zsecond; } return ichk; } uucp-1.07/protz.c0000664000076400007640000020561607665321756007465 /* protz.c Version 1.5, 92Apr24 */ /* Modified by Ian Lance Taylor for Taylor UUCP 1.04 92Aug4. */ /* * Doug Evans, dje@sspiff.UUCP or dje@ersys.edmonton.ab.ca * * This file provides the Zmodem protocol (by Chuck Forsberg) for * Ian Taylor's UUCP package. * * It was originally developed to establish a uucp link between myself and my * employer: Ivation Datasystems, Inc. of Ottawa. * * My thanks to Ivation for letting me release this to the public. Given that * Zmodem is in the public domain, no additional copyrights have been added. * ***************************************************************************** * * It's been difficult fitting Zmodem into the UUCP world. I have been guided * mostly by trying to plug it into Taylor UUCP. Where "the Zmodem way of doing * things" conflicted with "the UUCP way of doing things", I have err'd on the * side of UUCP. At the end of it all, I have achieved something that will plug * into Taylor UUCP very easily, but some might argue that I have corrupted Z * too much. At any rate, compatibility with sz/rz was sacrificed to achieve a * clean UUCP protocol. Given that, I took the opportunity to start from * scratch when defining protocol constants (EG: ZBIN). * * 1) I wasn't quite sure how to enhance Zmodem to handle send+receive in one * session, so I added a 'g' protocol like initialization sequence. This * also gets this stuff out of the way, in case we ever try to support * full-duplex. * * Caller Callee * ------ ------ * ZINIT --> <-- ZINIT * ZDATA (ZCRCF) --> <-- ZDATA (ZCRCF) * ZACK --> <-- ZACK * ZINITEND --> <-- ZINITEND * * ZINIT is a combination of ZRINIT and ZSINIT and is intended to exchange * simple protocol information (flags) and the protocol version number. * ZDATA is intended to include window size information as well as the * "Myattn" string (although at the moment it doesn't contain anything). * ZDATA may contain at most 1k bytes of data and is sent out as one ZCRCF * packet. Two ack's (ZACK + ZINITEND) are needed to ensure both sides have * received ZDATA. * * 2) I've hardcoded several protocol parameters, like 32 bit CRC's for data. * Others are not supported (we don't need them). * * 3) ZHEX headers use 32 bit CRC's. * * 4) Zmodem sends the ZFILE message "in one chunk". If there are errors, the * entire string is resent. I have continued this practice. All UUCP * commands are sent "in one chunk". This can be changed down the road if * necessary. * * 5) The ZEOF message has been replaced with a new ZCRCx value: ZCRCF. ZCRCF * is identical to ZCRCW except that it indicates the end of the message. * The protocol here is *not* a file transfer protocol. It is an end to end * transport protocol (that preserves message boundaries). * * 6) Zmodem handles restarting a file transfer, but as best as I can tell UUCP * does not. At least Taylor UUCP doesn't. And if UUCP does start handling * file restart, can it be plugged into the existing Zmodem way with zero * changes? Beats me. Therefore I have removed this part of the code. One * can always put it back in if and when UUCP handles it. Ditto for other * pieces of removed code: there's no point in overly complicating this code * when supporting all the bells and whistles requires enhancements to UUCP * itself. * * *** It is easier to put code back in in an upward compatible manner *** * *** than it is to correct for misunderstood code or poorly merged *** * *** (Zmodem vs UUCP) code. *** * * 7) For the character in the initial "protocol selection" sequence, I have * chosen 'a'. I'm told 'z' is already in use for something that isn't * Zmodem. It's entirely reasonable to believe that if Zmodem ever becomes a * standard UUCP protocol, this won't be it (so I'll leave z/Z for them). * Publicly, this is the 'a' protocol. Internally, it is refered to as 'z'. * A little confusing, I know. Maybe in time I'll refer to it internally as * 'a', or maybe in time this will be *the* 'z' protocol. * * 8) Since we are writing a transport protocol, which isn't supposed to know * anything about what is being transferred or where it is coming from, the * header data value has changed meaning. It no longer means "file position" * but instead means "window position". It is a running counter of the bytes * transferred. Each "message" begins on a 1k boundary so the count isn't a * precise byte count. The counter wraps every 4 gigabytes, although this * wrapping isn't supported yet. * * FIXME: At present the max data transferred per session is 4 gigabytes. * **************************************************************************** * * A typical message sequence is (master sending file to slave): * * Master Slave * ------ ----- * ZDATA (S, ZCRCF) --> * <-- ZACK * <-- ZDATA (SY, ZCRCF) * ZACK --> * ZDATA --> * ... <-- ZACK/ZRPOS * ZDATA (ZCRCF) --> * <-- ZACK * <-- ZDATA (CY, ZCRCF) * ZACK --> * * A typical message sequence is (master receiving file from slave): * * Master Slave * ------ ----- * ZDATA (R, ZCRCF) --> * <-- ZACK * <-- ZDATA (RY, ZCRCF) * ZACK --> * <-- ZDATA * ZACK/ZRPOS ... --> * <-- ZDATA (ZCRCF) * ZACK --> * ZDATA (CY, ZCRCF) --> * <-- ZACK * ***************************************************************************** * * Notes: * 1) For future bidirectional concerns, keep packet types "unidirectional". * Sender always uses: ZDATA, ZNAK * Receiver always uses: ZRPOS, ZACK * There is no intersection. * * I'm not sure if this is necessary or even useful, but it seems to be. * * 2) I use to store the byte count / 32 in the data header. This left 5 bits * unused for future concerns. I removed this because of the following * situation when sending a file: * * ZDATA (ZCRCG, xx bytes) - received ok * ZDATA (ZCRCF, 0 bytes) - corrupted * * At this point the receiver would like to send back a ZRPOS with a value * of the size of the file. However, it can't because the value is divided * by 32, and it would have to round up to the next multiple of 32. This * seemed a little ugly, so I went with using the entire header to store * the byte count. * ***************************************************************************** * * Source version: * * 1.1,2,3 * Protocol version 0 * Early attempts, completely rewritten later. * * 1.4 Protocol version 1 * Beta test sent to Ian for analysis 92Apr18. * * 1.5 Protocol version 1 * Released 92Apr24. * ***************************************************************************** * * Protocol version: * * A version number is exchanged in the ZINIT message, so it is possible to * correct or enhance the protocol, without breaking existing versions. * The purpose of this section is to document these versions as they come out. * Remember, this is the protocol version, not the source version. * * 0 Initial version. * Zmodem controlled file transfer. This was more of a "plug Z * into UUCP as is" port. * * 1 Complete rewrite. * Made Z more of a transport protocol. UUCP now controls transfer and Z * is on the same footing as the other UUCP protocols. * Theoretically, there will be little pain when UUCP goes bidirectional. */ #include "uucp.h" #if USE_RCS_ID const char protz_rcsid[] = "$Id: protz.c,v 1.11 2002/02/08 10:35:52 ian Rel $"; #endif #include #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "trans.h" #include "system.h" #include "prot.h" #define ZPROTOCOL_VERSION 1 /* * Control message characters ... */ #define ZPAD '*' /* Padding character begins frames */ #define ZDLE 030 /* Ctrl-X Zmodem escape - `ala BISYNC DLE */ #define ZBIN 'A' /* Binary frame indicator */ #define ZHEX 'B' /* HEX frame indicator */ /* * Frame types (see array "frametypes" in zm.c) ... * * Note that the numbers here have been reorganized, as we don't support * all of them (nor do we need to). * * WARNING: The init sequence assumes ZINIT < ZDATA < ZACK < ZINITEND. */ #define ZINIT 0 /* Init (contains protocol version, flags) */ #define ZDATA 1 /* Data packet(s) follow */ #define ZRPOS 2 /* Resume data trans at this position */ #define ZACK 3 /* ACK to above */ #define ZNAK 4 /* Last packet was garbled */ #define Zreserved 5 /* reserved (for future concerns) */ #define ZINITEND 6 /* end of init sequence */ #define ZFIN 7 /* Finish session */ /* * ZDLE sequences ... * * Note addition of ZCRCF: "end of message". */ #define ZCRCE 'h' /* CRC next, frame ends, header packet follows */ #define ZCRCG 'i' /* CRC next, frame continues nonstop */ #define ZCRCQ 'j' /* CRC next, frame continues, ZACK expected */ #define ZCRCW 'k' /* CRC next, ZACK expected, end of frame */ #define ZCRCF 'l' /* CRC next, ZACK expected, end of message */ #define ZRUB0 'm' /* Translate to rubout 0177 */ #define ZRUB1 'n' /* Translate to rubout 0377 */ /* * zdlread return values (internal) ... * Other values are ZM_ERROR, ZM_TIMEOUT, ZM_RCDO. */ #define GOTOR 0400 #define GOTCRCE (ZCRCE | GOTOR) /* ZDLE-ZCRCE received */ #define GOTCRCG (ZCRCG | GOTOR) /* ZDLE-ZCRCG received */ #define GOTCRCQ (ZCRCQ | GOTOR) /* ZDLE-ZCRCQ received */ #define GOTCRCW (ZCRCW | GOTOR) /* ZDLE-ZCRCW received */ #define GOTCRCF (ZCRCF | GOTOR) /* ZDLE-ZCRCF received */ /* * Byte positions within header array ... */ #define ZF0 3 /* First flags byte */ #define ZF1 2 #define ZF2 1 #define ZF3 0 #define ZP0 0 /* Low order 8 bits of position */ #define ZP1 1 #define ZP2 2 #define ZP3 3 /* High order 8 bits of position */ /* * Bit Masks for ZRQINIT flags byte ZF0 ... */ #define TX_ESCCTL 1 /* Tx will escape control chars */ /* * Possible errors when running ZMODEM ... */ #define ZM_ERROR (-1) /* crc error, etc. */ #define ZM_TIMEOUT (-2) #define ZM_RCDO (-3) /* Carrier Lost */ /* * ASCII characters ... */ #define LF 012 #define CR 015 #define XON 021 #define XOFF 023 #define XON_WAIT 10 /* seconds */ /* * Packet sizes ... * * FIXME: CPACKETSIZE is hardcoded in a lot of places. * It's not clear to me whether changing it's value would be a * "good thing" or not. But of course that doesn't excuse the hardcoding. */ #define CPACKETSIZE 1024 /* max packet size (data only) */ #define CFRAMELEN 12 /* header size */ #define CSUFFIXLEN 10 /* suffix at end of data packets */ #define CEXCHANGE_INIT_RETRIES 4 /* The header CRC value. */ #if ANSI_C #define IHDRCRC 0xDEBB20E3UL #else #define IHDRCRC ((unsigned long) 0xDEBB20E3L) #endif /* packet buffer size */ #define CPACKBUFSIZE (CFRAMELEN + 2 * CPACKETSIZE + CSUFFIXLEN + 42 /*slop*/) /* * Data types ... */ typedef unsigned char achdrval_t[4]; typedef unsigned long hdrval_t; typedef unsigned long winpos_t; /* * Configurable parms ... * * FIXME: isn't used yet. It may not be needed. */ #define CTIMEOUT 10 #define CRETRIES 10 #define CSTARTUP_RETRIES 4 #define CGARBAGE 2400 #define CSEND_WINDOW 16384 #define FESCAPE_CONTROL FALSE static int cZtimeout = CTIMEOUT; /* (seconds) */ static int cZretries = CRETRIES; static int cZstartup_retries = CSTARTUP_RETRIES; static int cZmax_garbage = CGARBAGE; /* max garbage before header */ static int cZtx_window = CSEND_WINDOW; /* our transmission window */ static int cZrx_buf_len = 0; /* our reception buffer size */ static boolean fZesc_ctl = FESCAPE_CONTROL; /* escape control chars */ struct uuconf_cmdtab asZproto_params[] = { {"timeout", UUCONF_CMDTABTYPE_INT, (pointer) & cZtimeout, NULL}, {"retries", UUCONF_CMDTABTYPE_INT, (pointer) & cZretries, NULL}, {"startup-retries", UUCONF_CMDTABTYPE_INT, (pointer) & cZstartup_retries, NULL}, {"garbage", UUCONF_CMDTABTYPE_INT, (pointer) & cZmax_garbage, NULL}, {"send-window", UUCONF_CMDTABTYPE_INT, (pointer) & cZtx_window, NULL}, {"escape-control", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) & fZesc_ctl, NULL}, {NULL, 0, NULL, NULL} }; /* * Variables for statistic gathering ... * * We use to record the number of "packets" * sent/received. Packets is in double quotes because some of them aren't full. */ static unsigned long cZheaders_sent; static unsigned long cZheaders_received; static unsigned long cZbytes_resent; static unsigned long cZtimeouts; static unsigned long cZerrors; /* * Data buffers ... */ static char *zZtx_buf; /* transmit buffer */ static char *zZtx_packet_buf; /* raw outgoing packet data */ static char *zZrx_packet_buf; /* raw incoming packet data */ /* * Transmitter state variables ... */ static unsigned cZblklen; /* data length in sent/received packets */ static unsigned cZtxwspac; /* spacing between ZCRCQ requests */ /*static unsigned cZblklen_override;*//* override value for */ static unsigned cZtxwcnt; /* counter used to space ack requests */ static unsigned cZrxwcnt; /* counter used to watch receiver's buf size */ static winpos_t wpZtxstart; /* when message started */ static winpos_t wpZtxpos; /* transmitter position */ static winpos_t wpZlastsync; /* last offset to which we got a ZRPOS */ static winpos_t wpZlrxpos; /* receiver's last reported offset */ static winpos_t wpZrxpos; /* receiver file position */ static int iZlast_tx_data_packet; /* type of last ZDATA packet (ZCRCx) */ static int iZjunk_count; /* amount of garbage characters received */ static int iZtleft; /* for dynamic packet resizing */ static int iZbeenhereb4; /* times we've been ZRPOS'd to same place */ /* * Receiver state variables ... */ static winpos_t wpZrxbytes; /* receiver byte count */ static int iZlast_rx_data_packet; /* last successfully received ZCRCx packet */ /* * Misc. globals ... */ static char xon = XON; #ifdef DJE_TESTING int uucptest = -1; int uucptest2; int uucptestseed; #endif /* * Kludge!!! * See fzfinish_tx(). Basically the next two globals are used to record the * fact that we got a ZDATA, but aren't quite ready to process it. */ static int iZpkt_rcvd_kludge; /* -1 if not valid */ static hdrval_t hvZpkt_hdrval_kludge; /* * Packet types ... */ static const char *azZframe_types[] = { "Carrier Lost", /* -3 */ "Timeout", /* -2 */ "Error", /* -1 */ #define FTOFFSET 3 "ZINIT", "ZDATA", "ZRPOS", "ZACK", "ZNAK", "Zreserved", "ZINITEND", "ZFIN", "UNKNOWN!!!" }; #define FTNUMBER (sizeof(azZframe_types) / sizeof(char *)) #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #endif #define ZZHEADER_NAME(itype) \ azZframe_types[min((size_t) ((itype) + FTOFFSET), FTNUMBER - 1)] /* * Local functions ... */ static boolean fzsend_data P((struct sdaemon *qdaemon, char *zdata, size_t cdata, boolean fendofmessage)); static boolean fzprocess P((struct sdaemon *qdaemon)); static boolean fzstart_proto P((struct sdaemon *qdaemon)); static int izexchange_init P((struct sdaemon *qdaemon, int send_type, achdrval_t send_val, achdrval_t recv_val)); static boolean fzshutdown_proto P((struct sdaemon *qdaemon)); static boolean fzstart_tx P((void)); static boolean fzfinish_tx P((struct sdaemon *qdaemon, long *plredo)); static boolean fzstart_rx P((void)); static boolean fzfinish_rx P((struct sdaemon *qdaemon)); static boolean fzsend_hdr P((struct sdaemon *qdaemon, int ipkttype, int ihdrtype, hdrval_t hdrval, boolean fcheckreceive)); static boolean fzsend_data_packet P((struct sdaemon *qdaemon, char *zdata, size_t cdata, int frameend, boolean fcheckreceive)); static int czbuild_header P((char *zresult, int ipkttype, int ihdrtype, hdrval_t hdrval)); static int czbuild_data_packet P((char *zresult, const char *zdata, size_t cdata, int frameend)); /* * The rest of the functions do not follow Ian's naming style. I have left * the names the same as the original zm source. Over time, they may change. */ static int izrecv_hdr P((struct sdaemon *qdaemon, achdrval_t hdr)); static int zrbhdr32 P((struct sdaemon *qdaemon, achdrval_t hdr)); static int zrhhdr P((struct sdaemon *qdaemon, achdrval_t hdr)); static int zrdat32 P((struct sdaemon *qdaemon, char *buf, int length, int *iprxcount)); static int getinsync P((struct sdaemon *qdaemon, boolean flag)); static char *zputhex P((char *p, int ch)); static char *zputchar P((char *p, int ch)); static int zgethex P((struct sdaemon *qdaemon)); static int zdlread P((struct sdaemon *qdaemon)); static int noxrd7 P((struct sdaemon *qdaemon)); static int realreadchar P((struct sdaemon *qdaemon, int timeout)); static boolean fzreceive_ready P((void)); static void stohdr P((hdrval_t pos, achdrval_t hdr)); static hdrval_t rclhdr P((achdrval_t hdr)); static hdrval_t hvzencode_data_hdr P((winpos_t cbytes)); static void zdecode_data_hdr P((hdrval_t hdrval, winpos_t *pcbytes)); static winpos_t lzupdate_rxpos P((achdrval_t rx_hdr, winpos_t rxpos, winpos_t lrxpos, winpos_t txpos)); /* * This macro replaces readchar() because it achieves a noticable speed up. The * readchar() function has been renamed realreadchar(). Thanks to Ian for * running this stuff through a profiler to find this out. Ian suggests further * speed ups may be obtained by doing a similar thing in zrdat32(). */ /* Assign the next character to b. */ #define READCHAR(qdaemon, b, i) \ (iPrecstart != iPrecend \ ? ((b) = BUCHAR (abPrecbuf[iPrecstart]), \ iPrecstart = (iPrecstart + 1) % CRECBUFLEN) \ : ((b) = realreadchar ((qdaemon), (i)))) /************************************************************************/ /* * Start the protocol ... */ boolean fzstart(qdaemon, pzlog) struct sdaemon *qdaemon; char **pzlog; { *pzlog = zbufalc (sizeof "protocol 'a' starting: , , , , , " + 100); sprintf (*pzlog, "protocol 'a' starting: %d, %d, %d, %d, %d, %d", cZtimeout, cZretries, cZstartup_retries, cZmax_garbage, cZtx_window, fZesc_ctl); if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE, STRIPSETTING_EIGHTBITS, XONXOFF_OFF)) return FALSE; /* * For now, we place tight restrictions on the size of the transmit * window. This might be relaxed in the future. If it is relaxed, * some of these tests will stay, some will go. That is why it is * coded like it is. */ if (cZtx_window % 1024 != 0 || cZtx_window < 4096 || cZtx_window > 65536 || 65536 % cZtx_window != 0 ) { ulog (LOG_ERROR, "fzstart: cZtx_window not one of 4096, 8192, 16384, 32768, 65536"); return FALSE; } zZtx_buf = (char *) xmalloc (CPACKETSIZE); zZtx_packet_buf = (char *) xmalloc (CPACKBUFSIZE); zZrx_packet_buf = (char *) xmalloc (CPACKBUFSIZE); iZlast_tx_data_packet = -1; iZlast_rx_data_packet = -1; wpZtxpos = wpZlrxpos = wpZrxpos = wpZrxbytes = 0; cZtxwspac = cZtx_window / 4; cZheaders_sent = cZheaders_received = cZbytes_resent = 0; cZtimeouts = cZerrors = 0; iZpkt_rcvd_kludge = -1; #if 0 /* * We ensure is at least 4k, so the following is * unnecessary. It can be put back in later if needed. */ if (cZblklen_override > cZtxwspac || (!cZblklen_override && cZtxwspac < 1024)) cZblklen_override = cZtxwspac; #endif #ifdef DJE_TESTING { extern int uucptest,uucptest2,uucptestseed; FILE *f; if (uucptest == -1) { f = fopen ("/usr/local/src/bin/uucp/uucptest", "r"); if (f != NULL) { fscanf (f, "%d %d %d", &uucptestseed, &uucptest, &uucptest2); fclose (f); } srand (uucptestseed); } } #endif /* * Fire up the protocol (exchange init messages) ... */ if (!fzstart_proto (qdaemon)) return FALSE; return TRUE; } /* * Stop the protocol ... */ boolean fzshutdown(qdaemon) struct sdaemon *qdaemon; { (void) fzshutdown_proto (qdaemon); xfree ((pointer) zZtx_buf); xfree ((pointer) zZtx_packet_buf); xfree ((pointer) zZrx_packet_buf); zZtx_buf = NULL; zZtx_packet_buf = NULL; zZrx_packet_buf = NULL; /* * Print some informative statistics ... * * I use the word "messages" here instead of "headers" because the * latter is jargonese. */ ulog (LOG_NORMAL, "Protocol 'a' messages: sent %lu, received %lu", cZheaders_sent, cZheaders_received); ulog (LOG_NORMAL, "Protocol 'a' packets: sent %lu, received %lu", wpZtxpos / 1024, wpZrxbytes / 1024); if (cZbytes_resent != 0 || cZtimeouts != 0 || cZerrors != 0) ulog (LOG_NORMAL, "Protocol 'a' errors: bytes resent %lu, timeouts %lu, errors %lu", cZbytes_resent, cZtimeouts, cZerrors); /* * Reset all the parameters to their default values, so that the * protocol parameters used for this connection do not affect the * next one. */ cZtimeout = CTIMEOUT; cZretries = CRETRIES; cZstartup_retries = CSTARTUP_RETRIES; cZmax_garbage = CGARBAGE; cZtx_window = CSEND_WINDOW; fZesc_ctl = FESCAPE_CONTROL; cZheaders_sent = cZheaders_received = cZbytes_resent = 0; cZtimeouts = cZerrors = 0; return TRUE; } /* * Send a command string ... * We send everything up to and including the null byte. * * We assume the command will fit in the outgoing data buffer. * FIXME: A valid assumption? */ /*ARGSUSED*/ boolean fzsendcmd(qdaemon, z, ilocal, iremote) struct sdaemon *qdaemon; const char *z; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; { size_t n,clen; long lredo; char *zbuf; clen = strlen (z) + 1; DEBUG_MESSAGE1 (DEBUG_PROTO, "fzsendcmd: sending command %s", z); if (!fzstart_tx ()) /* must be called before zzgetspace() */ return FALSE; if ((zbuf = zzgetspace (qdaemon, &n)) == NULL) return FALSE; #if DEBUG > 0 if (clen > n) ulog (LOG_FATAL, "fzsendcmd: clen > n"); #endif strcpy (zbuf, z); /* * Send it out ... */ do { if (!fzsend_data (qdaemon, zbuf, clen, TRUE)) return FALSE; if (!fzfinish_tx (qdaemon, &lredo)) return FALSE; } while (lredo >= 0); return fzprocess (qdaemon); } /* * Allocate a packet to send out ... * * Note that 'z' has dynamic packet resizing and that will range * from 32 to 1024, in multiples of 2. */ /*ARGSUSED*/ char * zzgetspace(qdaemon, pclen) struct sdaemon *qdaemon ATTRIBUTE_UNUSED; size_t *pclen; { *pclen = cZblklen; return zZtx_buf; } /* * Send a block of data ... * * If (cdata == 0) then the end of the file has been reached. */ /*ARGSUSED*/ boolean fzsenddata(qdaemon, zdata, cdata, ilocal, iremote, ipos) struct sdaemon *qdaemon; char *zdata; size_t cdata; int ilocal ATTRIBUTE_UNUSED; int iremote ATTRIBUTE_UNUSED; long ipos ATTRIBUTE_UNUSED; { DEBUG_MESSAGE1 (DEBUG_PROTO, "fzsenddata: %d bytes", cdata); if (! fzsend_data (qdaemon, zdata, cdata, cdata == 0)) return FALSE; return fzprocess (qdaemon); } /* * Send a block of data (command or file) ... */ /* This should buffer the data internally. Until it does, it needs to be able to reset the file position when it is called. This is really ugly. */ extern struct stransfer *qTsend; static boolean fzsend_data(qdaemon, zdata, cdata, fendofmessage) struct sdaemon *qdaemon; char *zdata; size_t cdata; boolean fendofmessage; { size_t n; if (iZlast_tx_data_packet == -1 || iZlast_tx_data_packet == ZCRCW) { cZtxwcnt = cZrxwcnt = 0; iZjunk_count = 0; if (!fzsend_hdr (qdaemon, ZBIN, ZDATA, hvzencode_data_hdr (wpZtxpos), TRUE)) return FALSE; } n = cdata; if (fendofmessage) iZlast_tx_data_packet = ZCRCF; else if (iZjunk_count > 3) iZlast_tx_data_packet = ZCRCW; else if (wpZtxpos == wpZlastsync) iZlast_tx_data_packet = ZCRCW; else if (cZrx_buf_len && (cZrxwcnt += n) >= (size_t) cZrx_buf_len) iZlast_tx_data_packet = ZCRCW; else if ((cZtxwcnt += n) >= cZtxwspac) { iZlast_tx_data_packet = ZCRCQ; cZtxwcnt = 0; } else iZlast_tx_data_packet = ZCRCG; if (++iZtleft > 3) { iZtleft = 0; if (cZblklen < 1024) cZblklen *= 2; #if 0 /* is currently unnecessary */ if (cZblklen_override && cZblklen > cZblklen_override) cZblklen = cZblklen_override; #endif if (cZblklen > 1024) cZblklen = 1024; if (cZrx_buf_len && cZblklen > (size_t) cZrx_buf_len) cZblklen = cZrx_buf_len; } #if DEBUG > 1 if (FDEBUGGING(DEBUG_PROTO)) { const char *type; switch (iZlast_tx_data_packet) { case ZCRCW: type = "ZCRCW"; break; case ZCRCG: type = "ZCRCG"; break; case ZCRCQ: type = "ZCRCQ"; break; case ZCRCE: type = "ZCRCE"; break; case ZCRCF: type = "ZCRCF"; break; default : type = "UNKNOWN!!!"; break; } DEBUG_MESSAGE3 (DEBUG_PROTO, "fzsend_data: %s, pos 0x%lx, %d bytes", type, wpZtxpos, n); } #endif if (!fzsend_data_packet (qdaemon, zdata, n, iZlast_tx_data_packet, TRUE)) return FALSE; wpZtxpos += n; if (iZlast_tx_data_packet == ZCRCW) { /* * FIXME: Ideally this would be done in fzprocess. However, it * is only called if there is data pending which there * may not be yet. I could have patched fploop() a bit but * for now, I've done it like this. */ switch (getinsync (qdaemon, FALSE)) { case ZACK: break; case ZRPOS: if (qTsend == NULL || ! ffileisopen (qTsend->e)) { ulog (LOG_ERROR, "Can't reset non-file"); return FALSE; } iZlast_tx_data_packet = -1; /* trigger ZDATA */ DEBUG_MESSAGE1 (DEBUG_PROTO, "fzsend_data: Seeking to %ld", (long) (wpZrxpos - wpZtxstart)); if (!ffileseek (qTsend->e, wpZrxpos - wpZtxstart)) { ulog (LOG_ERROR, "seek: %s", strerror (errno)); return FALSE; } break; default: return FALSE; } return TRUE; } /* * If we've reached the maximum transmit window size, let the * receiver catch up ... * * I use (cZtx_window - 2048) to play it safe. */ while (wpZtxpos - wpZlrxpos >= (size_t) cZtx_window - 2048) { if (iZlast_tx_data_packet != ZCRCQ) { if (!fzsend_data_packet (qdaemon, zdata, (size_t) 0, iZlast_tx_data_packet = ZCRCQ, TRUE)) return FALSE; } /* * FIXME: I'd rather not call ffileseek() in this file. When we * start buffering the outgoing data, the following * ffileseek() will disappear. */ switch (getinsync (qdaemon, TRUE)) { case ZACK: break; case ZRPOS: if (qTsend == NULL || ! ffileisopen (qTsend->e)) { ulog (LOG_ERROR, "Can't reset non-file"); return FALSE; } iZlast_tx_data_packet = -1; /* trigger ZDATA */ DEBUG_MESSAGE1 (DEBUG_PROTO, "fzsend_data: Seeking to %ld", (long) (wpZrxpos - wpZtxstart)); if (!ffileseek (qTsend->e, wpZrxpos - wpZtxstart)) { ulog (LOG_ERROR, "seek: %s", strerror (errno)); return FALSE; } break; default: return FALSE; } } return TRUE; } /* * Process existing data ... */ static boolean fzprocess(qdaemon) struct sdaemon *qdaemon; { int c,ch; while (fzreceive_ready ()) { READCHAR (qdaemon, ch, 1); switch (ch) { case ZPAD: /* see if we're detecting ZRPOS packets quickly */ DEBUG_MESSAGE0 (DEBUG_PROTO, "fzprocess: possible ZRPOS packet"); /* We just ate the ZPAD char that getinsync expects, so put it back. */ iPrecstart = ((iPrecstart + CRECBUFLEN - 1) % CRECBUFLEN); c = getinsync (qdaemon, TRUE); if (c == ZACK) break; /* FIXME: sz does a TCFLSH here */ #if 0 /* FIXME: Not sure if this is needed, or where to put it. */ /* ZCRCE - dinna wanna starta ping-pong game */ if (!fzsend_data_packet (qdaemon, zZtx_packet_buf, 0, ZCRCE, TRUE)) return FALSE; #endif if (c == ZRPOS) { if (qTsend == NULL || ! ffileisopen (qTsend->e)) { ulog (LOG_ERROR, "Attempt to back up non-file"); return FALSE; } if (! ffileseek (qTsend->e, wpZrxpos - wpZtxstart)) { ulog (LOG_ERROR, "seek: %s", strerror (errno)); return FALSE; } iZlast_tx_data_packet = -1; /* trigger ZDATA */ break; /* not returning is intentional */ } return FALSE; case XOFF: case XOFF | 0200: READCHAR (qdaemon, ch, XON_WAIT); break; case CR: break; default: iZjunk_count++; break; } } return TRUE; } /* * Wait for data to come in. * * This continues processing until a complete file or command has been * received. */ boolean fzwait(qdaemon) struct sdaemon *qdaemon; { int c,cerr,rxcount; boolean fexit; achdrval_t rx_hdr; if (!fzstart_rx ()) return FALSE; cerr = cZretries; goto nxthdr; for (;;) { if (!fzsend_hdr (qdaemon, ZHEX, ZRPOS, hvzencode_data_hdr (wpZrxbytes), FALSE)) return FALSE; nxthdr: c = izrecv_hdr (qdaemon, rx_hdr); switch (c) { case ZM_TIMEOUT: case ZNAK: if (--cerr < 0) { ulog (LOG_ERROR, "fzwait: retries exhausted"); return FALSE; } continue; case ZM_ERROR: if (--cerr < 0) { ulog (LOG_ERROR, "fzwait: retries exhausted"); return FALSE; } /*fport_break ();*/ continue; case ZM_RCDO: case ZFIN: return FALSE; case ZRPOS: case ZACK: goto nxthdr; /* ignore, partner is out of sync */ case ZDATA: { winpos_t rx_bytes; zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes); DEBUG_MESSAGE2 (DEBUG_PROTO, "fzwait: bytes(us,them) 0x%lx,0x%lx", wpZrxbytes, rx_bytes); if (rx_bytes != wpZrxbytes) { if (--cerr < 0) { ulog (LOG_ERROR, "fzwait: retries exhausted"); return FALSE; } (void) zrdat32 (qdaemon, zZrx_packet_buf, 1024, &rxcount); /*fport_break ();*/ /* * FIXME: Seems to me we should ignore this one * and go for a timeout, the theory being * that the appropriate ZRPOS has already * been sent. We're obviously out of sync. * /dje 92Mar10 */ continue; /* goto nxthdr? */ } moredata: /* * Do not call fgot_data() with (rxcount == 0) if it's * not ZCRCF. fgot_data() will erroneously think this * is the end of the message. */ c = zrdat32 (qdaemon, zZrx_packet_buf, 1024, &rxcount); #if DEBUG > 1 if (FDEBUGGING(DEBUG_PROTO)) { const char *msg; if (c < 0) { msg = ZZHEADER_NAME(c); } else { switch (c) { case GOTCRCW: msg = "ZCRCW"; break; case GOTCRCG: msg = "ZCRCG"; break; case GOTCRCQ: msg = "ZCRCQ"; break; case GOTCRCE: msg = "ZCRCE"; break; case GOTCRCF: msg = "ZCRCF"; break; default : msg = NULL; break; } } if (msg != NULL) DEBUG_MESSAGE2 (DEBUG_PROTO, "fzwait: zrdat32: %s, %d bytes", msg, rxcount); else DEBUG_MESSAGE2 (DEBUG_PROTO, "fzwait: zrdat32: %d, %d bytes", c, rxcount); } #endif switch (c) { case ZM_ERROR: /* CRC error */ cZerrors++; if (--cerr < 0) { ulog (LOG_ERROR, "fzwait: retries exhausted"); return FALSE; } /*fport_break ();*/ continue; case ZM_TIMEOUT: cZtimeouts++; if (--cerr < 0) { ulog (LOG_ERROR, "fzwait: retries exhausted"); return FALSE; } continue; case ZM_RCDO: return FALSE; case GOTCRCW: iZlast_rx_data_packet = ZCRCW; cerr = cZretries; if (rxcount != 0 && !fgot_data (qdaemon, zZrx_packet_buf, (size_t) rxcount, (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, &fexit)) return FALSE; wpZrxbytes += rxcount; if (!fzsend_hdr (qdaemon, ZHEX, ZACK, hvzencode_data_hdr (wpZrxbytes), FALSE)) return FALSE; if (! fsend_data (qdaemon->qconn, &xon, (size_t) 1, FALSE)) return FALSE; goto nxthdr; case GOTCRCQ: iZlast_rx_data_packet = ZCRCQ; cerr = cZretries; if (rxcount != 0 && !fgot_data (qdaemon, zZrx_packet_buf, (size_t) rxcount, (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, &fexit)) return FALSE; wpZrxbytes += rxcount; if (!fzsend_hdr (qdaemon, ZHEX, ZACK, hvzencode_data_hdr (wpZrxbytes), FALSE)) return FALSE; goto moredata; case GOTCRCG: iZlast_rx_data_packet = ZCRCG; cerr = cZretries; if (rxcount != 0 && !fgot_data (qdaemon, zZrx_packet_buf, (size_t) rxcount, (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, &fexit)) return FALSE; wpZrxbytes += rxcount; goto moredata; case GOTCRCE: iZlast_rx_data_packet = ZCRCE; cerr = cZretries; if (rxcount != 0 && !fgot_data (qdaemon, zZrx_packet_buf, (size_t) rxcount, (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, &fexit)) return FALSE; wpZrxbytes += rxcount; goto nxthdr; case GOTCRCF: iZlast_rx_data_packet = ZCRCF; /* * fzfinish_rx() must be called before * fgot_data() because fgot_data() will send * out a UUCP-command but the sender won't be * ready for it until it receives our final * ZACK. */ cerr = cZretries; wpZrxbytes += rxcount; if (!fzfinish_rx (qdaemon)) return FALSE; if (!fgot_data (qdaemon, zZrx_packet_buf, (size_t) rxcount, (const char *) NULL, (size_t) 0, -1, -1, (long) -1, TRUE, &fexit)) return FALSE; /* * FIXME: Examine ? * Or maybe ensure it's TRUE? */ return TRUE; } return FALSE; } default: ulog (LOG_FATAL, "fzwait: received header %s", ZZHEADER_NAME(c)); return FALSE; } } } /* * File level routine. Called when initiating/terminating file transfers. * * When starting to send a file: (TRUE, TRUE, cbytes) * When starting to receive a file: (TRUE, FALSE, -1) * When send EOF, check resend: (FALSE, TRUE, -1) * When receive EOF, check re-receive: (FALSE, FALSE, -1) */ boolean fzfile(qdaemon, qtrans, fstart, fsend, cbytes, pfhandled) struct sdaemon *qdaemon; struct stransfer *qtrans; boolean fstart; boolean fsend; long cbytes ATTRIBUTE_UNUSED; boolean *pfhandled; { long iredo; *pfhandled = FALSE; DEBUG_MESSAGE2 (DEBUG_PROTO, "fzfile: fstart=%d, fsend=%d", fstart, fsend); if (fsend) { if (fstart) return fzstart_tx (); if (! fzfinish_tx (qdaemon, &iredo)) return FALSE; if (iredo >= 0) { if (! ffileisopen (qtrans->e)) { ulog (LOG_ERROR, "Attempt to back up non-file"); return FALSE; } if (! ffileseek (qtrans->e, iredo)) { ulog (LOG_ERROR, "seek: %s", strerror (errno)); return FALSE; } *pfhandled = TRUE; qtrans->fsendfile = TRUE; return fqueue_send (qdaemon, qtrans); } } return TRUE; } /****************************************************************************/ #if 0 /* not used, we only use 32 bit crc's */ /* * crctab calculated by Mark G. Mendel, Network Systems Corporation */ static unsigned short crctab[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; #endif /* crctab */ /* * Copyright (C) 1986 Gary S. Brown. You may use this program, or * code or tables extracted from it, as desired without restriction. */ /* First, the polynomial itself and its table of feedback terms. The */ /* polynomial is */ /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ /* Note that we take it "backwards" and put the highest-order term in */ /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ /* the MSB being 1. */ /* Note that the usual hardware shift register implementation, which */ /* is what we're using (we're merely optimizing it by doing eight-bit */ /* chunks at a time) shifts bits into the lowest-order term. In our */ /* implementation, that means shifting towards the right. Why do we */ /* do it this way? Because the calculated CRC must be transmitted in */ /* order from highest-order term to lowest-order term. UARTs transmit */ /* characters in order from LSB to MSB. By storing the CRC this way, */ /* we hand it to the UART in the order low-byte to high-byte; the UART */ /* sends each low-bit to hight-bit; and the result is transmission bit */ /* by bit from highest- to lowest-order term without requiring any bit */ /* shuffling on our part. Reception works similarly. */ /* The feedback terms table consists of 256, 32-bit entries. Notes: */ /* */ /* The table can be generated at runtime if desired; code to do so */ /* is shown later. It might not be obvious, but the feedback */ /* terms simply represent the results of eight shift/xor opera- */ /* tions for all combinations of data and CRC register values. */ /* */ /* The values must be right-shifted by eight bits by the "updcrc" */ /* logic; the shift must be unsigned (bring in zeroes). On some */ /* hardware you could probably optimize the shift in assembler by */ /* using byte-swap instructions. */ static unsigned long crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; /* * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. * NOTE: First argument must be in range 0 to 255. * Second argument is referenced twice. * * Programmers may incorporate any or all code into their programs, * giving proper credit within the source. Publication of the * source routines is permitted so long as proper credit is given * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, * Omen Technology. */ #define updcrc(cp, crc) (crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp) #define UPDC32(b, crc) \ (crc_32_tab[((unsigned)(crc) ^ (unsigned)(b)) & 0xff] \ ^ (((crc) >> 8) & 0x00ffffffL)) /****************************************************************************/ /* * This section contains the guts of the Zmodem protocol. The intention * is to leave as much of it alone as possible at the start. Overtime it * will be cleaned up (EG: I'd like to clean up the naming of the globals). * Also, Zmodem has a different coding style. Over time this will be converted * to the Taylor UUCP coding style. */ /* * Start the protocol (exchange init packets) ... * * UUCP can transfer files in both directions in one session. Therefore the * init sequence is a little different. * * 1) ZINIT packets are exchanged * - contains protocol version and protocol flags * 2) ZDATA packets are exchanged * - is intended to contain various numeric and string information * 3) ZACK packets are exchanged * 4) ZINITEND packets are exchanged */ static boolean fzstart_proto(qdaemon) struct sdaemon *qdaemon; { int i; achdrval_t tx_hdr,rx_hdr; for (i = 0; i < cZstartup_retries; i++) { stohdr (0L, tx_hdr); tx_hdr[ZF0] = ZPROTOCOL_VERSION; if (fZesc_ctl) tx_hdr[ZF1] |= TX_ESCCTL; switch (izexchange_init (qdaemon, ZINIT, tx_hdr, rx_hdr)) { case -1: return FALSE; case 0: continue; case 1: break; } #if 0 /* can't work, but kept for documentation */ if (rx_hdr[ZF0] == 0) { ulog (LOG_ERROR, "Old protocol version, init failed"); return FALSE; } #endif fZesc_ctl = fZesc_ctl || (rx_hdr[ZF1] & TX_ESCCTL) != 0; stohdr (0L, tx_hdr); switch (izexchange_init (qdaemon, ZDATA, tx_hdr, rx_hdr)) { case -1: return FALSE; case 0: continue; case 1: break; } stohdr (0L, tx_hdr); switch (izexchange_init (qdaemon, ZACK, tx_hdr, rx_hdr)) { case -1: return FALSE; case 0: continue; case 1: break; } stohdr (0L, tx_hdr); switch (izexchange_init (qdaemon, ZINITEND, tx_hdr, rx_hdr)) { case -1: return FALSE; case 0: continue; case 1: break; } DEBUG_MESSAGE0 (DEBUG_PROTO, "fzstart_proto: Protocol started"); return TRUE; /* FIXME: see protg.c regarding sequencing here. */ } ulog (LOG_ERROR, "Protocol init failed"); return FALSE; } /* * Exchange init messages. This is based on 'g'. * See the comments concerning fgexchange_init() in protg.c. * * We return 1 for success, 0 for restart, -1 for comm failure (terminate). */ static int izexchange_init(qdaemon, send_type, send_val, recv_val) struct sdaemon *qdaemon; int send_type; achdrval_t send_val; achdrval_t recv_val; { int i,recv_type,count; for (i = 0; i < CEXCHANGE_INIT_RETRIES; i++) { if (!fzsend_hdr (qdaemon, send_type == ZDATA ? ZBIN : ZHEX, send_type, rclhdr (send_val), FALSE)) return -1; /* * The ZDATA packet is intended to contain the string * (eventually, if it's ever usable) and allow for anything * else that will need to be thrown in. */ if (send_type == ZDATA) { count = czbuild_data_packet (zZtx_packet_buf, "", (size_t) 1, ZCRCF); if (!fsend_data (qdaemon->qconn, zZtx_packet_buf, (size_t) count, FALSE)) return -1; } recv_type = izrecv_hdr (qdaemon, recv_val); switch (recv_type) { case ZM_TIMEOUT: case ZM_ERROR: continue; case ZM_RCDO: case ZFIN: return -1; case ZINIT: case ZACK: case ZINITEND: break; case ZDATA: if (zrdat32 (qdaemon, zZrx_packet_buf, 1024, &count) == GOTCRCF) break; continue; default: continue; } if (recv_type == send_type) return 1; /* * If the other side is farther along than we are, we have lost * a packet. Fall immediately back to ZINIT (but don't fail * if we are already doing ZINIT, since that would count * against cStart_retries more than it should). * * FIXME: The ">" test is "<" in protg.c. Check who's right. */ if (recv_type > send_type && send_type != ZINIT) return 0; /* * If we are sending ZINITEND and we receive an ZINIT, the * other side has falled back (we know this because we have * seen a ZINIT from them). Fall back ourselves to start * the whole handshake over again. */ if (recv_type == ZINIT && send_type == ZINITEND) return 0; } return 0; } /* * Shut down the protocol ... */ static boolean fzshutdown_proto(qdaemon) struct sdaemon *qdaemon; { (void) fzsend_hdr (qdaemon, ZHEX, ZFIN, 0L, FALSE); return TRUE; } /* * Reset the transmitter side for sending a new message ... */ static boolean fzstart_tx() { iZlast_tx_data_packet = -1; /* * is set to -1L to suppress ZCRCW request otherwise * triggered by (wpZlastsync == wpZtxpos). */ cZblklen = 1024; wpZlastsync = -1L; iZbeenhereb4 = 0; iZtleft = 0; iZjunk_count = 0; wpZtxpos = (wpZtxpos + 1024L) & ~1023L; /* next packet boundary */ wpZlrxpos = wpZrxpos = wpZtxpos; wpZtxstart = wpZtxpos; /* so we can compute the "file offset" */ return TRUE; } /* * Finish the sending of a message ... * * Basically, we wait for some indication that the receiver received our last * message. If the receiver tells us to restart from some point, we set * *plredo to that point. * * FIXME: This function is a major kludge at the moment. It is taken from * getinsync(). It is necessary because I don't yet buffer outgoing data. * It will go away when we do (buffer outgoing data). */ static boolean fzfinish_tx(qdaemon, plredo) struct sdaemon *qdaemon; long *plredo; { int c,cerr,ctimeouts; achdrval_t rx_hdr; winpos_t rx_bytes; *plredo = -1; cerr = cZretries; ctimeouts = 0; DEBUG_MESSAGE4 (DEBUG_PROTO, "fzfinish_tx: txpos=0x%lx, rxpos=0x%lx, lrxpos=0x%lx, rxbytes=0x%lx", wpZtxpos, wpZrxpos, wpZlrxpos, wpZrxbytes); for (;;) { c = izrecv_hdr (qdaemon, rx_hdr); switch (c) { case ZRPOS: wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos, wpZlrxpos, wpZtxpos); /* * If the receiver sends a ZRPOS for the 1k block after * the one we're currently at, we lost the final ZACK. * We cheat and ignore this ZRPOS. Remember: the theory * is that this entire function will go away when we * begin buffering the outgoing data. Of course, one * can reword the protocol definition and say this * isn't cheating at all. */ if (((wpZtxpos + 1024) & ~1023) == wpZrxpos) return TRUE; cZbytes_resent += wpZtxpos - wpZrxpos; wpZlrxpos = wpZtxpos = wpZrxpos; if (wpZlastsync == wpZrxpos) { if (++iZbeenhereb4 > 4) if (cZblklen > 32) cZblklen /= 2; /* FIXME: shouldn't we reset iZbeenhereb4? */ } wpZlastsync = wpZrxpos; iZlast_tx_data_packet = ZCRCW; /* force a timeout */ *plredo = wpZrxpos - wpZtxstart; return TRUE; case ZACK: wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos, wpZlrxpos, wpZtxpos); wpZlrxpos = wpZrxpos; if (wpZtxpos == wpZrxpos) /* the ACK we want? */ return TRUE; break; case ZDATA: /* * We cheat here and take advantage of UUCP's current * half duplex nature. If we get a ZDATA starting on * the next 1k boundary, we lost the ZACK. We cheat and * tuck it away so that izrecv_hdr() can later detect * it. Remember: see above. */ zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes); if (((wpZrxbytes + 1024L) & ~1023L) == rx_bytes) { iZpkt_rcvd_kludge = ZDATA; hvZpkt_hdrval_kludge = rclhdr (rx_hdr); return TRUE; } break; /* ignore, out of sync (old) */ case ZNAK: /* * We cheat here and take advantage of UUCP's current * half duplex nature. If we get a ZNAK starting on * the next 1k boundary, we lost the ZACK. We cheat and * throw the ZNAK away. Remember: see above. * * On the other hand, if (rx_bytes == wpZrxbytes) then * the other side is also in fzfinish_tx(). He must * have lost our ZACK, so we send him another. */ zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes); if (((wpZrxbytes + 1024L) & ~1023L) == rx_bytes) return TRUE; if (rx_bytes == wpZrxbytes) { if (!fzsend_hdr (qdaemon, ZHEX, ZACK, hvzencode_data_hdr (wpZrxbytes), TRUE)) return FALSE; } break; /* ignore, out of sync (old) */ case ZFIN: case ZM_RCDO: return FALSE; case ZM_TIMEOUT: if (--cerr < 0) { ulog (LOG_ERROR, "fzfinish_tx: retries exhausted"); return FALSE; } /* * Normally the sender doesn't send NAK's for timeouts. * We have to here because of the following scenario: * * - We send ZDATA/ZCRCF * - They send ZACK (corrupted) * - They send ZDATA/ZCRCF (corrupted) * * At this point, both sides are in fzfinish_tx(). * We only send ZNAK every second timeout to increase * our timeout delay vs. our partner. This tries to * avoid ZRPOS and ZNAK "passing in transit". */ if (++ctimeouts % 2 == 0) if (!fzsend_hdr (qdaemon, ZHEX, ZNAK, hvzencode_data_hdr (wpZtxpos), TRUE)) return FALSE; break; case ZM_ERROR: default: if (--cerr < 0) { ulog (LOG_ERROR, "fzfinish_tx: retries exhausted"); return FALSE; } if (!fzsend_hdr (qdaemon, ZHEX, ZNAK, hvzencode_data_hdr (wpZtxpos), TRUE)) return FALSE; break; } } } /* * Initialize the receiver ... */ static boolean fzstart_rx() { wpZrxbytes = (wpZrxbytes + 1024L) & ~1023L; /* next packet boundary */ return TRUE; } /* * Terminate the receiver ... * * Acknowledge the last packet received. */ static boolean fzfinish_rx(qdaemon) struct sdaemon *qdaemon; { DEBUG_MESSAGE0 (DEBUG_PROTO, "fzfinish_rx: message/file received"); return fzsend_hdr (qdaemon, ZHEX, ZACK, hvzencode_data_hdr (wpZrxbytes), FALSE); } /* * Send a Zmodem header to our partner ... */ static boolean fzsend_hdr(qdaemon, ipkttype, ihdrtype, hdrval, fcheckreceive) struct sdaemon *qdaemon; int ipkttype; int ihdrtype; hdrval_t hdrval; boolean fcheckreceive; { int cpacketlen; DEBUG_MESSAGE2 (DEBUG_PROTO, "fzsend_hdr: %s, data = 0x%lx", ZZHEADER_NAME(ihdrtype), hdrval); cpacketlen = czbuild_header (zZtx_packet_buf, ipkttype, ihdrtype, hdrval); #ifdef DJE_TESTING #if 0 if (ihdrtype == ZACK && rand () % 100 < uucptest2) { cZheaders_sent++; return TRUE; } #else if (ihdrtype == ZACK || ihdrtype == ZDATA) { boolean fresult; int old; extern int uucptest,uucptest2; old = uucptest; uucptest = uucptest2; cZheaders_sent++; fresult = fsend_data (qdaemon->qconn, zZtx_packet_buf, (size_t) cpacketlen, fcheckreceive); uucptest = old; return fresult; } #endif #endif cZheaders_sent++; return fsend_data (qdaemon->qconn, zZtx_packet_buf, (size_t) cpacketlen, fcheckreceive); } /* * Send a data packet to our partner ... * is one of ZCRCx. */ static boolean fzsend_data_packet(qdaemon, zdata, cdata, frameend, fcheckreceive) struct sdaemon *qdaemon; char *zdata; size_t cdata; int frameend; boolean fcheckreceive; { int cpacketlen; cpacketlen = czbuild_data_packet (zZtx_packet_buf, zdata, cdata, frameend); return fsend_data (qdaemon->qconn, zZtx_packet_buf, (size_t) cpacketlen, fcheckreceive); } /* * Build Zmodem headers ... * * Note that we use 32 bit CRC's for ZHEX headers. * * This function is a combination of zm fns: zsbhdr(), zsbh32(), and zshhdr(). */ static int czbuild_header(zresult, ipkttype, ihdrtype, hdrval) char *zresult; int ipkttype; int ihdrtype; hdrval_t hdrval; { char *p; int i; unsigned long crc; achdrval_t achdrval; p = zresult; switch (ipkttype) { case ZBIN: *p++ = ZPAD; *p++ = ZDLE; *p++ = ZBIN; p = zputchar (p, ihdrtype); crc = ICRCINIT; crc = UPDC32 (ihdrtype, crc); stohdr (hdrval, achdrval); for (i = 0; i < 4; i++) { p = zputchar (p, achdrval[i]); crc = UPDC32 (achdrval[i], crc); } crc = ~crc; for (i = 0; i < 4; i++) { p = zputchar (p, (char) crc); crc >>= 8; } break; case ZHEX: /* build hex header */ *p++ = ZPAD; *p++ = ZPAD; *p++ = ZDLE; *p++ = ZHEX; p = zputhex (p, ihdrtype); crc = ICRCINIT; crc = UPDC32 (ihdrtype, crc); stohdr (hdrval, achdrval); for (i = 0; i < 4; i++) { p = zputhex (p, achdrval[i]); crc = UPDC32 (achdrval[i], crc); } crc = ~crc; for (i = 0; i < 4; i++) { p = zputhex (p, (char) crc); crc >>= 8; } *p++ = CR; /* * Uncork the remote in case a fake XOFF has stopped data flow. */ if (ihdrtype != ZFIN && ihdrtype != ZACK) /* FIXME: why? */ *p++ = XON; break; default: ulog (LOG_FATAL, "czbuild_header: ipkttype == %d", ipkttype); break; } return p - zresult; } /* * Build Zmodem data packets ... * * This function is zsdata() and zsda32() from the zm source. */ static int czbuild_data_packet(zresult, zdata, cdata, frameend) char *zresult; const char *zdata; size_t cdata; int frameend; { char *p; unsigned long crc; p = zresult; crc = ICRCINIT; for ( ; cdata-- != 0; zdata++) { char c; c = *zdata; if (c & 0140) *p++ = c; else p = zputchar (p, c); crc = UPDC32 ((unsigned char) c, crc); } *p++ = ZDLE; *p++ = frameend; crc = UPDC32 (frameend, crc); crc = ~crc; for (cdata = 0; cdata < 4; cdata++) { p = zputchar (p, (char) crc); crc >>= 8; } if (frameend == ZCRCW || frameend == ZCRCE || frameend == ZCRCF) { *p++ = CR; *p++ = XON; } return p - zresult; } /* * Read in a header ... * * This is function zgethdr() from the Zmodem source. */ static int izrecv_hdr(qdaemon, hdr) struct sdaemon *qdaemon; achdrval_t hdr; { int c,cerr; /* * Kludge alert! If another part of the program received a packet but * wasn't ready to handle it, it is tucked away for us to handle now. */ if (iZpkt_rcvd_kludge != -1) { c = iZpkt_rcvd_kludge; iZpkt_rcvd_kludge = -1; stohdr (hvZpkt_hdrval_kludge, hdr); DEBUG_MESSAGE2 (DEBUG_PROTO, "izrecv_hdr: queued %s, data = 0x%lx", ZZHEADER_NAME(c), rclhdr (hdr)); cZheaders_received++; return c; } cerr = cZmax_garbage; /* Max bytes before start of frame */ again: switch (c = noxrd7 (qdaemon)) { case ZM_TIMEOUT: case ZM_ERROR: case ZM_RCDO: goto fifi; case ZPAD: /* This is what we want */ break; case CR: /* padding at end of previous header */ default: if (--cerr < 0) { c = ZM_ERROR; goto fifi; } goto again; } splat: switch (c = noxrd7 (qdaemon)) { case ZPAD: if (--cerr < 0) { c = ZM_ERROR; goto fifi; } goto splat; case ZM_TIMEOUT: case ZM_RCDO: goto fifi; case ZDLE: /* This is what we want */ break; default: if (--cerr < 0) { c = ZM_ERROR; goto fifi; } goto again; } switch (c = noxrd7 (qdaemon)) { case ZM_TIMEOUT: case ZM_RCDO: goto fifi; case ZBIN: c = zrbhdr32 (qdaemon, hdr); break; case ZHEX: c = zrhhdr (qdaemon, hdr); break; default: if (--cerr < 0) { c = ZM_ERROR; goto fifi; } goto again; } fifi: switch (c) { case ZM_TIMEOUT: cZtimeouts++; break; case ZM_ERROR: cZerrors++; break; case ZM_RCDO: break; default: cZheaders_received++; break; } DEBUG_MESSAGE2 (DEBUG_PROTO, "izrecv_hdr: %s, data = 0x%lx", ZZHEADER_NAME(c), rclhdr (hdr)); return c; } /* * Receive a binary style header (type and position) with 32 bit FCS ... */ static int zrbhdr32(qdaemon, hdr) struct sdaemon *qdaemon; achdrval_t hdr; { int c,i,type; unsigned long crc; if ((c = zdlread (qdaemon)) & ~0377) return c; type = c; crc = ICRCINIT; crc = UPDC32 (c, crc); for (i = 0; i < 4; i++) { if ((c = zdlread (qdaemon)) & ~0377) return c; crc = UPDC32 (c, crc); hdr[i] = (char) c; } for (i = 0; i < 4; i++) { if ((c = zdlread (qdaemon)) & ~0377) return c; crc = UPDC32 (c, crc); } if (crc != IHDRCRC) return ZM_ERROR; return type; } /* * Receive a hex style header (type and position) ... */ static int zrhhdr(qdaemon, hdr) struct sdaemon *qdaemon; achdrval_t hdr; { int c,i,type; unsigned long crc; if ((c = zgethex (qdaemon)) < 0) return c; type = c; crc = ICRCINIT; crc = UPDC32 (c, crc); for (i = 0; i < 4; i++) { if ((c = zgethex (qdaemon)) < 0) return c; crc = UPDC32 (c, crc); hdr[i] = (char) c; } for (i = 0; i < 4; i++) { if ((c = zgethex (qdaemon)) < 0) return c; crc = UPDC32 (c, crc); } if (crc != IHDRCRC) return ZM_ERROR; return type; } /* * Receive a data packet ... */ static int zrdat32(qdaemon, buf, length, iprxcount) struct sdaemon *qdaemon; char *buf; int length; int *iprxcount; { int c,d; unsigned long crc; char *end; crc = ICRCINIT; *iprxcount = 0; end = buf + length; while (buf <= end) { if ((c = zdlread (qdaemon)) & ~0377) { crcfoo: switch (c) { case GOTCRCE: case GOTCRCG: case GOTCRCQ: case GOTCRCW: case GOTCRCF: d = c; c &= 0377; crc = UPDC32 (c, crc); if ((c = zdlread (qdaemon)) & ~0377) goto crcfoo; crc = UPDC32 (c, crc); if ((c = zdlread (qdaemon)) & ~0377) goto crcfoo; crc = UPDC32 (c, crc); if ((c = zdlread (qdaemon)) & ~0377) goto crcfoo; crc = UPDC32 (c, crc); if ((c = zdlread (qdaemon)) & ~0377) goto crcfoo; crc = UPDC32 (c, crc); if (crc != IHDRCRC) return ZM_ERROR; *iprxcount = length - (end - buf); return d; case ZM_TIMEOUT: case ZM_RCDO: return c; default: return ZM_ERROR; } } *buf++ = (char) c; crc = UPDC32 (c, crc); } return ZM_ERROR; /* bad packet, too long */ } /* * Respond to receiver's complaint, get back in sync with receiver ... */ static int getinsync(qdaemon, flag) struct sdaemon *qdaemon; boolean flag; { int c,cerr; achdrval_t rx_hdr; cerr = cZretries; for (;;) { c = izrecv_hdr (qdaemon, rx_hdr); switch (c) { case ZRPOS: wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos, wpZlrxpos, wpZtxpos); cZbytes_resent += wpZtxpos - wpZrxpos; wpZlrxpos = wpZtxpos = wpZrxpos; if (wpZlastsync == wpZrxpos) { if (++iZbeenhereb4 > 4) if (cZblklen > 32) cZblklen /= 2; /* FIXME: shouldn't we reset iZbeenhereb4? */ } wpZlastsync = wpZrxpos; return ZRPOS; case ZACK: wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos, wpZlrxpos, wpZtxpos); wpZlrxpos = wpZrxpos; if (flag || wpZtxpos == wpZrxpos) return ZACK; break; case ZNAK: { winpos_t rx_bytes; /* * Our partner is in fzfinish_tx() and is waiting * for ZACK ... */ zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes); if (rx_bytes == wpZrxbytes) { if (!fzsend_hdr (qdaemon, ZHEX, ZACK, hvzencode_data_hdr (wpZrxbytes), TRUE)) return FALSE; } break; } case ZFIN: case ZM_RCDO: return c; case ZM_TIMEOUT: if (--cerr < 0) { ulog (LOG_ERROR, "getinsync: retries exhausted"); return ZM_ERROR; } break; /* sender doesn't send ZNAK for timeout */ case ZM_ERROR: default: if (--cerr < 0) { ulog (LOG_ERROR, "getinsync: retries exhausted"); return ZM_ERROR; } if (!fzsend_hdr (qdaemon, ZHEX, ZNAK, hvzencode_data_hdr (wpZtxpos), TRUE)) return ZM_ERROR; break; } } } /* * Send a byte as two hex digits ... */ static char * zputhex(p, ch) char *p; int ch; { static char digits[] = "0123456789abcdef"; *p++ = digits[(ch & 0xF0) >> 4]; *p++ = digits[ch & 0xF]; return p; } /* * Send character c with ZMODEM escape sequence encoding ... * * Escape XON, XOFF. * FIXME: Escape CR following @ (Telenet net escape) ... disabled for now * Will need to put back references to . */ static char * zputchar(p, ch) char *p; int ch; { char c = ch; /* Quick check for non control characters */ if (c & 0140) { *p++ = c; } else { switch (c & 0377) { case ZDLE: *p++ = ZDLE; *p++ = c ^ 0100; break; case CR: #if 0 if (!fZesc_ctl && (lastsent & 0177) != '@') goto sendit; #endif /* fall through */ case 020: /* ^P */ case XON: case XOFF: *p++ = ZDLE; c ^= 0100; /*sendit:*/ *p++ = c; break; default: if (fZesc_ctl && !(c & 0140)) { *p++ = ZDLE; c ^= 0100; } *p++ = c; break; } } return p; } /* * Decode two lower case hex digits into an 8 bit byte value ... */ static int zgethex(qdaemon) struct sdaemon *qdaemon; { int c,n; if ((c = noxrd7 (qdaemon)) < 0) return c; n = c - '0'; if (n > 9) n -= ('a' - ':'); if (n & ~0xF) return ZM_ERROR; if ((c = noxrd7 (qdaemon)) < 0) return c; c -= '0'; if (c > 9) c -= ('a' - ':'); if (c & ~0xF) return ZM_ERROR; c += (n << 4); return c; } /* * Read a byte, checking for ZMODEM escape encoding ... */ static int zdlread(qdaemon) struct sdaemon *qdaemon; { int c; again: READCHAR (qdaemon, c, cZtimeout); if (c < 0) return c; if (c & 0140) /* quick check for non control characters */ return c; switch (c) { case ZDLE: break; case XON: goto again; case XOFF: READCHAR (qdaemon, c, XON_WAIT); goto again; default: if (fZesc_ctl && !(c & 0140)) goto again; return c; } again2: READCHAR (qdaemon, c, cZtimeout); if (c < 0) return c; switch (c) { case ZCRCE: case ZCRCG: case ZCRCQ: case ZCRCW: case ZCRCF: return c | GOTOR; case ZRUB0: /* FIXME: This is never generated. */ return 0177; case ZRUB1: /* FIXME: This is never generated. */ return 0377; case XON: goto again2; case XOFF: READCHAR (qdaemon, c, XON_WAIT); goto again2; default: if (fZesc_ctl && !(c & 0140)) goto again2; /* FIXME: why again2? */ if ((c & 0140) == 0100) return c ^ 0100; break; } return ZM_ERROR; } /* * Read a character from the modem line with timeout ... * Eat parity bit, XON and XOFF characters. */ static int noxrd7(qdaemon) struct sdaemon *qdaemon; { int c; for (;;) { READCHAR (qdaemon, c, cZtimeout); if (c < 0) return c; switch (c &= 0177) { case XON: continue; case XOFF: READCHAR (qdaemon, c, XON_WAIT); continue; case CR: case ZDLE: return c; default: if (fZesc_ctl && !(c & 0140)) continue; return c; } } } /* * Read a character from the receive buffer, or from the line if empty ... * * is in seconds (maybe make it tenths of seconds like in Zmodem?) */ static int realreadchar(qdaemon, timeout) struct sdaemon *qdaemon; int timeout; { int c; if ((c = breceive_char (qdaemon->qconn, timeout, TRUE)) >= 0) return c; switch (c) { case -1: return ZM_TIMEOUT; case -2: return ZM_RCDO; } ulog (LOG_FATAL, "realreadchar: breceive_char() returned %d", c); return ZM_ERROR; } /* * Check if the receive channel has any characters in it. * * At present we can only test the receive buffer. No mechanism is available * to go to the hardware. This should not be a problem though, as long as all * appropriate calls to fsend_data() set to TRUE. */ static boolean fzreceive_ready() { return iPrecstart != iPrecend; } /* * Store integer value in an achdrval_t ... */ static void stohdr(val, hdr) hdrval_t val; achdrval_t hdr; { hdr[ZP0] = (char) val; hdr[ZP1] = (char) (val >> 8); hdr[ZP2] = (char) (val >> 16); hdr[ZP3] = (char) (val >> 24); } /* * Recover an integer from a header ... */ static hdrval_t rclhdr(hdr) achdrval_t hdr; { hdrval_t v; v = hdr[ZP3] & 0377; v = (v << 8) | (hdr[ZP2] & 0377); v = (v << 8) | (hdr[ZP1] & 0377); v = (v << 8) | (hdr[ZP0] & 0377); return v; } /* * Encode a from the byte count ... * * We use to store the byte count / 32 and a message sequence number which * made this function very useful. Don't remove it. * FIXME: Well, maybe remove it later. */ static hdrval_t hvzencode_data_hdr(cbytes) winpos_t cbytes; { return (hdrval_t) cbytes; } /* * Decode a into a byte count ... * * We use to store the byte count / 32 and a message sequence number which * made this function very useful. Don't remove it. * FIXME: Well, maybe remove it later. */ static void zdecode_data_hdr(hdrval, pcbytes) hdrval_t hdrval; winpos_t *pcbytes; { *pcbytes = hdrval; } /* * Update from the received data header value ... * * FIXME: Here is where we'd handle wrapping around at 4 gigabytes. */ static winpos_t lzupdate_rxpos(rx_hdr, rxpos, lrxpos, txpos) achdrval_t rx_hdr; winpos_t rxpos,lrxpos,txpos; { winpos_t rx_pktpos; zdecode_data_hdr (rclhdr (rx_hdr), &rx_pktpos); DEBUG_MESSAGE4 (DEBUG_PROTO, "lzupdate_rxpos: rx_pktpos=0x%lx, rxpos=0x%lx, lrxpos=0x%lx, txpos=0x%lx", rx_pktpos, rxpos, lrxpos, txpos); /* * Check if valid. It could be old. */ if (rx_pktpos < wpZlrxpos || rx_pktpos > ((wpZtxpos + 1024L) & ~1023L)) return rxpos; return rx_pktpos; } uucp-1.07/time.c0000664000076400007640000000632107665321756007235 /* time.c Routines to deal with UUCP time spans. Copyright (C) 1991, 1992, 1993 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char time_rcsid[] = "$Id: time.c,v 1.22 2002/03/05 19:10:41 ian Rel $"; #endif #include #if TM_IN_SYS_TIME #include #else #include #endif #include "uudefs.h" #include "uuconf.h" /* External functions. */ #ifndef time extern time_t time (); #endif #ifndef localtime extern struct tm *localtime (); #endif /* See if the current time matches a time span. If it does, return TRUE, set *pival to the value for the matching span, and set *pcretry to the retry for the matching span. Otherwise return FALSE. */ boolean ftimespan_match (qspan, pival, pcretry) const struct uuconf_timespan *qspan; long *pival; int *pcretry; { time_t inow; struct tm *qtm; int itm; const struct uuconf_timespan *q; if (qspan == NULL) return FALSE; time (&inow); qtm = localtime (&inow); /* Get the number of minutes since Sunday for the time. */ itm = qtm->tm_wday * 24 * 60 + qtm->tm_hour * 60 + qtm->tm_min; for (q = qspan; q != NULL; q = q->uuconf_qnext) { if (q->uuconf_istart <= itm && itm <= q->uuconf_iend) { if (pival != NULL) *pival = q->uuconf_ival; if (pcretry != NULL) *pcretry = q->uuconf_cretry; return TRUE; } } return FALSE; } /* Determine the maximum size that may ever be transferred, according to a timesize span. This returns -1 if there is no limit. */ long cmax_size_ever (qtimesize) const struct uuconf_timespan *qtimesize; { long imax; const struct uuconf_timespan *q; if (qtimesize == NULL) return -1; /* Look through the list of spans. If there is any gap larger than 1 hour, we assume there are no restrictions. Otherwise we keep track of the largest value we see. I picked 1 hour arbitrarily, on the theory that a 1 hour span to transfer large files might actually occur, and is probably not an accident. */ if (qtimesize->uuconf_istart >= 60) return -1; imax = -1; for (q = qtimesize; q != NULL; q = q->uuconf_qnext) { if (q->uuconf_qnext == NULL) { if (q->uuconf_iend <= 6 * 24 * 60 + 23 * 60) return -1; } else { if (q->uuconf_iend + 60 <= q->uuconf_qnext->uuconf_istart) return -1; } if (imax < q->uuconf_ival) imax = q->uuconf_ival; } return imax; } uucp-1.07/conn.h0000664000076400007640000003117107665321755007241 /* conn.h Header file for routines which manipulate connections. Copyright (C) 1991, 1992, 1993, 1994, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #ifndef CONN_H #define CONN_H #if ANSI_C /* These structures are used in prototypes but are not defined in this header file. */ struct uuconf_system; struct uuconf_dialer; struct uuconf_chat; #endif /* This structure represents a connection. */ struct sconnection { /* Pointer to command table for this type of connection. */ const struct sconncmds *qcmds; /* Pointer to system dependent information. */ pointer psysdep; /* Pointer to system independent information. */ struct uuconf_port *qport; }; /* Whether fconn_dial got a dialer. */ enum tdialerfound { /* Did not find a dialer. */ DIALERFOUND_FALSE, /* Found a dialer which does not need to be freed. */ DIALERFOUND_TRUE, /* Found a dialer which does need to be freed. */ DIALERFOUND_FREE }; /* Parity settings to pass to fconn_set. */ enum tparitysetting { /* Do not change output parity generation. */ PARITYSETTING_DEFAULT, /* No parity (all eight output bits used). */ PARITYSETTING_NONE, /* Even parity. */ PARITYSETTING_EVEN, /* Odd parity. */ PARITYSETTING_ODD, /* Mark parity. */ PARITYSETTING_MARK, /* Space parity. */ PARITYSETTING_SPACE }; /* Type of strip control argument to fconn_set. */ enum tstripsetting { /* Do not change the stripping of input characters. */ STRIPSETTING_DEFAULT, /* Do not strip input characters to seven bits. */ STRIPSETTING_EIGHTBITS, /* Strip input characters to seven bits. */ STRIPSETTING_SEVENBITS }; /* Type of XON/XOFF control argument to fconn_set. */ enum txonxoffsetting { /* Do not change XON/XOFF handshake setting. */ XONXOFF_DEFAULT, /* Do not do XON/XOFF handshaking. */ XONXOFF_OFF, /* Do XON/XOFF handshaking. */ XONXOFF_ON }; /* A command table holds the functions which implement actions for each different kind of connection. */ struct sconncmds { /* Free up a connection. */ void (*pufree) P((struct sconnection *qconn)); /* Lock the connection. The fin argument is TRUE if the connection is to be used for an incoming call. The fuser argument is TRUE if, should the port be opened, it should be opened using the user's permissions rather than the effective permissions. May be NULL. */ boolean (*pflock) P((struct sconnection *qconn, boolean fin, boolean fuser)); /* Unlock the connection. May be NULL. */ boolean (*pfunlock) P((struct sconnection *qconn)); /* Open the connection. */ boolean (*pfopen) P((struct sconnection *qconn, long ibaud, boolean fwait, boolean fuser)); /* Close the connection. */ boolean (*pfclose) P((struct sconnection *qconn, pointer puuconf, struct uuconf_dialer *qdialer, boolean fsuccess)); /* Dial a number on a connection. This set *qdialer to the dialer used, if any, and sets *ptdialerfound appropriately. The qsys and zphone arguments are for the chat script. This field may be NULL. */ boolean (*pfdial) P((struct sconnection *qconn, pointer puuconf, const struct uuconf_system *qsys, const char *zphone, struct uuconf_dialer *qdialer, enum tdialerfound *ptdialerfound)); /* Read data from a connection, with a timeout in seconds. When called *pclen is the length of the buffer; on successful return *pclen is the number of bytes read into the buffer. The cmin argument is the minimum number of bytes to read before returning ahead of a timeout. */ boolean (*pfread) P((struct sconnection *qconn, char *zbuf, size_t *pclen, size_t cmin, int ctimeout, boolean freport)); /* Write data to the connection. */ boolean (*pfwrite) P((struct sconnection *qconn, const char *zbuf, size_t clen)); /* Read and write data to the connection. This reads and writes data until either all passed in data has been written or the read buffer has been filled. When called *pcread is the size of the read buffer and *pcwrite is the number of bytes to write; on successful return *pcread is the number of bytes read and *pcwrite is the number of bytes written. */ boolean (*pfio) P((struct sconnection *qconn, const char *zwrite, size_t *pcwrite, char *zread, size_t *pcread)); /* Send a break character. This field may be NULL. */ boolean (*pfbreak) P((struct sconnection *qconn)); /* Change the connection setting. This field may be NULL. */ boolean (*pfset) P((struct sconnection *qconn, enum tparitysetting tparity, enum tstripsetting tstrip, enum txonxoffsetting txonxoff)); /* Require or ignore carrer. This field may be NULL. */ boolean (*pfcarrier) P((struct sconnection *qconn, boolean fcarrier)); /* Run a chat program on a connection. */ boolean (*pfchat) P((struct sconnection *qconn, char **pzprog)); /* Get the baud rate of a connection. This field may be NULL. */ long (*pibaud) P((struct sconnection *qconn)); }; /* Connection functions. */ /* Initialize a connection. This must be called before any of the other connection functions are called. It initializes the fields of qconn. If qport is NULL, this opens standard input as a port using type ttype. This function returns FALSE on error. */ extern boolean fconn_init P((struct uuconf_port *qport, struct sconnection *qconn, enum uuconf_porttype ttype)); /* Free up connection data. */ extern void uconn_free P((struct sconnection *qconn)); /* Lock a connection. The fin argument is TRUE if the port is to be used for an incoming call; certains type of Unix locking need this information because they need to open the port. The fuser argument is TRUE if, should the port be opened, it should be opened using the user's permissions rather than the effective permissions. */ extern boolean fconn_lock P((struct sconnection *qconn, boolean fin, boolean fuser)); /* Unlock a connection. */ extern boolean fconn_unlock P((struct sconnection *qconn)); /* Open a connection. If ibaud is 0, the natural baud rate of the port is used. If ihighbaud is not 0, fconn_open chooses the highest supported baud rate between ibaud and ihighbaud. If fwait is TRUE, this should wait for an incoming call. If fuser is true, the device should be opened using the user's permissions rather than the effective permissions. */ extern boolean fconn_open P((struct sconnection *qconn, long ibaud, long ihighbaud, boolean fwait, boolean fuser)); /* Close a connection. The fsuccess argument is TRUE if the conversation completed normally, FALSE if it is being aborted. */ extern boolean fconn_close P((struct sconnection *qconn, pointer puuconf, struct uuconf_dialer *qdialer, boolean fsuccess)); /* Dial out on a connection. The qsys and zphone arguments are for the chat scripts; zphone is the phone number to dial. If qdialer is not NULL, *qdialer will be set to the dialer information used if any; *ptdialerfound will be set appropriately. */ extern boolean fconn_dial P((struct sconnection *q, pointer puuconf, const struct uuconf_system *qsys, const char *zphone, struct uuconf_dialer *qdialer, enum tdialerfound *ptdialerfound)); /* Read from a connection. zbuf -- buffer to read bytes into *pclen on call -- length of zbuf *pclen on successful return -- number of bytes read cmin -- minimum number of bytes to read before returning ahead of timeout ctimeout -- timeout in seconds, 0 if none freport -- whether to report errors. */ extern boolean fconn_read P((struct sconnection *qconn, char *zbuf, size_t *pclen, size_t cmin, int ctimeout, boolean freport)); /* Write to a connection. */ extern boolean fconn_write P((struct sconnection *qconn, const char *zbuf, size_t cbytes)); /* Read and write to a connection. This reads and writes data until either all passed-in data has been written or the read buffer is full. zwrite -- buffer to write bytes from *pcwrite on call -- number of bytes to write *pcwrite on successful return -- number of bytes written zread -- buffer to read bytes into *pcread on call -- size of read buffer *pcread on successful return -- number of bytes read. */ extern boolean fconn_io P((struct sconnection *qconn, const char *zwrite, size_t *pcwrite, char *zread, size_t *pcread)); /* Send a break character to a connection. */ extern boolean fconn_break P((struct sconnection *qconn)); /* Change the settings of a connection. This allows independent control over the parity of output characters, whether to strip input characters, and whether to do XON/XOFF handshaking. There is no explicit control over parity checking of input characters. This function returns FALSE on error. Attempts to set values not supported by the hardware are silently ignored. */ extern boolean fconn_set P((struct sconnection *qconn, enum tparitysetting tparity, enum tstripsetting tstrip, enum txonxoffsetting txonxoff)); /* Get the baud rate of a connection. */ extern long iconn_baud P((struct sconnection *qconn)); /* Do a chat script with a system. */ extern boolean fchat P((struct sconnection *qconn, pointer puuconf, const struct uuconf_chat *qchat, const struct uuconf_system *qsys, const struct uuconf_dialer *qdialer, const char *zphone, boolean ftranslate, const char *zport, long ibaud)); /* Tell the connection to either require or ignore carrier as fcarrier is TRUE or FALSE respectively. This is called with fcarrier TRUE when \m is encountered in a chat script, and with fcarrier FALSE when \M is encountered. */ extern boolean fconn_carrier P((struct sconnection *qconn, boolean fcarrier)); /* Run a chat program on a connection. */ extern boolean fconn_run_chat P((struct sconnection *qconn, char **pzprog)); /* Run through a dialer sequence. This is a support routine for the port type specific dialing routines. */ extern boolean fconn_dial_sequence P((struct sconnection *qconn, pointer puuconf, char **pzdialer, const struct uuconf_system *qsys, const char *zphone, struct uuconf_dialer *qdialer, enum tdialerfound *ptdialerfound)); /* Dialing out on a modem is partially system independent. This is the modem dialing routine. */ extern boolean fmodem_dial P((struct sconnection *qconn, pointer puuconf, const struct uuconf_system *qsys, const char *zphone, struct uuconf_dialer *qdialer, enum tdialerfound *ptdialerfound)); /* Begin dialing out. This should open the dialer device if there is one, toggle DTR if requested and possible, and tell the port to ignore carrier. It should return FALSE on error. */ extern boolean fsysdep_modem_begin_dial P((struct sconnection *qconn, struct uuconf_dialer *qdial)); /* Finish dialing out on a modem. This should close the dialer device if there is one. If the dialer and the port both support carrier, the connection should be told to pay attention to carrier. If it is possible to wait for carrier to come on, and the dialer and the port both the port support carrier, it should wait until carrier comes on. */ extern boolean fsysdep_modem_end_dial P((struct sconnection *qconn, struct uuconf_dialer *qdial)); /* System dependent initialization routines. */ extern boolean fsysdep_stdin_init P((struct sconnection *qconn)); extern boolean fsysdep_modem_init P((struct sconnection *qconn)); extern boolean fsysdep_direct_init P((struct sconnection *qconn)); #if HAVE_TCP extern boolean fsysdep_tcp_init P((struct sconnection *qconn)); #endif #if HAVE_TLI extern boolean fsysdep_tli_init P((struct sconnection *qconn)); #endif extern boolean fsysdep_pipe_init P((struct sconnection *qconn)); #endif /* ! defined (CONN_H) */ uucp-1.07/util.c0000664000076400007640000001051507665321756007254 /* util.c A couple of UUCP utility functions. Copyright (C) 1991, 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char util_rcsid[] = "$Id: util.c,v 1.11 2002/03/05 19:10:42 ian Rel $"; #endif #include #include "uudefs.h" #include "uuconf.h" #include "system.h" /* Get information for an unknown system. This will leave the name allocated on the heap. We could fix this by breaking the abstraction and adding the name to qsys->palloc. It makes sure the name is not too long, but takes no other useful action. */ boolean funknown_system (puuconf, zsystem, qsys) pointer puuconf; const char *zsystem; struct uuconf_system *qsys; { char *z; int iuuconf; if (strlen (zsystem) <= cSysdep_max_name_len) z = zbufcpy (zsystem); else { char **pznames, **pz; boolean ffound; z = zbufalc (cSysdep_max_name_len + 1); memcpy (z, zsystem, cSysdep_max_name_len); z[cSysdep_max_name_len] = '\0'; iuuconf = uuconf_system_names (puuconf, &pznames, TRUE); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); ffound = FALSE; for (pz = pznames; *pz != NULL; pz++) { if (strcmp (*pz, z) == 0) ffound = TRUE; xfree ((pointer) *pz); } xfree ((pointer) pznames); if (ffound) { ubuffree (z); return FALSE; } } iuuconf = uuconf_system_unknown (puuconf, qsys); if (iuuconf == UUCONF_NOT_FOUND) { ubuffree (z); return FALSE; } else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); for (; qsys != NULL; qsys = qsys->uuconf_qalternate) qsys->uuconf_zname = z; return TRUE; } /* Remove all occurrences of the local system name followed by an exclamation point from the front of a string, returning the new string. This is used by uucp and uux. */ char * zremove_local_sys (qlocalsys, z) struct uuconf_system *qlocalsys; char *z; { size_t clen; char *zexclam; clen = strlen (qlocalsys->uuconf_zname); zexclam = strchr (z, '!'); while (zexclam != NULL) { if (z == zexclam || ((size_t) (zexclam - z) == clen && strncmp (z, qlocalsys->uuconf_zname, clen) == 0)) ; else if (qlocalsys->uuconf_pzalias == NULL) break; else { char **pzal; for (pzal = qlocalsys->uuconf_pzalias; *pzal != NULL; pzal++) if (strlen (*pzal) == (size_t) (zexclam - z) && strncmp (z, *pzal, (size_t) (zexclam - z)) == 0) break; if (*pzal == NULL) break; } z = zexclam + 1; zexclam = strchr (z, '!'); } return z; } /* See whether a file is in a directory list, and make sure the user has appropriate access. */ boolean fin_directory_list (zfile, pzdirs, zpubdir, fcheck, freadable, zuser) const char *zfile; char **pzdirs; const char *zpubdir; boolean fcheck; boolean freadable; const char *zuser; { boolean fmatch; char **pz; fmatch = FALSE; for (pz = pzdirs; *pz != NULL; pz++) { char *zuse; if (pz[0][0] == '!') { zuse = zsysdep_local_file (*pz + 1, zpubdir, (boolean *) NULL); if (zuse == NULL) return FALSE; if (fsysdep_in_directory (zfile, zuse, FALSE, FALSE, (const char *) NULL)) fmatch = FALSE; } else { zuse = zsysdep_local_file (*pz, zpubdir, (boolean *) NULL); if (zuse == NULL) return FALSE; if (fsysdep_in_directory (zfile, zuse, fcheck, freadable, zuser)) fmatch = TRUE; } ubuffree (zuse); } return fmatch; } uucp-1.07/uuconv.c0000664000076400007640000016037307665321757007627 /* uuconv.c Convert one type of UUCP configuration file to another. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char uuconv_rcsid[] = "$Id: uuconv.c,v 1.30 2002/03/05 19:10:42 ian Rel $"; #endif #include "getopt.h" /* Local functions. */ static void uvusage P((void)); static void uvhelp P((void)); static void uvwrite_time P((FILE *e, struct uuconf_timespan *qtime)); static void uvwrite_string P((FILE *e, const char *zarg, const char *zcmd)); static void uvwrite_size P((FILE *e, struct uuconf_timespan *qsize, const char *zcmd)); static void uvwrite_boolean P((FILE *e, int iarg, const char *zcmd)); static void uvwrite_string_array P((FILE *e, char **pz, const char *zcmd)); static void uvwrite_chat_script P((FILE *e, char **pz)); static void uvwrite_chat P((FILE *e, const struct uuconf_chat *qchat, const struct uuconf_chat *qlast, const char *zprefix, boolean fforce)); static void uvwrite_proto_params P((FILE *e, const struct uuconf_proto_param *qparam, const char *zprefix)); static void uvwrite_taylor_system P((FILE *e, const struct uuconf_system *qsys)); static void uvwrite_v2_system P((FILE *e, const struct uuconf_system *qsys)); static void uvwrite_hdb_system P((FILE *e, const struct uuconf_system *qsys)); static boolean fvperm_string_cmp P((const char *z1, const char *z2)); static boolean fvperm_array_cmp P((const char **pz1, const char **pz2)); static void uvadd_perm P((struct shpermissions *qadd)); static void uvwrite_perms P((void)); static void uvwrite_perm_array P((FILE *e, const char **pz, const char *zcmd, size_t *pccol)); static void uvwrite_perm_boolean P((FILE *e, int f, const char *zcmd, size_t *pccol, boolean fsendfiles)); static void uvwrite_perm_rw_array P((FILE *e, const char **pz, const char *zcmd, size_t *pccol)); static void uvwrite_perm_string P((FILE *e, const char *z, const char *zcmd, size_t *pccol)); static int ivwrite_taylor_port P((struct uuconf_port *qport, pointer pinfo)); static int ivwrite_v2_port P((struct uuconf_port *qport, pointer pinfo)); static int ivwrite_hdb_port P((struct uuconf_port *qport, pointer pinfo)); static void uvwrite_taylor_port P((FILE *e, struct uuconf_port *qport, const char *zprefix)); static void uvwrite_taylor_dialer P((FILE *e, struct uuconf_dialer *qdialer, const char *zprefix)); static void uvwrite_hdb_dialer P((FILE *e, struct uuconf_dialer *qdialer)); static void uvuuconf_error P((pointer puuconf, int iret)); /* The program name. */ const char *zProgram; /* A list of Permissions entries built when writing out HDB system information. */ static struct shpermissions *qVperms; /* Type of configuration file. */ enum tconfig { CONFIG_TAYLOR, CONFIG_V2, CONFIG_HDB }; /* Long getopt options. */ static const struct option asVlongopts[] = { { "input", required_argument, NULL, 'i' }, { "output", required_argument, NULL, 'o' }, { "program", required_argument, NULL, 'p' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; int main (argc, argv) int argc; char **argv; { /* -I: The configuration file name. */ const char *zconfig = NULL; /* -i: Input type. */ const char *zinput = NULL; /* -o: Output type. */ const char *zoutput = NULL; /* -p: Program name. */ const char *zprogram = NULL; int iopt; enum tconfig tinput, toutput; int iret; pointer pinput; zProgram = argv[0]; while ((iopt = getopt_long (argc, argv, "i:I:o:p:vx:", asVlongopts, (int *) NULL)) != EOF) { switch (iopt) { case 'i': /* Input type. */ zinput = optarg; break; case 'o': /* Output type. */ zoutput = optarg; break; case 'p': /* Program name. */ zprogram = optarg; break; case 'I': /* Set the configuration file name. */ zconfig = optarg; break; case 'x': /* Set the debugging level. */ break; case 'v': /* Print version and exit. */ printf ("uuconv (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 1: /* --help. */ uvhelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found, and flag value set. */ break; default: uvusage (); break; } } if (optind != argc || zinput == NULL || zoutput == NULL) uvusage (); if (strcasecmp (zinput, "taylor") == 0) tinput = CONFIG_TAYLOR; else if (strcasecmp (zinput, "v2") == 0) tinput = CONFIG_V2; else if (strcasecmp (zinput, "hdb") == 0) tinput = CONFIG_HDB; else { uvusage (); tinput = CONFIG_TAYLOR; } if (strcasecmp (zoutput, "taylor") == 0) toutput = CONFIG_TAYLOR; else if (strcasecmp (zoutput, "v2") == 0) toutput = CONFIG_V2; else if (strcasecmp (zoutput, "hdb") == 0) toutput = CONFIG_HDB; else { uvusage (); toutput = CONFIG_TAYLOR; } if (tinput == toutput) uvusage (); iret = UUCONF_SUCCESS; /* Initialize the input. */ pinput = NULL; switch (tinput) { case CONFIG_TAYLOR: iret = uuconf_taylor_init (&pinput, zprogram, zconfig); break; case CONFIG_V2: iret = uuconf_v2_init (&pinput); break; case CONFIG_HDB: iret = uuconf_hdb_init (&pinput, zprogram); break; } if (iret != UUCONF_SUCCESS) { uvuuconf_error (pinput, iret); exit (EXIT_FAILURE); } { char **pzsystems; char *zsys; char abtaylor[sizeof ZCURDIR + sizeof SYSFILE - 1]; char abv2[sizeof ZCURDIR + sizeof V2_SYSTEMS - 1]; char abhdb[sizeof ZCURDIR + sizeof HDB_SYSTEMS - 1]; FILE *esys; char **pz; /* Get the list of systems. */ switch (tinput) { case CONFIG_TAYLOR: iret = uuconf_taylor_system_names (pinput, &pzsystems, FALSE); break; case CONFIG_V2: iret = uuconf_v2_system_names (pinput, &pzsystems, FALSE); break; case CONFIG_HDB: iret = uuconf_hdb_system_names (pinput, &pzsystems, FALSE); break; } if (iret != UUCONF_SUCCESS) uvuuconf_error (pinput, iret); else { /* Open the sys file for the output type. */ switch (toutput) { default: case CONFIG_TAYLOR: sprintf (abtaylor, "%s%s", ZCURDIR, SYSFILE); zsys = abtaylor; break; case CONFIG_V2: sprintf (abv2, "%s%s", ZCURDIR, V2_SYSTEMS); zsys = abv2; break; case CONFIG_HDB: sprintf (abhdb, "%s%s", ZCURDIR, HDB_SYSTEMS); zsys = abhdb; break; } esys = fopen (zsys, "w"); if (esys == NULL) { fprintf (stderr, "uuchk:%s: ", zsys); perror ("fopen"); exit (EXIT_FAILURE); } fprintf (esys, "# %s file automatically generated by uuconv.\n", zsys); /* Read and write each system. We cheat and call the internal routines, so that we can easily detect default information and not write it out. This isn't necessary, but it makes the output smaller and simpler. */ for (pz = pzsystems; *pz != NULL; pz++) { struct uuconf_system ssys; switch (tinput) { case CONFIG_TAYLOR: iret = _uuconf_itaylor_system_internal (pinput, *pz, &ssys); break; case CONFIG_V2: iret = _uuconf_iv2_system_internal (pinput, *pz, &ssys); break; case CONFIG_HDB: iret = _uuconf_ihdb_system_internal (pinput, *pz, &ssys); break; } if (iret != UUCONF_SUCCESS) uvuuconf_error (pinput, iret); else { switch (toutput) { case CONFIG_TAYLOR: uvwrite_taylor_system (esys, &ssys); break; case CONFIG_V2: uvwrite_v2_system (esys, &ssys); break; case CONFIG_HDB: uvwrite_hdb_system (esys, &ssys); break; } if (toutput != CONFIG_HDB) (void) uuconf_system_free (pinput, &ssys); } } if (toutput == CONFIG_HDB) uvwrite_perms (); if (ferror (esys) || fclose (esys) == EOF) { fprintf (stderr, "uuchk:%s: error during output\n", zsys); exit (EXIT_FAILURE); } } } { /* Open the port file for the output type. */ char *zport; char abtaylor[sizeof ZCURDIR + sizeof PORTFILE - 1]; char abv2[sizeof ZCURDIR + sizeof V2_DEVICES - 1]; char abhdb[sizeof ZCURDIR + sizeof HDB_DEVICES - 1]; FILE *eport; int (*piportfn) P((struct uuconf_port *, pointer)); struct uuconf_port sport; switch (toutput) { default: case CONFIG_TAYLOR: sprintf (abtaylor, "%s%s", ZCURDIR, PORTFILE); zport = abtaylor; piportfn = ivwrite_taylor_port; break; case CONFIG_V2: sprintf (abv2, "%s%s", ZCURDIR, V2_DEVICES); zport = abv2; piportfn = ivwrite_v2_port; break; case CONFIG_HDB: sprintf (abhdb, "%s%s", ZCURDIR, HDB_DEVICES); zport = abhdb; piportfn = ivwrite_hdb_port; break; } eport = fopen (zport, "w"); if (eport == NULL) { fprintf (stderr, "uuchk:%s: ", zport); perror ("fopen"); exit (EXIT_FAILURE); } fprintf (eport, "# %s file automatically generated by uuconv.\n", zport); switch (tinput) { case CONFIG_TAYLOR: iret = uuconf_taylor_find_port (pinput, (const char *) NULL, 0L, 0L, piportfn, (pointer) eport, &sport); break; case CONFIG_V2: iret = uuconf_v2_find_port (pinput, (const char *) NULL, 0L, 0L, piportfn, (pointer) eport, &sport); break; case CONFIG_HDB: iret = uuconf_hdb_find_port (pinput, (const char *) NULL, 0L, 0L, piportfn, (pointer) eport, &sport); break; } if (iret != UUCONF_NOT_FOUND) uvuuconf_error (pinput, iret); if (ferror (eport) || fclose (eport) == EOF) { fprintf (stderr, "uuchk:%s: error during output\n", zport); exit (EXIT_FAILURE); } } /* V2 configuration files don't support dialers. */ if (tinput != CONFIG_V2 && toutput != CONFIG_V2) { char **pzdialers; char *zdialer; char abtaylor[sizeof ZCURDIR + sizeof DIALFILE - 1]; char abhdb[sizeof ZCURDIR + sizeof HDB_DIALERS - 1]; FILE *edialer; char **pz; /* Get the list of dialers. */ switch (tinput) { default: case CONFIG_TAYLOR: iret = uuconf_taylor_dialer_names (pinput, &pzdialers); break; case CONFIG_HDB: iret = uuconf_hdb_dialer_names (pinput, &pzdialers); break; } if (iret != UUCONF_SUCCESS) uvuuconf_error (pinput, iret); else { /* Open the sys file for the output type. */ switch (toutput) { default: case CONFIG_TAYLOR: sprintf (abtaylor, "%s%s", ZCURDIR, DIALFILE); zdialer = abtaylor; break; case CONFIG_HDB: sprintf (abhdb, "%s%s", ZCURDIR, HDB_DIALERS); zdialer = abhdb; break; } edialer = fopen (zdialer, "w"); if (edialer == NULL) { fprintf (stderr, "uuchk:%s: ", zdialer); perror ("fopen"); exit (EXIT_FAILURE); } fprintf (edialer, "# %s file automatically generated by uuconv.\n", zdialer); /* Read and write each dialer. */ for (pz = pzdialers; *pz != NULL; pz++) { struct uuconf_dialer sdialer; switch (tinput) { default: case CONFIG_TAYLOR: iret = uuconf_taylor_dialer_info (pinput, *pz, &sdialer); break; case CONFIG_HDB: iret = uuconf_hdb_dialer_info (pinput, *pz, &sdialer); break; } if (iret != UUCONF_SUCCESS) uvuuconf_error (pinput, iret); else { switch (toutput) { default: case CONFIG_TAYLOR: fprintf (edialer, "# Start of dialer %s\n", sdialer.uuconf_zname); fprintf (edialer, "dialer %s\n", sdialer.uuconf_zname); uvwrite_taylor_dialer (edialer, &sdialer, ""); break; case CONFIG_HDB: uvwrite_hdb_dialer (edialer, &sdialer); break; } (void) uuconf_dialer_free (pinput, &sdialer); } } if (ferror (edialer) || fclose (edialer) == EOF) { fprintf (stderr, "uuchk:%s: error during output\n", zdialer); exit (EXIT_FAILURE); } } } exit (EXIT_SUCCESS); /* Avoid complaints about not returning. */ return 0; } /* Print out a usage message and die. */ static void uvusage () { fprintf (stderr, "Usage: %s -i input-type -o output-type [-p program]\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EXIT_FAILURE); } /* Print a help message. */ static void uvhelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); printf ("Converts UUCP configuration files from one format to another.\n"); printf ("Usage: %s -i input -o output [-p program] [-I file]\n", zProgram); printf (" -i,--input input: Set input type (one of taylor, v2, hdb)\n"); printf (" -o,--output output: Set output type (one of taylor, v2, hdb)\n"); printf (" -p,--program program: Program to convert (e.g., uucp or cu)\n"); printf (" -I,--config file: Set Taylor UUCP configuration file to use\n"); printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } /* Write out a timespan. */ static void uvwrite_time (e, qtime) FILE *e; struct uuconf_timespan *qtime; { if (qtime == NULL) { fprintf (e, "Never"); return; } if (qtime->uuconf_istart == 0 && qtime->uuconf_iend == 7 * 24 * 60) { fprintf (e, "Any"); return; } for (; qtime != NULL; qtime = qtime->uuconf_qnext) { int idaystart, idayend; int ihourstart, ihourend; int iminutestart, iminuteend; const char * const zdays = "Su\0Mo\0Tu\0We\0Th\0Fr\0Sa"; idaystart = qtime->uuconf_istart / (24 * 60); ihourstart = (qtime->uuconf_istart % (24 * 60)) / 60; iminutestart = qtime->uuconf_istart % 60; if (qtime->uuconf_iend >= 7 * 24 * 60) qtime->uuconf_iend = 7 * 24 * 60 - 1; idayend = qtime->uuconf_iend / (24 * 60); ihourend = (qtime->uuconf_iend % (24 * 60)) / 60; iminuteend = qtime->uuconf_iend % 60; if (ihourend == 0 && iminuteend == 0) --idayend; if (idaystart == idayend) fprintf (e, "%s%02d%02d-%02d%02d", zdays + idaystart * 3, ihourstart, iminutestart, ihourend, iminuteend); else { int i; fprintf (e, "%s%02d%02d-0000", zdays + idaystart * 3, ihourstart, iminutestart); for (i = idaystart + 1; i < idayend; i++) fprintf (e, ",%s", zdays + i * 3); if (ihourend != 0 || iminuteend != 0) fprintf (e, ",%s0000-%02d%02d", zdays + idayend * 3, ihourend, iminuteend); } if (qtime->uuconf_qnext != NULL) fprintf (e, ","); } } /* Some subroutines used when writing out Taylor UUCP configuration files. */ /* Write a command with a string argument. */ static void uvwrite_string (e, zarg, zcmd) FILE *e; const char *zarg; const char *zcmd; { if (zarg != (const char *) &_uuconf_unset) fprintf (e, "%s %s\n", zcmd, zarg == NULL ? (const char *) "" : zarg); } /* Write out a size restriction command. */ static void uvwrite_size (e, qtime, zcmd) FILE *e; struct uuconf_timespan *qtime; const char *zcmd; { if (qtime != (struct uuconf_timespan *) &_uuconf_unset) { for (; qtime != NULL; qtime = qtime->uuconf_qnext) { fprintf (e, "%s %ld", zcmd, qtime->uuconf_ival); uvwrite_time (e, qtime); fprintf (e, "\n"); } } } /* Write out a boolean argument with a string command. If the value is less than zero, than it was uninitialized and we don't write anything. */ static void uvwrite_boolean (e, fval, zcmd) FILE *e; int fval; const char *zcmd; { if (fval >= 0) fprintf (e, "%s %s\n", zcmd, fval > 0 ? "true" : "false"); } /* Write out a string array as a single command. */ static void uvwrite_string_array (e, pz, zcmd) FILE *e; char **pz; const char *zcmd; { if (pz != (char **) &_uuconf_unset) { fprintf (e, "%s", zcmd); if (pz != NULL) for (; *pz != NULL; pz++) fprintf (e, " %s", *pz); fprintf (e, "\n"); } } /* Write out a chat script. Don't separate subsend/subexpect strings by spaces. */ static void uvwrite_chat_script (e, pzarg) FILE *e; char **pzarg; { char **pz; if (pzarg == NULL || pzarg == (char **) &_uuconf_unset) return; for (pz = pzarg; *pz != NULL; pz++) { if ((*pz)[0] != '-' && pz != pzarg) fprintf (e, " "); fprintf (e, *pz); } } /* Write out chat information. If the qlast argument is not NULL, then only values that are different from qlast should be written. The fforce argument is used to get around a peculiar problem: if the ``chat'' command is used with no arguments for a system, then uuconf_pzchat will be NULL (not &_uuconf_unset) and the default chat script will not be used. We must distinguish this case from the ``chat'' command not appearing at all for a port or dialer, in which case the value will again be NULL. In the former case we must output a ``chat'' command, in the latter case we would prefer not to. */ static void uvwrite_chat (e, q, qlast, zprefix, fforce) FILE *e; const struct uuconf_chat *q; const struct uuconf_chat *qlast; const char *zprefix; boolean fforce; { char **pz; char ab[100]; if (q->uuconf_pzchat != (char **) &_uuconf_unset && (qlast == NULL ? (fforce || q->uuconf_pzchat != NULL) : qlast->uuconf_pzchat != q->uuconf_pzchat)) { fprintf (e, "%schat ", zprefix); uvwrite_chat_script (e, q->uuconf_pzchat); fprintf (e, "\n"); } if (q->uuconf_pzprogram != (char **) &_uuconf_unset && (qlast == NULL ? q->uuconf_pzprogram != NULL : qlast->uuconf_pzprogram != q->uuconf_pzprogram)) { sprintf (ab, "%schat-program", zprefix); uvwrite_string_array (e, q->uuconf_pzprogram, ab); } if (q->uuconf_ctimeout >= 0 && (qlast == NULL || qlast->uuconf_ctimeout != q->uuconf_ctimeout)) fprintf (e, "%schat-timeout %d\n", zprefix, q->uuconf_ctimeout); if (q->uuconf_pzfail != NULL && q->uuconf_pzfail != (char **) &_uuconf_unset && (qlast == NULL || qlast->uuconf_pzfail != q->uuconf_pzfail)) for (pz = q->uuconf_pzfail; *pz != NULL; pz++) fprintf (e, "%schat-fail %s\n", zprefix, *pz); if (qlast == NULL || qlast->uuconf_fstrip != q->uuconf_fstrip) { sprintf (ab, "%schat-strip", zprefix); uvwrite_boolean (e, q->uuconf_fstrip, ab); } } /* Write out protocol parameters to a Taylor UUCP file. */ static void uvwrite_proto_params (e, qparams, zprefix) FILE *e; const struct uuconf_proto_param *qparams; const char *zprefix; { const struct uuconf_proto_param *qp; if (qparams == NULL || qparams == (struct uuconf_proto_param *) &_uuconf_unset) return; for (qp = qparams; qp->uuconf_bproto != '\0'; qp++) { const struct uuconf_proto_param_entry *qe; for (qe = qp->uuconf_qentries; qe->uuconf_cargs > 0; qe++) { int i; fprintf (e, "%sprotocol-parameter %c", zprefix, qp->uuconf_bproto); for (i = 0; i < qe->uuconf_cargs; i++) fprintf (e, " %s", qe->uuconf_pzargs[i]); fprintf (e, "\n"); } } } /* Write out Taylor UUCP system information. */ static void uvwrite_taylor_system (e, q) FILE *e; const struct uuconf_system *q; { char **pz; const struct uuconf_system *qlast; fprintf (e, "# Start of system %s\n", q->uuconf_zname); fprintf (e, "system %s\n", q->uuconf_zname); if (q->uuconf_pzalias != NULL && q->uuconf_pzalias != (char **) &_uuconf_unset) for (pz = q->uuconf_pzalias; *pz != NULL; pz++) uvwrite_string (e, *pz, "alias"); for (qlast = NULL; q != NULL; qlast = q, q = q->uuconf_qalternate) { struct uuconf_timespan *qtime; if (qlast != NULL) { fprintf (e, "alternate"); if (q->uuconf_zalternate != (char *) &_uuconf_unset && q->uuconf_zalternate != NULL) fprintf (e, " %s", q->uuconf_zalternate); fprintf (e, "\n"); } #define CHANGED(x) (qlast == NULL || qlast->x != q->x) if (CHANGED (uuconf_qtimegrade) && (q->uuconf_qtimegrade != (struct uuconf_timespan *) &_uuconf_unset)) { if (q->uuconf_qtimegrade == NULL) fprintf (e, "time never\n"); else { for (qtime = q->uuconf_qtimegrade; qtime != NULL; qtime = qtime->uuconf_qnext) { if ((char) qtime->uuconf_ival == UUCONF_GRADE_LOW) fprintf (e, "time "); else fprintf (e, "timegrade %c ", (char) qtime->uuconf_ival); uvwrite_time (e, qtime); if (qtime->uuconf_cretry != 0) fprintf (e, " %d", qtime->uuconf_cretry); fprintf (e, "\n"); } } } if (CHANGED (uuconf_qcalltimegrade) && (q->uuconf_qcalltimegrade != (struct uuconf_timespan *) &_uuconf_unset)) { for (qtime = q->uuconf_qcalltimegrade; qtime != NULL; qtime = qtime->uuconf_qnext) { fprintf (e, "call-timegrade %c ", (char) qtime->uuconf_ival); uvwrite_time (e, qtime); fprintf (e, "\n"); } } if (CHANGED (uuconf_qcalledtimegrade) && (q->uuconf_qcalledtimegrade != (struct uuconf_timespan *) &_uuconf_unset)) { for (qtime = q->uuconf_qcalledtimegrade; qtime != NULL; qtime = qtime->uuconf_qnext) { fprintf (e, "called-timegrade %c ", (char) qtime->uuconf_ival); uvwrite_time (e, qtime); fprintf (e, "\n"); } } if (CHANGED (uuconf_qcall_local_size)) uvwrite_size (e, q->uuconf_qcall_local_size, "call-local-size"); if (CHANGED (uuconf_qcall_remote_size)) uvwrite_size (e, q->uuconf_qcall_remote_size, "call-remote-size"); if (CHANGED (uuconf_qcalled_local_size)) uvwrite_size (e, q->uuconf_qcalled_local_size, "called-local-size"); if (CHANGED (uuconf_qcalled_remote_size)) uvwrite_size (e, q->uuconf_qcalled_remote_size, "called-remote-size"); if (CHANGED (uuconf_ibaud) || CHANGED (uuconf_ihighbaud)) { if (q->uuconf_ibaud >= 0) { if (q->uuconf_ihighbaud > 0) fprintf (e, "baud-range %ld %ld\n", q->uuconf_ibaud, q->uuconf_ihighbaud); else fprintf (e, "baud %ld\n", q->uuconf_ibaud); } } if (CHANGED (uuconf_zport) || CHANGED (uuconf_qport)) { if (q->uuconf_zport != NULL && q->uuconf_zport != (char *) &_uuconf_unset) uvwrite_string (e, q->uuconf_zport, "port"); else if (q->uuconf_qport != NULL && (q->uuconf_qport != (struct uuconf_port *) &_uuconf_unset)) uvwrite_taylor_port (e, q->uuconf_qport, "port "); } if (CHANGED (uuconf_zphone)) { const char *zcmd; if (q->uuconf_qport != NULL && q->uuconf_qport != (struct uuconf_port *) &_uuconf_unset && (q->uuconf_qport->uuconf_ttype == UUCONF_PORTTYPE_TCP || q->uuconf_qport->uuconf_ttype == UUCONF_PORTTYPE_TLI)) zcmd = "address"; else zcmd = "phone"; uvwrite_string (e, q->uuconf_zphone, zcmd); } uvwrite_chat (e, &q->uuconf_schat, (qlast == NULL ? (struct uuconf_chat *) NULL : &qlast->uuconf_schat), "", TRUE); if (CHANGED (uuconf_zcall_login)) uvwrite_string (e, q->uuconf_zcall_login, "call-login"); if (CHANGED (uuconf_zcall_password)) uvwrite_string (e, q->uuconf_zcall_password, "call-password"); if (CHANGED (uuconf_zcalled_login)) uvwrite_string (e, q->uuconf_zcalled_login, "called-login"); if (CHANGED (uuconf_fcallback)) uvwrite_boolean (e, q->uuconf_fcallback, "callback"); if (CHANGED (uuconf_fsequence)) uvwrite_boolean (e, q->uuconf_fsequence, "sequence"); if (CHANGED (uuconf_zprotocols)) uvwrite_string (e, q->uuconf_zprotocols, "protocol"); if (CHANGED (uuconf_qproto_params)) uvwrite_proto_params (e, q->uuconf_qproto_params, ""); uvwrite_chat (e, &q->uuconf_scalled_chat, (qlast == NULL ? (struct uuconf_chat *) NULL : &qlast->uuconf_scalled_chat), "called-", FALSE); if (CHANGED (uuconf_zdebug)) uvwrite_string (e, q->uuconf_zdebug, "debug"); if (CHANGED (uuconf_zmax_remote_debug)) uvwrite_string (e, q->uuconf_zmax_remote_debug, "max-remote-debug"); if ((CHANGED (uuconf_fsend_request) || CHANGED (uuconf_frec_request)) && (q->uuconf_fsend_request >= 0 || q->uuconf_frec_request >= 0)) { if (q->uuconf_fsend_request >= 0 && (q->uuconf_fsend_request > 0 ? q->uuconf_frec_request > 0 : q->uuconf_frec_request == 0)) uvwrite_boolean (e, q->uuconf_fsend_request, "request"); else { uvwrite_boolean (e, q->uuconf_fsend_request, "send-request"); uvwrite_boolean (e, q->uuconf_frec_request, "receive-request"); } } if ((CHANGED (uuconf_fcall_transfer) || CHANGED (uuconf_fcalled_transfer)) && (q->uuconf_fcall_transfer >= 0 || q->uuconf_fcalled_transfer >= 0)) { if (q->uuconf_fcall_transfer >= 0 && (q->uuconf_fcall_transfer > 0 ? q->uuconf_fcalled_transfer > 0 : q->uuconf_fcalled_transfer == 0)) uvwrite_boolean (e, q->uuconf_fcall_transfer, "transfer"); else { uvwrite_boolean (e, q->uuconf_fcall_transfer, "call-transfer"); uvwrite_boolean (e, q->uuconf_fcalled_transfer, "called-transfer"); } } if (CHANGED (uuconf_pzlocal_send)) uvwrite_string_array (e, q->uuconf_pzlocal_send, "local-send"); if (CHANGED (uuconf_pzremote_send)) uvwrite_string_array (e, q->uuconf_pzremote_send, "remote-send"); if (CHANGED (uuconf_pzlocal_receive)) uvwrite_string_array (e, q->uuconf_pzlocal_receive, "local-receive"); if (CHANGED (uuconf_pzremote_receive)) uvwrite_string_array (e, q->uuconf_pzremote_receive, "remote-receive"); if (CHANGED (uuconf_pzpath)) uvwrite_string_array (e, q->uuconf_pzpath, "command-path"); if (CHANGED (uuconf_pzcmds)) uvwrite_string_array (e, q->uuconf_pzcmds, "commands"); if (CHANGED (uuconf_cfree_space) && q->uuconf_cfree_space >= 0) fprintf (e, "free-space %ld\n", q->uuconf_cfree_space); if (CHANGED (uuconf_pzforward_from)) uvwrite_string_array (e, q->uuconf_pzforward_from, "forward-from"); if (CHANGED (uuconf_pzforward_to)) uvwrite_string_array (e, q->uuconf_pzforward_to, "forward-to"); if (CHANGED (uuconf_zpubdir)) uvwrite_string (e, q->uuconf_zpubdir, "pubdir"); if (CHANGED (uuconf_zlocalname)) uvwrite_string (e, q->uuconf_zlocalname, "myname"); } } /* Write out V2 system information. */ static void uvwrite_v2_system (e, q) FILE *e; const struct uuconf_system *q; { for (; q != NULL; q = q->uuconf_qalternate) { fprintf (e, "%s", q->uuconf_zname); if (q->uuconf_qtimegrade != (struct uuconf_timespan *) &_uuconf_unset) { fprintf (e, " "); uvwrite_time (e, q->uuconf_qtimegrade); if (q->uuconf_zport != (char *) &_uuconf_unset || q->uuconf_qport != (struct uuconf_port *) &_uuconf_unset) { struct uuconf_port *qp; boolean ftcp; qp = q->uuconf_qport; ftcp = (qp != (struct uuconf_port *) &_uuconf_unset && qp != NULL && qp->uuconf_ttype == UUCONF_PORTTYPE_TCP); if (ftcp || (q->uuconf_zport != NULL && q->uuconf_zport != (char *) &_uuconf_unset)) { if (ftcp) fprintf (e, " TCP"); else fprintf (e, " %s", q->uuconf_zport); if (ftcp || q->uuconf_ibaud >= 0) { fprintf (e, " "); if (ftcp) { const char *zport; zport = qp->uuconf_u.uuconf_stcp.uuconf_zport; if (zport == NULL) zport = "uucp"; fprintf (e, "%s", zport); } else fprintf (e, "%ld", q->uuconf_ibaud); if (q->uuconf_zphone != (char *) &_uuconf_unset && q->uuconf_zphone != NULL) { char **pzc; fprintf (e, " %s", q->uuconf_zphone); pzc = q->uuconf_schat.uuconf_pzchat; if (pzc != (char **) &_uuconf_unset && pzc != NULL) { fprintf (e, " "); uvwrite_chat_script (e, pzc); } } } } } } fprintf (e, "\n"); /* Here we should gather information to write out to USERFILE and L.cmds, and perhaps some day we will. It's much more likely to happen if somebody else does it, though. */ } } /* Write out HDB system information. */ static void uvwrite_hdb_system (e, qsys) FILE *e; const struct uuconf_system *qsys; { const struct uuconf_system *q; struct shpermissions sperm; char *azmachine[2]; char *azlogname[2]; for (q = qsys; q != NULL; q = q->uuconf_qalternate) { if (q->uuconf_fcall) { fprintf (e, "%s", q->uuconf_zname); if (q->uuconf_qtimegrade != (struct uuconf_timespan *) &_uuconf_unset) { const char *zport; fprintf (e, " "); uvwrite_time (e, q->uuconf_qtimegrade); zport = q->uuconf_zport; if (q->uuconf_qport != NULL && q->uuconf_qport != (struct uuconf_port *) &_uuconf_unset && q->uuconf_qport->uuconf_ttype == UUCONF_PORTTYPE_TCP) zport = "TCP"; if (zport != NULL && zport != (char *) &_uuconf_unset) { fprintf (e, " %s", zport); if (q->uuconf_zprotocols != (char *) &_uuconf_unset && q->uuconf_zprotocols != NULL) fprintf (e, ",%s", q->uuconf_zprotocols); if (q->uuconf_ibaud >= 0 || q->uuconf_zphone != (char *) &_uuconf_unset) { fprintf (e, " "); if (q->uuconf_ibaud < 0) fprintf (e, "Any"); else { fprintf (e, "%ld", q->uuconf_ibaud); if (q->uuconf_ihighbaud >= 0) fprintf (e, "-%ld", q->uuconf_ihighbaud); } if (q->uuconf_zphone != (char *) &_uuconf_unset && q->uuconf_zphone != NULL) { char **pzc; fprintf (e, " %s", q->uuconf_zphone); pzc = q->uuconf_schat.uuconf_pzchat; if (pzc != (char **) &_uuconf_unset && pzc != NULL) { fprintf (e, " "); uvwrite_chat_script (e, pzc); } } } } } fprintf (e, "\n"); } } /* Build a Permissions entry for this system. There will be only one MACHINE entry for a given system. */ for (q = qsys; q != NULL; q = q->uuconf_qalternate) if (q->uuconf_fcall) break; if (q != NULL) { sperm.qnext = NULL; sperm.pzlogname = NULL; sperm.pzmachine = NULL; sperm.frequest = -1; sperm.fsendfiles = -1; sperm.pzread = NULL; sperm.pzwrite = NULL; sperm.fcallback = -1; sperm.pzcommands = NULL; sperm.pzvalidate = NULL; sperm.zmyname = NULL; sperm.zpubdir = NULL; sperm.pzalias = NULL; azmachine[0] = q->uuconf_zname; azmachine[1] = NULL; sperm.pzmachine = azmachine; if (q->uuconf_fsend_request >= 0) sperm.frequest = q->uuconf_fsend_request; if (q->uuconf_pzremote_send != (char **) &_uuconf_unset && q->uuconf_pzremote_send != NULL) sperm.pzread = q->uuconf_pzremote_send; if (q->uuconf_pzremote_receive != (char **) &_uuconf_unset && q->uuconf_pzremote_receive != NULL) sperm.pzwrite = q->uuconf_pzremote_receive; if (q->uuconf_pzcmds != (char **) &_uuconf_unset && q->uuconf_pzcmds != NULL) sperm.pzcommands = q->uuconf_pzcmds; if (q->uuconf_zlocalname != (char *) &_uuconf_unset && q->uuconf_zlocalname != NULL) sperm.zmyname = q->uuconf_zlocalname; if (q->uuconf_zpubdir != (char *) &_uuconf_unset && q->uuconf_zpubdir != NULL) sperm.zpubdir = q->uuconf_zpubdir; if (q->uuconf_pzalias != (char **) &_uuconf_unset && q->uuconf_pzalias != NULL) sperm.pzalias = q->uuconf_pzalias; if (q->uuconf_fcalled && q->uuconf_zcalled_login != (char *) &_uuconf_unset && q->uuconf_zcalled_login != NULL) { azlogname[0] = q->uuconf_zcalled_login; azlogname[1] = NULL; sperm.pzlogname = azlogname; if (q->uuconf_fcalled_transfer >= 0) sperm.fsendfiles = q->uuconf_fcalled_transfer; if (q->uuconf_fcallback >= 0) sperm.fcallback = q->uuconf_fcallback; sperm.pzvalidate = azmachine; } uvadd_perm (&sperm); } /* Now add a Permissions entry for each alternative that is not used for calling out. */ for (q = qsys; q != NULL; q = q->uuconf_qalternate) { if (! q->uuconf_fcalled || q->uuconf_fcall) continue; sperm.qnext = NULL; sperm.pzlogname = NULL; sperm.pzmachine = NULL; sperm.frequest = -1; sperm.fsendfiles = -1; sperm.pzread = NULL; sperm.pzwrite = NULL; sperm.fcallback = -1; sperm.pzcommands = NULL; sperm.pzvalidate = NULL; sperm.zmyname = NULL; sperm.zpubdir = NULL; sperm.pzalias = NULL; if (q->uuconf_zcalled_login != (char *) &_uuconf_unset && q->uuconf_zcalled_login != NULL) azlogname[0] = q->uuconf_zcalled_login; else azlogname[0] = (char *) "OTHER"; azlogname[1] = NULL; sperm.pzlogname = azlogname; if (q->uuconf_fsend_request >= 0) sperm.frequest = q->uuconf_fsend_request; if (q->uuconf_fcalled_transfer >= 0) sperm.fsendfiles = q->uuconf_fcalled_transfer; if (q->uuconf_pzremote_send != (char **) &_uuconf_unset && q->uuconf_pzremote_send != NULL) sperm.pzread = q->uuconf_pzremote_send; if (q->uuconf_pzremote_receive != (char **) &_uuconf_unset && q->uuconf_pzremote_receive != NULL) sperm.pzwrite = q->uuconf_pzremote_receive; if (q->uuconf_fcallback >= 0) sperm.fcallback = q->uuconf_fcallback; if (q->uuconf_zlocalname != (char *) &_uuconf_unset && q->uuconf_zlocalname != NULL) sperm.zmyname = q->uuconf_zlocalname; if (q->uuconf_zpubdir != (char *) &_uuconf_unset && q->uuconf_zpubdir != NULL) sperm.zpubdir = q->uuconf_zpubdir; uvadd_perm (&sperm); } } /* Compare two strings from a Permissions entry, returning TRUE if they are the same. */ static boolean fvperm_string_cmp (z1, z2) const char *z1; const char *z2; { if (z1 == NULL) return z2 == NULL; if (z2 == NULL) return FALSE; return strcmp (z1, z2) == 0; } /* Compare two arrays of strings from a Permissions entry, returning TRUE if they are the same. */ static boolean fvperm_array_cmp (pz1, pz2) const char **pz1; const char **pz2; { if (pz1 == NULL) return pz2 == NULL; if (pz2 == NULL) return FALSE; for (; *pz1 != NULL && *pz2 != NULL; pz1++, pz2++) if (strcmp (*pz1, *pz2) != 0) break; return *pz1 == NULL && *pz2 == NULL; } /* Add a Permissions entry to a global list, combining entries where possible. */ static void uvadd_perm (qadd) struct shpermissions *qadd; { struct shpermissions *qlook; struct shpermissions *qnew; int iret; /* If there's no information, don't bother to add this entry. */ if (qadd->pzlogname == NULL && qadd->frequest < 0 && qadd->fsendfiles < 0 && qadd->pzread == NULL && qadd->pzwrite == NULL && qadd->fcallback < 0 && qadd->pzcommands == NULL && qadd->pzvalidate == NULL && qadd->zmyname == NULL && qadd->zpubdir == NULL && qadd->pzalias == NULL) return; for (qlook = qVperms; qlook != NULL; qlook = qlook->qnext) { /* See if we can merge qadd into qlook. */ if (qadd->pzlogname == NULL ? qlook->pzlogname != NULL : qlook->pzlogname == NULL) continue; if (qadd->pzmachine == NULL ? qlook->pzmachine != NULL : qlook->pzmachine == NULL) continue; if (qadd->frequest != qlook->frequest || qadd->fsendfiles != qlook->fsendfiles || qadd->fcallback != qlook->fcallback) continue; if (! fvperm_string_cmp (qadd->zmyname, qlook->zmyname) || ! fvperm_string_cmp (qadd->zpubdir, qlook->zpubdir)) continue; if (! fvperm_array_cmp ((const char **) qadd->pzread, (const char **) qlook->pzread) || ! fvperm_array_cmp ((const char **) qadd->pzwrite, (const char **) qlook->pzwrite) || ! fvperm_array_cmp ((const char **) qadd->pzcommands, (const char **) qlook->pzcommands)) continue; /* Merge qadd into qlook. */ if (qadd->pzmachine != NULL) { iret = _uuconf_iadd_string ((struct sglobal *) NULL, qadd->pzmachine[0], FALSE, TRUE, &qlook->pzmachine, (pointer) NULL); if (iret != UUCONF_SUCCESS) uvuuconf_error ((pointer) NULL, iret); } if (qadd->pzlogname != NULL) { iret = _uuconf_iadd_string ((struct sglobal *) NULL, qadd->pzlogname[0], FALSE, TRUE, &qlook->pzlogname, (pointer) NULL); if (iret != UUCONF_SUCCESS) uvuuconf_error ((pointer) NULL, iret); } if (qadd->pzalias != NULL) { char **pz; for (pz = qadd->pzalias; *pz != NULL; pz++) { iret = _uuconf_iadd_string ((struct sglobal *) NULL, *pz, FALSE, TRUE, &qlook->pzalias, (pointer) NULL); if (iret != UUCONF_SUCCESS) uvuuconf_error ((pointer) NULL, iret); } } return; } /* We must add qadd as a new entry on the list, which means we must copy it into the heap. */ qnew = (struct shpermissions *) malloc (sizeof (struct shpermissions)); if (qnew == NULL) uvuuconf_error ((pointer) NULL, UUCONF_MALLOC_FAILED); *qnew = *qadd; if (qadd->pzmachine != NULL) { qnew->pzmachine = NULL; iret = _uuconf_iadd_string ((struct sglobal *) NULL, qadd->pzmachine[0], FALSE, FALSE, &qnew->pzmachine, (pointer) NULL); if (iret != UUCONF_SUCCESS) uvuuconf_error ((pointer) NULL, iret); } if (qadd->pzlogname != NULL) { qnew->pzlogname = NULL; iret = _uuconf_iadd_string ((struct sglobal *) NULL, qadd->pzlogname[0], FALSE, FALSE, &qnew->pzlogname, (pointer) NULL); if (iret != UUCONF_SUCCESS) uvuuconf_error ((pointer) NULL, iret); } if (qadd->pzvalidate != NULL) qnew->pzvalidate = qnew->pzmachine; qnew->qnext = qVperms; qVperms = qnew; } /* Write out the Permissions entries. */ static void uvwrite_perms () { char ab[sizeof ZCURDIR + sizeof HDB_PERMISSIONS - 1]; FILE *e; struct shpermissions *q; sprintf (ab, "%s%s", ZCURDIR, HDB_PERMISSIONS); e = fopen (ab, "w"); if (e == NULL) { fprintf (stderr, "uuchk:%s: ", ab); perror ("fopen"); exit (EXIT_FAILURE); } fprintf (e, "# Permissions file automatically generated by uuconv.\n"); for (q = qVperms; q != NULL; q = q->qnext) { size_t ccol; ccol = 0; uvwrite_perm_array (e, (const char **) q->pzlogname, "LOGNAME", &ccol); uvwrite_perm_array (e, (const char **) q->pzmachine, "MACHINE", &ccol); uvwrite_perm_boolean (e, q->frequest, "REQUEST", &ccol, FALSE); uvwrite_perm_boolean (e, q->fsendfiles, "SENDFILES", &ccol, TRUE); uvwrite_perm_rw_array (e, (const char **) q->pzread, "READ", &ccol); uvwrite_perm_rw_array (e, (const char **) q->pzwrite, "WRITE", &ccol); uvwrite_perm_boolean (e, q->fcallback, "CALLBACK", &ccol, FALSE); uvwrite_perm_array (e, (const char **) q->pzcommands, "COMMANDS", &ccol); uvwrite_perm_array (e, (const char **) q->pzvalidate, "VALIDATE", &ccol); uvwrite_perm_string (e, q->zmyname, "MYNAME", &ccol); uvwrite_perm_string (e, q->zpubdir, "PUBDIR", &ccol); uvwrite_perm_array (e, (const char **) q->pzalias, "ALIAS", &ccol); fprintf (e, "\n"); } if (ferror (e) || fclose (e) == EOF) { fprintf (stderr, "uuchk:%s: error during output\n", HDB_PERMISSIONS); exit (EXIT_FAILURE); } } /* Write an array out to the Permissions file. */ static void uvwrite_perm_array (e, pzarg, zcmd, pccol) FILE *e; const char **pzarg; const char *zcmd; size_t *pccol; { size_t c; const char **pz; if (pzarg == NULL) return; c = strlen (zcmd) + 1; for (pz = pzarg; *pz != NULL; pz++) c += strlen (*pz) + 1; if (*pccol > 20 && c + *pccol > 75) { fprintf (e, " \\\n"); *pccol = c - 1; } else { if (*pccol != 0) fprintf (e, " "); *pccol += c; } fprintf (e, "%s=", zcmd); for (pz = pzarg; *pz != NULL; pz++) { if (pz != pzarg) fprintf (e, ":"); fprintf (e, "%s", *pz); } } /* Write a boolean value out to the Permissions file. This may be either a yes/no boolean or a yes/call boolean (the latter is for SENDFILES). */ static void uvwrite_perm_boolean (e, f, zcmd, pccol, fsendfiles) FILE *e; int f; const char *zcmd; size_t *pccol; boolean fsendfiles; { const char *az[2]; if (f < 0) return; if (f) az[0] = "yes"; else az[0] = fsendfiles ? "call" : "no"; az[1] = NULL; uvwrite_perm_array (e, az, zcmd, pccol); } /* Write a set of READ or WRITE entries to the Permissions file. We have to separate out all entries that start with '!'. */ static void uvwrite_perm_rw_array (e, pzarg, zcmd, pccol) FILE *e; const char **pzarg; const char *zcmd; size_t *pccol; { size_t c; const char **pz, **pzcopy, **pzset; if (pzarg == NULL) return; c = 0; for (pz = pzarg; *pz != NULL; pz++) c++; pzcopy = (const char **) malloc ((c + 1) * sizeof (char *)); if (pzcopy == NULL) uvuuconf_error ((pointer) NULL, UUCONF_MALLOC_FAILED); pzset = pzcopy; for (pz = pzarg; *pz != NULL; pz++) if ((*pz)[0] != '!') *pzset++ = *pz; *pzset = NULL; if (pzset != pzcopy) uvwrite_perm_array (e, (const char **) pzcopy, zcmd, pccol); pzset = pzcopy; for (pz = pzarg; *pz != NULL; pz++) if ((*pz)[0] == '!') *pzset++ = *pz; *pzset = NULL; if (pzset != pzcopy) { char ab[20]; sprintf (ab, "NO%s", zcmd); uvwrite_perm_array (e, (const char **) pzcopy, ab, pccol); } } /* Write a string out to the Permissions file. */ static void uvwrite_perm_string (e, z, zcmd, pccol) FILE *e; const char *z; const char *zcmd; size_t *pccol; { const char *az[2]; if (z == NULL) return; az[0] = z; az[1] = NULL; uvwrite_perm_array (e, az, zcmd, pccol); } /* Write out a Taylor UUCP port. This is called via uuconf_find_port; the pinfo argument is the port file. */ static int ivwrite_taylor_port (qport, pinfo) struct uuconf_port *qport; pointer pinfo; { FILE *e = (FILE *) pinfo; fprintf (e, "port %s\n", qport->uuconf_zname); uvwrite_taylor_port (e, qport, ""); /* Return UUCONF_NOT_FOUND to force uuconf_find_port to keep looking for ports. */ return UUCONF_NOT_FOUND; } /* Write a port out to a Taylor UUCP configuration file. This doesn't output the name, since it is called to output a specially defined port in the sys file. */ static void uvwrite_taylor_port (e, qport, zprefix) FILE *e; struct uuconf_port *qport; const char *zprefix; { const char *ztype; char ab[100]; switch (qport->uuconf_ttype) { default: case UUCONF_PORTTYPE_UNKNOWN: fprintf (stderr, "uuconv: Bad port type\n"); exit (EXIT_FAILURE); break; case UUCONF_PORTTYPE_STDIN: ztype = "stdin"; break; case UUCONF_PORTTYPE_MODEM: ztype = "modem"; break; case UUCONF_PORTTYPE_DIRECT: ztype = "direct"; break; case UUCONF_PORTTYPE_TCP: ztype = "tcp"; break; case UUCONF_PORTTYPE_TLI: ztype = "tli"; break; case UUCONF_PORTTYPE_PIPE: ztype = "pipe"; break; } fprintf (e, "%stype %s\n", zprefix, ztype); if (qport->uuconf_zprotocols != NULL) fprintf (e, "%sprotocol %s\n", zprefix, qport->uuconf_zprotocols); if (qport->uuconf_qproto_params != NULL) uvwrite_proto_params (e, qport->uuconf_qproto_params, zprefix); if ((qport->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) { sprintf (ab, "%sseven-bit", zprefix); uvwrite_boolean (e, ((qport->uuconf_ireliable & UUCONF_RELIABLE_EIGHT) == 0), ab); sprintf (ab, "%sreliable", zprefix); uvwrite_boolean (e, ((qport->uuconf_ireliable & UUCONF_RELIABLE_RELIABLE) != 0), ab); sprintf (ab, "%shalf-duplex", zprefix); uvwrite_boolean (e, ((qport->uuconf_ireliable & UUCONF_RELIABLE_FULLDUPLEX) == 0), ab); } if (qport->uuconf_zlockname != NULL) fprintf (e, "%slockname %s\n", zprefix, qport->uuconf_zlockname); switch (qport->uuconf_ttype) { default: break; case UUCONF_PORTTYPE_MODEM: { struct uuconf_modem_port *qm; qm = &qport->uuconf_u.uuconf_smodem; if (qm->uuconf_zdevice != NULL) fprintf (e, "%sdevice %s\n", zprefix, qm->uuconf_zdevice); if (qm->uuconf_zdial_device != NULL) fprintf (e, "%sdial-device %s\n", zprefix, qm->uuconf_zdial_device); if (qm->uuconf_ibaud != 0) fprintf (e, "%sbaud %ld\n", zprefix, qm->uuconf_ibaud); if (qm->uuconf_ilowbaud != 0) fprintf (e, "%sbaud-range %ld %ld\n", zprefix, qm->uuconf_ilowbaud, qm->uuconf_ihighbaud); if (! qm->uuconf_fcarrier) fprintf (e, "%scarrier false\n", zprefix); if (! qm->uuconf_fhardflow) fprintf (e, "%shardflow false\n", zprefix); if (qm->uuconf_pzdialer != NULL) { if (qm->uuconf_pzdialer[1] == NULL) fprintf (e, "%sdialer %s\n", zprefix, qm->uuconf_pzdialer[0]); else { sprintf (ab, "%sdialer-sequence", zprefix); uvwrite_string_array (e, qm->uuconf_pzdialer, ab); } } if (qm->uuconf_qdialer != NULL) { sprintf (ab, "%sdialer ", zprefix); uvwrite_taylor_dialer (e, qm->uuconf_qdialer, ab); } } break; case UUCONF_PORTTYPE_DIRECT: { struct uuconf_direct_port *qd; qd = &qport->uuconf_u.uuconf_sdirect; if (qd->uuconf_zdevice != NULL) fprintf (e, "%sdevice %s\n", zprefix, qd->uuconf_zdevice); if (qd->uuconf_ibaud != 0) fprintf (e, "%sbaud %ld\n", zprefix, qd->uuconf_ibaud); if (qd->uuconf_fcarrier) fprintf (e, "%scarrier true\n", zprefix); if (! qd->uuconf_fhardflow) fprintf (e, "%shardflow false\n", zprefix); } break; case UUCONF_PORTTYPE_TCP: if (qport->uuconf_u.uuconf_stcp.uuconf_zport != NULL) fprintf (e, "%sservice %s\n", zprefix, qport->uuconf_u.uuconf_stcp.uuconf_zport); if (qport->uuconf_u.uuconf_stcp.uuconf_pzdialer != NULL) { sprintf (ab, "%sdialer-sequence", zprefix); uvwrite_string_array (e, qport->uuconf_u.uuconf_stcp.uuconf_pzdialer, ab); } break; case UUCONF_PORTTYPE_TLI: { struct uuconf_tli_port *qt; qt = &qport->uuconf_u.uuconf_stli; if (qt->uuconf_zdevice != NULL) fprintf (e, "%sdevice %s\n", zprefix, qt->uuconf_zdevice); sprintf (ab, "%sstream", zprefix); uvwrite_boolean (e, qt->uuconf_fstream, ab); if (qt->uuconf_pzpush != NULL) { sprintf (ab, "%spush", zprefix); uvwrite_string_array (e, qt->uuconf_pzpush, ab); } if (qt->uuconf_pzdialer != NULL) { sprintf (ab, "%sdialer-sequence", zprefix); uvwrite_string_array (e, qt->uuconf_pzdialer, ab); } if (qt->uuconf_zservaddr != NULL) fprintf (e, "%sserver-address %s\n", zprefix, qt->uuconf_zservaddr); } break; case UUCONF_PORTTYPE_PIPE: { struct uuconf_pipe_port *qp; qp = &qport->uuconf_u.uuconf_spipe; if (qp->uuconf_pzcmd != NULL) { sprintf (ab, "%scommad", zprefix); uvwrite_string_array (e, qp->uuconf_pzcmd, ab); } } break; } } /* Write out a port to the V2 L-devices file. This is called via uuconf_find_port. */ static int ivwrite_v2_port (qport, pinfo) struct uuconf_port *qport; pointer pinfo; { FILE *e = (FILE *) pinfo; if (qport->uuconf_ttype == UUCONF_PORTTYPE_DIRECT) { fprintf (e, "DIR %s - %ld direct", qport->uuconf_u.uuconf_sdirect.uuconf_zdevice, qport->uuconf_u.uuconf_sdirect.uuconf_ibaud); } else if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM) { fprintf (e, "%s %s ", qport->uuconf_zname, qport->uuconf_u.uuconf_smodem.uuconf_zdevice); if (qport->uuconf_u.uuconf_smodem.uuconf_zdial_device != NULL) fprintf (e, "%s", qport->uuconf_u.uuconf_smodem.uuconf_zdial_device); else fprintf (e, "-"); fprintf (e, " "); if (qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud != 0L) fprintf (e, "%ld-%ld", qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud, qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud); else if (qport->uuconf_u.uuconf_smodem.uuconf_ibaud != 0L) fprintf (e, "%ld", qport->uuconf_u.uuconf_smodem.uuconf_ibaud); else fprintf (e, "Any"); if (qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL) fprintf (e, " %s", qport->uuconf_u.uuconf_smodem.uuconf_pzdialer[0]); } else { fprintf (e, "# Ignoring port %s with unsupported type", qport->uuconf_zname); } fprintf (e, "\n"); /* Return UUCONF_NOT_FOUND to force uuconf_find_port to keep looking for a port. */ return UUCONF_NOT_FOUND; } /* Write out a port to the HDB Devices file. This is called via uuconf_find_port. */ static int ivwrite_hdb_port (qport, pinfo) struct uuconf_port *qport; pointer pinfo; { FILE *e = (FILE *) pinfo; if (qport->uuconf_ttype == UUCONF_PORTTYPE_DIRECT) { fprintf (e, "Direct"); if (qport->uuconf_zprotocols != NULL) fprintf (e, ",%s", qport->uuconf_zprotocols); fprintf (e, " "); if (qport->uuconf_u.uuconf_sdirect.uuconf_zdevice != NULL) fprintf (e, "%s", qport->uuconf_u.uuconf_sdirect.uuconf_zdevice); else fprintf (e, "%s", qport->uuconf_zname); fprintf (e, " - %ld", qport->uuconf_u.uuconf_sdirect.uuconf_ibaud); } else if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM) { fprintf (e, "%s", qport->uuconf_zname); if (qport->uuconf_zprotocols != NULL) fprintf (e, ",%s", qport->uuconf_zprotocols); fprintf (e, " "); if (qport->uuconf_u.uuconf_smodem.uuconf_zdevice != NULL) fprintf (e, "%s", qport->uuconf_u.uuconf_smodem.uuconf_zdevice); else fprintf (e, "%s", qport->uuconf_zname); fprintf (e, " "); if (qport->uuconf_u.uuconf_smodem.uuconf_zdial_device != NULL) fprintf (e, "%s", qport->uuconf_u.uuconf_smodem.uuconf_zdial_device); else fprintf (e, "-"); fprintf (e, " "); if (qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud != 0L) fprintf (e, "%ld-%ld", qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud, qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud); else if (qport->uuconf_u.uuconf_smodem.uuconf_ibaud != 0L) fprintf (e, "%ld", qport->uuconf_u.uuconf_smodem.uuconf_ibaud); else fprintf (e, "Any"); if (qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL) { char **pz; for (pz = qport->uuconf_u.uuconf_smodem.uuconf_pzdialer; *pz != NULL; pz++) fprintf (e, " %s", *pz); } } else if (qport->uuconf_ttype == UUCONF_PORTTYPE_TCP) { char **pz; fprintf (e, "TCP"); if (qport->uuconf_zprotocols != NULL) fprintf (e, ",%s", qport->uuconf_zprotocols); fprintf (e, " "); if (qport->uuconf_u.uuconf_stcp.uuconf_zport == NULL) fprintf (e, "uucp"); else fprintf (e, "%s", qport->uuconf_u.uuconf_stcp.uuconf_zport); fprintf (e, " - -"); pz = qport->uuconf_u.uuconf_stcp.uuconf_pzdialer; if (pz != NULL) for (; *pz != NULL; pz++) fprintf (e, " %s", *pz); } else if (qport->uuconf_ttype == UUCONF_PORTTYPE_TLI) { char **pz; fprintf (e, "%s", qport->uuconf_zname); if (qport->uuconf_zprotocols != NULL) fprintf (e, ",%s", qport->uuconf_zprotocols); fprintf (e, " "); if (qport->uuconf_u.uuconf_stli.uuconf_zdevice != NULL) fprintf (e, "%s", qport->uuconf_u.uuconf_smodem.uuconf_zdevice); else fprintf (e, "-"); fprintf (e, " - -"); pz = qport->uuconf_u.uuconf_stli.uuconf_pzdialer; if (pz == NULL || *pz == NULL || (strcmp (*pz, "TLI") != 0 && strcmp (*pz, "TLIS") != 0)) fprintf (e, " TLI%s \\D", qport->uuconf_u.uuconf_stli.uuconf_fstream ? "S" : ""); if (pz != NULL) for (; *pz != NULL; pz++) fprintf (e, " %s", *pz); } else { fprintf (e, "# Ignoring port %s with unsupported type", qport->uuconf_zname); } fprintf (e, "\n"); /* Return UUCONF_NOT_FOUND to force uuconf_find_port to keep looking for a port. */ return UUCONF_NOT_FOUND; } /* Write a dialer out to a Taylor UUCP configuration file. This doesn't output the name, since it is called to output a specially defined dialer in the sys or port file. */ static void uvwrite_taylor_dialer (e, qdialer, zprefix) FILE *e; struct uuconf_dialer *qdialer; const char *zprefix; { char ab[100]; /* Reset default values, so we don't output them unnecessarily. */ if (qdialer->uuconf_schat.uuconf_ctimeout == 60) qdialer->uuconf_schat.uuconf_ctimeout = -1; if (qdialer->uuconf_schat.uuconf_fstrip) qdialer->uuconf_schat.uuconf_fstrip = -1; if (qdialer->uuconf_scomplete.uuconf_ctimeout == 60) qdialer->uuconf_scomplete.uuconf_ctimeout = -1; if (qdialer->uuconf_scomplete.uuconf_fstrip) qdialer->uuconf_scomplete.uuconf_fstrip = -1; if (qdialer->uuconf_sabort.uuconf_ctimeout == 60) qdialer->uuconf_sabort.uuconf_ctimeout = -1; if (qdialer->uuconf_sabort.uuconf_fstrip) qdialer->uuconf_sabort.uuconf_fstrip = -1; uvwrite_chat (e, &qdialer->uuconf_schat, (struct uuconf_chat *) NULL, zprefix, FALSE); if (qdialer->uuconf_zdialtone != NULL && strcmp (qdialer->uuconf_zdialtone, ",") != 0) fprintf (e, "%sdialtone %s\n", zprefix, qdialer->uuconf_zdialtone); if (qdialer->uuconf_zpause != NULL && strcmp (qdialer->uuconf_zpause, ",") != 0) fprintf (e, "%spause %s\n", zprefix, qdialer->uuconf_zpause); if (! qdialer->uuconf_fcarrier) fprintf (e, "%scarrier false\n", zprefix); if (qdialer->uuconf_ccarrier_wait != 60) fprintf (e, "%scarrier-wait %d\n", zprefix, qdialer->uuconf_ccarrier_wait); if (qdialer->uuconf_fdtr_toggle) fprintf (e, "%sdtr-toggle %s %s\n", zprefix, qdialer->uuconf_fdtr_toggle ? "true" : "false", qdialer->uuconf_fdtr_toggle_wait ? "true" : "false"); sprintf (ab, "%scomplete-", zprefix); uvwrite_chat (e, &qdialer->uuconf_scomplete, (struct uuconf_chat *) NULL, ab, FALSE); sprintf (ab, "%sabort-", zprefix); uvwrite_chat (e, &qdialer->uuconf_sabort, (struct uuconf_chat *) NULL, ab, FALSE); if (qdialer->uuconf_qproto_params != NULL) uvwrite_proto_params (e, qdialer->uuconf_qproto_params, zprefix); if ((qdialer->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0) { sprintf (ab, "%sseven-bit", zprefix); uvwrite_boolean (e, ((qdialer->uuconf_ireliable & UUCONF_RELIABLE_EIGHT) == 0), ab); sprintf (ab, "%sreliable", zprefix); uvwrite_boolean (e, ((qdialer->uuconf_ireliable & UUCONF_RELIABLE_RELIABLE) != 0), ab); sprintf (ab, "%shalf-duplex", zprefix); uvwrite_boolean (e, ((qdialer->uuconf_ireliable & UUCONF_RELIABLE_FULLDUPLEX) == 0), ab); } } /* Write a dialer out to an HDB configuration file. */ static void uvwrite_hdb_dialer (e, qdialer) FILE *e; struct uuconf_dialer *qdialer; { fprintf (e, "%s ", qdialer->uuconf_zname); if (qdialer->uuconf_zdialtone != NULL) fprintf (e, "=%c", qdialer->uuconf_zdialtone[0]); if (qdialer->uuconf_zpause != NULL) fprintf (e, "-%c", qdialer->uuconf_zpause[0]); if (qdialer->uuconf_schat.uuconf_pzchat != NULL) { if (qdialer->uuconf_zdialtone == NULL && qdialer->uuconf_zpause == NULL) fprintf (e, "\"\""); fprintf (e, " "); uvwrite_chat_script (e, qdialer->uuconf_schat.uuconf_pzchat); } fprintf (e, "\n"); } /* Display a uuconf error and exit. */ static void uvuuconf_error (puuconf, iret) pointer puuconf; int iret; { char ab[512]; (void) uuconf_error_string (puuconf, iret, ab, sizeof ab); if ((iret & UUCONF_ERROR_FILENAME) == 0) fprintf (stderr, "uuconv: %s\n", ab); else fprintf (stderr, "uuconv:%s\n", ab); if (UUCONF_ERROR_VALUE (iret) != UUCONF_FOPEN_FAILED) exit (EXIT_FAILURE); } uucp-1.07/uucp.c0000664000076400007640000007731107665321757007263 /* uucp.c Prepare to copy a file to or from a remote system. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char uucp_rcsid[] = "$Id: uucp.c,v 1.76 2002/03/05 19:10:42 ian Rel $"; #endif #include #include #include "getopt.h" #include "uudefs.h" #include "uuconf.h" #include "system.h" /* Local functions. */ static void ucusage P((void)); static void uchelp P((void)); static void ucdirfile P((const char *zdir, const char *zfile, pointer pinfo)); static void uccopy P((const char *zfile, const char *zdest, boolean fforcelocal)); static void ucadd_cmd P((const struct uuconf_system *qsys, const struct scmd *qcmd, const char *zlog)); static void ucspool_cmds P((boolean fjobid)); static const char *zcone_system P((boolean *pfany)); static void ucrecord_file P((const char *zfile)); static void ucabort P((void)); /* Long getopt options. Note that any changes here must be reflected in the code in uuxqt.c which checks the options used for the uucp command. */ static const struct option asClongopts[] = { { "copy", no_argument, NULL, 'C' }, { "nocopy", no_argument, NULL, 'c' }, { "directories", no_argument, NULL, 'd' }, { "nodirectories", no_argument, NULL, 'f' }, { "grade", required_argument, NULL, 'g' }, { "jobid", no_argument, NULL, 'j' }, { "mail", no_argument, NULL, 'm' }, { "notify", required_argument, NULL, 'n' }, { "nouucico", no_argument, NULL, 'r' }, { "recursive", no_argument, NULL, 'R' }, { "status", required_argument, NULL, 's' }, { "uuto", no_argument, NULL, 't' }, { "user", required_argument, NULL, 'u' }, { "noexpand", no_argument, NULL, 'W' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; /* Local variables. There are a bunch of these, mostly set by the options and the last (the destination) argument. These have file scope so that they may be easily passed into uccopy; they could for the most part also be wrapped up in a structure and passed in. */ /* The uuconf global pointer. */ static pointer pCuuconf; /* TRUE if source files should be copied to the spool directory. */ static boolean fCcopy = TRUE; /* Grade to use. */ static char bCgrade = BDEFAULT_UUCP_GRADE; /* Whether to send mail to the requesting user when the copy is complete. */ static boolean fCmail = FALSE; /* User to notify on remote system. */ static const char *zCnotify = ""; /* TRUE if remote files should be prefixed with the current working directory. */ static boolean fCexpand = TRUE; /* TRUE if necessary directories should be created on the destination system. */ static boolean fCmkdirs = TRUE; /* Local name. */ static const char *zClocalname; /* User name. */ static const char *zCuser = NULL; /* TRUE if this is a remote request. */ static boolean fCremote = FALSE; /* TRUE if the destination is this system. */ static boolean fClocaldest; /* Destination system. */ static struct uuconf_system sCdestsys; /* Systems to forward to, if not NULL. */ static char *zCforward; /* Options to use when sending a file. */ static char abCsend_options[20]; /* Options to use when receiving a file. */ static char abCrec_options[20]; /* TRUE if the current file being copied from is in the cwd. */ static boolean fCneeds_cwd; /* The main program. */ int main (argc, argv) int argc; char **argv; { /* -I: configuration file name. */ const char *zconfig = NULL; /* -j: output job id. */ boolean fjobid = FALSE; /* -r: don't start uucico when finished. */ boolean fuucico = TRUE; /* -R: copy directories recursively. */ boolean frecursive = FALSE; /* -s: report status to named file. */ const char *zstatus_file = NULL; /* -t: emulate uuto. */ boolean fuuto = FALSE; int iopt; pointer puuconf; int iuuconf; int i; boolean fgetcwd; struct uuconf_system slocalsys; char *zexclam; char *zdestfile; const char *zdestsys; char *zoptions; boolean fexit; zProgram = argv[0]; while ((iopt = getopt_long (argc, argv, "cCdfg:I:jmn:prRs:tu:Wvx:", asClongopts, (int *) NULL)) != EOF) { switch (iopt) { case 'c': /* Do not copy local files to spool directory. */ fCcopy = FALSE; break; case 'p': case 'C': /* Copy local files to spool directory. */ fCcopy = TRUE; break; case 'd': /* Create directories if necessary. */ fCmkdirs = TRUE; break; case 'f': /* Do not create directories if they don't exist. */ fCmkdirs = FALSE; break; case 'g': /* Set job grade. */ bCgrade = optarg[0]; break; case 'I': /* Name configuration file. */ if (fsysdep_other_config (optarg)) zconfig = optarg; break; case 'j': /* Output job id. */ fjobid = TRUE; break; case 'm': /* Mail to requesting user. */ fCmail = TRUE; break; case 'n': /* Notify remote user. */ zCnotify = optarg; break; case 'r': /* Don't start uucico when finished. */ fuucico = FALSE; break; case 'R': /* Copy directories recursively. */ frecursive = TRUE; break; case 's': /* Report status to named file. */ zstatus_file = optarg; break; case 't': /* Emulate uuto. */ fuuto = TRUE; break; case 'u': /* Set user name. */ zCuser = optarg; break; case 'W': /* Expand only local file names. */ fCexpand = FALSE; break; case 'x': #if DEBUG > 1 /* Set debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'v': /* Print version and exit. */ printf ("uucp (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 1: /* --help. */ uchelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found and flag set. */ break; default: ucusage (); /*NOTREACHED*/ } } if (! UUCONF_GRADE_LEGAL (bCgrade) || ((bCgrade < '0' || bCgrade > '9') && (bCgrade < 'a' || bCgrade > 'z') && (bCgrade < 'A' || bCgrade > 'Z'))) { ulog (LOG_ERROR, "Ignoring illegal grade"); bCgrade = BDEFAULT_UUCP_GRADE; } /* The user name must contain a '!', which is treated as a remote name, to avoid spoofing of other users (there is no advantage to spoofing remote users, except to send them random bits of mail, which you can do anyhow). */ if (zCuser != NULL) { if (strchr (zCuser, '!') != NULL) fCremote = TRUE; else { ulog (LOG_ERROR, "Ignoring local user name"); zCuser = NULL; } } if (argc - optind < 2) ucusage (); iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); pCuuconf = puuconf; #if DEBUG > 1 { const char *zdebug; iuuconf = uuconf_debuglevel (puuconf, &zdebug); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (zdebug != NULL) iDebug |= idebug_parse (zdebug); } #endif /* See if we are going to need to know the current directory. We just check each argument to see whether it's an absolute pathname. We actually aren't going to need the cwd if fCexpand is FALSE and the file is remote, but so what. */ fgetcwd = FALSE; for (i = optind; i < argc; i++) { zexclam = strrchr (argv[i], '!'); if (zexclam == NULL) zexclam = argv[i]; else ++zexclam; if (fsysdep_needs_cwd (zexclam)) { fgetcwd = TRUE; break; } } #ifdef SIGINT usysdep_signal (SIGINT); #endif #ifdef SIGHUP usysdep_signal (SIGHUP); #endif #ifdef SIGQUIT usysdep_signal (SIGQUIT); #endif #ifdef SIGTERM usysdep_signal (SIGTERM); #endif #ifdef SIGPIPE usysdep_signal (SIGPIPE); #endif usysdep_initialize (puuconf, INIT_SUID | (fgetcwd ? INIT_GETCWD : 0)); ulog_fatal_fn (ucabort); if (zCuser == NULL) zCuser = zsysdep_login_name (); iuuconf = uuconf_localname (puuconf, &zClocalname); if (iuuconf == UUCONF_NOT_FOUND) { zClocalname = zsysdep_localname (); if (zClocalname == NULL) exit (EXIT_FAILURE); } else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); /* Get the local system information. */ iuuconf = uuconf_system_info (puuconf, zClocalname, &slocalsys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); iuuconf = uuconf_system_local (puuconf, &slocalsys); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); slocalsys.uuconf_zname = (char *) zClocalname; } /* If we are emulating uuto, translate the destination argument, and notify the destination user. This had better not turn into something that requires the current directory, or we may have passed INIT_GETCWD incorrectly. */ if (fuuto) { if (*zCnotify == '\0') { zexclam = strrchr (argv[argc - 1], '!'); if (zexclam == NULL) ucusage (); zCnotify = zexclam + 1; } argv[argc - 1] = zsysdep_uuto (argv[argc - 1], zClocalname); if (argv[argc - 1] == NULL) ucusage (); } /* Set up the file transfer options. */ zoptions = abCsend_options; if (fCcopy) *zoptions++ = 'C'; else *zoptions++ = 'c'; if (fCmkdirs) *zoptions++ = 'd'; else *zoptions++ = 'f'; if (fCmail) *zoptions++ = 'm'; if (*zCnotify != '\0') *zoptions++ = 'n'; *zoptions = '\0'; zoptions = abCrec_options; if (fCmkdirs) *zoptions++ = 'd'; else *zoptions++ = 'f'; if (fCmail) *zoptions++ = 'm'; *zoptions = '\0'; argv[argc - 1] = zremove_local_sys (&slocalsys, argv[argc - 1]); zexclam = strchr (argv[argc - 1], '!'); if (zexclam == NULL) { zdestsys = zClocalname; zdestfile = argv[argc - 1]; fClocaldest = TRUE; } else { size_t clen; char *zcopy; clen = zexclam - argv[argc - 1]; zcopy = zbufalc (clen + 1); memcpy (zcopy, argv[argc - 1], clen); zcopy[clen] = '\0'; zdestsys = zcopy; zdestfile = zexclam + 1; fClocaldest = FALSE; } iuuconf = uuconf_system_info (puuconf, zdestsys, &sCdestsys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (fClocaldest) { iuuconf = uuconf_system_local (puuconf, &sCdestsys); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); sCdestsys.uuconf_zname = (char *) zClocalname; } else { if (! funknown_system (puuconf, zdestsys, &sCdestsys)) ulog (LOG_FATAL, "%s: System not found", zdestsys); } } /* Here zdestfile is the destination file name following the destination system name (if any); it may contain other systems to forward the files through. Isolate the file from the list of systems. */ zexclam = strrchr (zdestfile, '!'); if (zexclam == NULL) zCforward = NULL; else { size_t clen; #if DEBUG > 0 if (fClocaldest) ulog (LOG_FATAL, "Can't happen"); #endif clen = zexclam - zdestfile; zCforward = zbufalc (clen + 1); memcpy (zCforward, zdestfile, clen); zCforward[clen] = '\0'; zdestfile = zexclam + 1; } /* Turn the destination into an absolute path, unless it is on a remote system and -W was used. */ if (fClocaldest) zdestfile = zsysdep_local_file_cwd (zdestfile, sCdestsys.uuconf_zpubdir, (boolean *) NULL); else if (fCexpand) zdestfile = zsysdep_add_cwd (zdestfile); if (zdestfile == NULL) { ulog_close (); usysdep_exit (FALSE); } /* Process each source argument. */ for (i = optind; i < argc - 1 && ! FGOT_SIGNAL (); i++) { boolean flocal; char *zfrom; fCneeds_cwd = FALSE; argv[i] = zremove_local_sys (&slocalsys, argv[i]); if (strchr (argv[i], '!') != NULL) { flocal = FALSE; zfrom = zbufcpy (argv[i]); } else { /* This is a local file. Make sure we get it out of the original directory. We don't support local wildcards, leaving that to the shell. */ flocal = TRUE; if (fsysdep_needs_cwd (argv[i])) fCneeds_cwd = TRUE; zfrom = zsysdep_local_file_cwd (argv[i], sCdestsys.uuconf_zpubdir, (boolean *) NULL); if (zfrom == NULL) ucabort (); } if (! flocal || ! fsysdep_directory (zfrom)) uccopy (zfrom, zdestfile, FALSE); else { char *zbase, *zindir; if (! frecursive) ulog (LOG_FATAL, "%s: directory without -R", zfrom); zbase = zsysdep_base_name (zfrom); if (zbase == NULL) ucabort (); zindir = zsysdep_in_dir (zdestfile, zbase); ubuffree (zbase); if (zindir == NULL) ucabort (); usysdep_walk_tree (zfrom, ucdirfile, zindir); ubuffree (zindir); } ubuffree (zfrom); } /* See if we got an interrupt, presumably from the user. */ if (FGOT_SIGNAL ()) ucabort (); /* Now push out the actual commands, making log entries for them. */ ulog_to_file (puuconf, TRUE); ulog_user (zCuser); ucspool_cmds (fjobid); ulog_close (); if (! fuucico) fexit = TRUE; else { const char *zsys; boolean fany; zsys = zcone_system (&fany); if (zsys == NULL && ! fany) fexit = TRUE; else { const char *zarg; char *zconfigarg; if (zsys == NULL) zarg = "-r1"; else { char *z; z = zbufalc (sizeof "-Cs" + strlen (zsys)); sprintf (z, "-Cs%s", zsys); zarg = z; } if (zconfig == NULL) zconfigarg = NULL; else { zconfigarg = zbufalc (sizeof "-I" + strlen (zconfig)); sprintf (zconfigarg, "-I%s", zconfig); } fexit = fsysdep_run (FALSE, "uucico", zarg, zconfigarg); } } usysdep_exit (fexit); /* Avoid error about not returning. */ return 0; } /* Print usage message and die. */ static void ucusage () { fprintf (stderr, "Usage: %s [options] file1 [file2 ...] dest\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EXIT_FAILURE); } /* Print help message. */ static void uchelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); printf ("Usage: %s [options] file1 [file2 ...] dest\n", zProgram); printf (" -c,--nocopy: Do not copy local files to spool directory\n"); printf (" -C,-p,--copy: Copy local files to spool directory (default)\n"); printf (" -d,--directories: Create necessary directories (default)\n"); printf (" -f,--nodirectories: Do not create directories (fail if they do not exist)\n"); printf (" -g,--grade grade: Set job grade (must be alphabetic)\n"); printf (" -m,--mail: Report status of copy by mail\n"); printf (" -n,--notify user: Report status of copy by mail to remote user\n"); printf (" -R,--recursive: Copy directories recursively\n"); printf (" -r,--nouucico: Do not start uucico daemon\n"); printf (" -s,--status file: Report completion status to file\n"); printf (" -j,--jobid: Report job id\n"); printf (" -W,--noexpand: Do not add current directory to remote filenames\n"); printf (" -t,--uuto: Emulate uuto\n"); printf (" -u,--user name: Set user name\n"); printf (" -x,--debug debug: Set debugging level\n"); #if HAVE_TAYLOR_CONFIG printf (" -I,--config file: Set configuration file to use\n"); #endif /* HAVE_TAYLOR_CONFIG */ printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } /* This is called for each file in a directory heirarchy. */ static void ucdirfile (zfull, zrelative, pinfo) const char *zfull; const char *zrelative; pointer pinfo; { const char *zdestfile = (const char *) pinfo; char *zto; zto = zsysdep_in_dir (zdestfile, zrelative); if (zto == NULL) ucabort (); uccopy (zfull, zto, TRUE); ubuffree (zto); } /* Handle the copying of one regular file. The zdest argument is the destination file; if we are recursively copying a directory, it will be extended by any subdirectory names. Note that zdest is an absolute path. */ static void uccopy (zfile, zdest, fforcelocal) const char *zfile; const char *zdest; boolean fforcelocal; { struct scmd s; char *zexclam; char *zto; zexclam = strchr (zfile, '!'); if (zexclam == NULL || fforcelocal) { openfile_t efrom; /* Copy from a local file. Make sure the user has access to this file, since we are running setuid. */ if (! fsysdep_access (zfile)) ucabort (); /* If this copy is being requested by a remote system, we may transfer the file if it needs the current working directory (meaning, I hope, that it is in the execution directory) or it is on the permitted transfer list. Note that unlike most of the other checks, this one is not double-checked by uucico. */ if (fCremote && ! fCneeds_cwd && ! fin_directory_list (zfile, sCdestsys.uuconf_pzremote_send, sCdestsys.uuconf_zpubdir, TRUE, TRUE, (const char *) NULL)) ulog (LOG_FATAL, "Not permitted to send %s", zfile); if (fClocaldest) { boolean fok; unsigned int imode; /* Copy one local file to another. */ /* Check that we have permission to receive into the desired directory. */ if (fCremote) fok = fin_directory_list (zdest, sCdestsys.uuconf_pzremote_receive, sCdestsys.uuconf_zpubdir, TRUE, FALSE, (const char *) NULL); else fok = fin_directory_list (zdest, sCdestsys.uuconf_pzlocal_receive, sCdestsys.uuconf_zpubdir, TRUE, FALSE, zCuser); if (! fok) ulog (LOG_FATAL, "Not permitted to receive to %s", zdest); zto = zsysdep_add_base (zdest, zfile); if (zto == NULL) ucabort (); efrom = esysdep_user_fopen (zfile, TRUE, TRUE); if (! ffileisopen (efrom)) ucabort (); if (! fcopy_open_file (efrom, zto, FALSE, fCmkdirs, TRUE)) ucabort (); (void) ffileclose (efrom); ubuffree (zto); imode = ixsysdep_user_file_mode (zfile); if (imode != 0) (void) fsysdep_change_mode (zto, imode); } else { const char *zloc; char abtname[CFILE_NAME_LEN]; unsigned int imode; char *ztemp; /* Copy a local file to a remote file. We may have to copy the local file to the spool directory. */ imode = ixsysdep_user_file_mode (zfile); if (imode == 0) ucabort (); zloc = sCdestsys.uuconf_zlocalname; if (zloc == NULL) zloc = zClocalname; ztemp = zsysdep_data_file_name (&sCdestsys, zloc, bCgrade, FALSE, abtname, (char *) NULL, (char *) NULL); if (ztemp == NULL) ucabort (); if (! fCcopy) { /* If we are copying the file, we don't actually use the temporary file; we still want to get a name for the other system to use as a key for file restart. */ ubuffree (ztemp); /* Make sure the daemon will be permitted to send this file. */ if (! fsysdep_daemon_access (zfile)) ucabort (); if (! fin_directory_list (zfile, sCdestsys.uuconf_pzlocal_send, sCdestsys.uuconf_zpubdir, TRUE, TRUE, (fCremote ? (const char *) NULL : zCuser))) ulog (LOG_FATAL, "Daemon not permitted to send %s (suggest --copy)", zfile); } else { efrom = esysdep_user_fopen (zfile, TRUE, TRUE); if (! ffileisopen (efrom)) ucabort (); ucrecord_file (ztemp); if (! fcopy_open_file (efrom, ztemp, FALSE, TRUE, TRUE)) ucabort (); (void) ffileclose (efrom); } if (zCforward == NULL) { /* We're not forwarding. Just send the file. */ s.bcmd = 'S'; s.bgrade = bCgrade; s.pseq = NULL; s.zfrom = zbufcpy (zfile); s.zto = zbufcpy (zdest); s.zuser = zCuser; s.zoptions = abCsend_options; s.ztemp = zbufcpy (abtname); s.imode = imode; s.znotify = zCnotify; s.cbytes = -1; s.zcmd = NULL; s.ipos = 0; ucadd_cmd (&sCdestsys, &s, (const char *) NULL); } else { char *zbase; char *zxqt; char abxtname[CFILE_NAME_LEN]; char abdname[CFILE_NAME_LEN]; char abxname[CFILE_NAME_LEN]; FILE *e; char *zlog; /* We want to forward this file through sCdestsys to some other system(s). We set up a remote execution of uucp on sCdestsys to forward the file along. */ zbase = zsysdep_base_name (zfile); if (zbase == NULL) ucabort (); zxqt = zsysdep_data_file_name (&sCdestsys, zloc, bCgrade, TRUE, abxtname, abdname, abxname); if (zxqt == NULL) ucabort (); e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE); if (e == NULL) ucabort (); ucrecord_file (zxqt); fprintf (e, "U %s %s\n", zCuser, zloc); fprintf (e, "F %s %s\n", abdname, zbase); fprintf (e, "C uucp -C"); if (fCmkdirs) fprintf (e, " -d"); else fprintf (e, " -f"); fprintf (e, " -g %c", bCgrade); if (fCmail) fprintf (e, " -m"); if (*zCnotify != '\0') fprintf (e, " -n %s", zCnotify); if (! fCexpand) fprintf (e, " -W"); fprintf (e, " %s %s!%s\n", zbase, zCforward, zdest); ubuffree (zbase); if (! fstdiosync (e, zxqt)) ulog (LOG_FATAL, "fsync failed"); if (fclose (e) != 0) ulog (LOG_FATAL, "fclose: %s", strerror (errno)); /* Send the execution file. */ s.bcmd = 'S'; s.bgrade = bCgrade; s.pseq = NULL; s.zfrom = zbufcpy (abxtname); s.zto = zbufcpy (abxname); s.zuser = zCuser; s.zoptions = "C"; s.ztemp = s.zfrom; s.imode = 0666; s.znotify = NULL; s.cbytes = -1; s.zcmd = NULL; s.ipos = 0; zlog = zbufalc (sizeof "Queuing uucp !" + strlen (zfile) + strlen (zCforward) + strlen (zdest)); sprintf (zlog, "Queuing uucp %s %s!%s", zfile, zCforward, zdest); ucadd_cmd (&sCdestsys, &s, zlog); /* Send the data file. */ s.bcmd = 'S'; s.bgrade = bCgrade; s.pseq = NULL; s.zfrom = zbufcpy (zfile); s.zto = zbufcpy (abdname); s.zuser = zCuser; s.zoptions = fCcopy ? "C" : "c"; s.ztemp = zbufcpy (abtname); s.imode = 0666; s.znotify = NULL; s.cbytes = -1; s.zcmd = NULL; s.ipos = 0; ucadd_cmd (&sCdestsys, &s, ""); } } } else { char *zfrom; char *zforward; size_t clen; char *zcopy; struct uuconf_system *qfromsys; int iuuconf; const char *zloc; /* Copy from a remote file. Get the file name after any systems we may need to forward the file from. */ zfrom = strrchr (zfile, '!'); if (zfrom == zexclam) zforward = NULL; else { clen = zfrom - zexclam - 1; zforward = zbufalc (clen + 1); memcpy (zforward, zexclam + 1, clen); zforward[clen] = '\0'; } ++zfrom; if (fCexpand) { /* Add the current directory to the filename if it's not already there. */ zfrom = zsysdep_add_cwd (zfrom); if (zfrom == NULL) ucabort (); } /* Read the system information. */ clen = zexclam - zfile; zcopy = zbufalc (clen + 1); memcpy (zcopy, zfile, clen); zcopy[clen] = '\0'; qfromsys = ((struct uuconf_system *) xmalloc (sizeof (struct uuconf_system))); iuuconf = uuconf_system_info (pCuuconf, zcopy, qfromsys); if (iuuconf == UUCONF_NOT_FOUND) { if (! funknown_system (pCuuconf, zcopy, qfromsys)) ulog (LOG_FATAL, "%s: System not found", zcopy); } else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, pCuuconf, iuuconf); ubuffree (zcopy); zloc = qfromsys->uuconf_zlocalname; if (zloc == NULL) zloc = zClocalname; if (zforward == NULL && fClocaldest) { boolean fok; /* The file is to come directly from qfromsys to the local system. */ /* Check that we have permission to receive into the desired directory. If we don't have permission, uucico will fail. */ if (fCremote) fok = fin_directory_list (zdest, qfromsys->uuconf_pzremote_receive, qfromsys->uuconf_zpubdir, TRUE, FALSE, (const char *) NULL); else fok = fin_directory_list (zdest, qfromsys->uuconf_pzlocal_receive, qfromsys->uuconf_zpubdir, TRUE, FALSE, zCuser); if (! fok) ulog (LOG_FATAL, "Not permitted to receive to %s", zdest); /* If the remote filespec is wildcarded, we must generate an 'X' request. We currently check for Unix shell wildcards. Note that it should do no harm to mistake a non-wildcard for a wildcard. */ if (zfrom[strcspn (zfrom, "*?[")] != '\0') { s.bcmd = 'X'; zto = zbufalc (strlen (zloc) + strlen (zdest) + sizeof "!"); sprintf (zto, "%s!%s", zloc, zdest); } else { s.bcmd = 'R'; zto = zbufcpy (zdest); } s.bgrade = bCgrade; s.pseq = NULL; s.zfrom = zfrom; s.zto = zto; s.zuser = zCuser; s.zoptions = abCrec_options; s.ztemp = ""; s.imode = 0; s.znotify = ""; s.cbytes = -1; s.zcmd = NULL; s.ipos = 0; ucadd_cmd (qfromsys, &s, (const char *) NULL); } else { char *zxqt; char abtname[CFILE_NAME_LEN]; char abxname[CFILE_NAME_LEN]; FILE *e; char *zcmd; char *zlog; /* The file either comes from some other system through qfromsys or is intended for some other system. Send an execution request to qfromsys to handle everything. */ zxqt = zsysdep_data_file_name (qfromsys, zloc, bCgrade, TRUE, abtname, (char *) NULL, abxname); if (zxqt == NULL) ucabort (); e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE); if (e == NULL) ucabort (); ucrecord_file (zxqt); fprintf (e, "U %s %s\n", zCuser, zloc); fprintf (e, "C uucp -C"); if (fCmkdirs) fprintf (e, " -d"); else fprintf (e, " -f"); fprintf (e, " -g %c", bCgrade); if (fCmail) fprintf (e, " -m"); if (*zCnotify != '\0') fprintf (e, " -n %s", zCnotify); if (! fCexpand) fprintf (e, " -W"); clen = (strlen (zfrom) + strlen (zloc) + strlen (sCdestsys.uuconf_zname) + strlen (zdest)); if (zforward != NULL) clen += strlen (zforward); if (zCforward != NULL) clen += strlen (zCforward); zcmd = zbufalc (sizeof "! !!!" + clen); *zcmd = '\0'; if (zforward != NULL) sprintf (zcmd + strlen (zcmd), "%s!", zforward); sprintf (zcmd + strlen (zcmd), "%s %s!", zfrom, zloc); if (! fClocaldest) sprintf (zcmd + strlen (zcmd), "%s!", sCdestsys.uuconf_zname); if (zCforward != NULL) sprintf (zcmd + strlen (zcmd), "%s!", zCforward); sprintf (zcmd + strlen (zcmd), "%s", zdest); fprintf (e, " %s\n", zcmd); if (! fstdiosync (e, zxqt)) ulog (LOG_FATAL, "fsync failed"); if (fclose (e) != 0) ulog (LOG_FATAL, "fclose: %s", strerror (errno)); /* Send the execution file. */ s.bcmd = 'S'; s.bgrade = bCgrade; s.pseq = NULL; s.zfrom = zbufcpy (abtname); s.zto = zbufcpy (abxname); s.zuser = zCuser; s.zoptions = "C"; s.ztemp = s.zfrom; s.imode = 0666; s.znotify = NULL; s.cbytes = -1; s.zcmd = NULL; s.ipos = 0; zlog = zbufalc (sizeof "Queueing uucp " + strlen (zcmd)); sprintf (zlog, "Queueing uucp %s", zcmd); ucadd_cmd (qfromsys, &s, zlog); ubuffree (zcmd); ubuffree (zforward); } } } /* We keep a list of jobs for each system. */ struct sjob { struct sjob *qnext; const struct uuconf_system *qsys; int ccmds; struct scmd *pascmds; const char **pazlogs; }; static struct sjob *qCjobs; static void ucadd_cmd (qsys, qcmd, zlog) const struct uuconf_system *qsys; const struct scmd *qcmd; const char *zlog; { struct sjob *qjob; if (! qsys->uuconf_fcall_transfer && ! qsys->uuconf_fcalled_transfer) ulog (LOG_FATAL, "Not permitted to transfer files to or from %s", qsys->uuconf_zname); for (qjob = qCjobs; qjob != NULL; qjob = qjob->qnext) if (strcmp (qjob->qsys->uuconf_zname, qsys->uuconf_zname) == 0) break; if (qjob == NULL) { qjob = (struct sjob *) xmalloc (sizeof (struct sjob)); qjob->qnext = qCjobs; qjob->qsys = qsys; qjob->ccmds = 0; qjob->pascmds = NULL; qjob->pazlogs = NULL; qCjobs = qjob; } qjob->pascmds = ((struct scmd *) xrealloc ((pointer) qjob->pascmds, (qjob->ccmds + 1) * sizeof (struct scmd))); qjob->pascmds[qjob->ccmds] = *qcmd; qjob->pazlogs = ((const char **) xrealloc ((pointer) qjob->pazlogs, (qjob->ccmds + 1) * sizeof (const char *))); qjob->pazlogs[qjob->ccmds] = zlog; ++qjob->ccmds; } static void ucspool_cmds (fjobid) boolean fjobid; { struct sjob *qjob; char *zjobid; for (qjob = qCjobs; qjob != NULL; qjob = qjob->qnext) { ulog_system (qjob->qsys->uuconf_zname); zjobid = zsysdep_spool_commands (qjob->qsys, bCgrade, qjob->ccmds, qjob->pascmds, (boolean *) NULL); if (zjobid != NULL) { int i; struct scmd *qcmd; const char **pz; for (i = 0, qcmd = qjob->pascmds, pz = qjob->pazlogs; i < qjob->ccmds; i++, qcmd++, pz++) { if (*pz != NULL) { if (**pz != '\0') ulog (LOG_NORMAL, "%s", *pz); } else if (qcmd->bcmd == 'S') ulog (LOG_NORMAL, "Queuing send of %s to %s", qcmd->zfrom, qcmd->zto); else if (qcmd->bcmd == 'R') ulog (LOG_NORMAL, "Queuing request of %s to %s", qcmd->zfrom, qcmd->zto); else { const char *zto; zto = strrchr (qcmd->zto, '!'); if (zto != NULL) ++zto; else zto = qcmd->zto; ulog (LOG_NORMAL, "Queuing request of %s to %s", qcmd->zfrom, zto); } } if (fjobid) printf ("%s\n", zjobid); ubuffree (zjobid); } } } /* Return the system name for which we have created commands, or NULL if we've created commands for more than one system. Set *pfany to FALSE if we didn't create work for any system. */ static const char * zcone_system (pfany) boolean *pfany; { if (qCjobs == NULL) { *pfany = FALSE; return NULL; } *pfany = TRUE; if (qCjobs->qnext == NULL) return qCjobs->qsys->uuconf_zname; else return NULL; } /* Keep track of all files we have created so that we can delete them if we get a signal. The argument will be on the heap. */ static int cCfiles; static const char **pCaz; static void ucrecord_file (zfile) const char *zfile; { pCaz = (const char **) xrealloc ((pointer) pCaz, (cCfiles + 1) * sizeof (const char *)); pCaz[cCfiles] = zfile; ++cCfiles; } /* Delete all the files we have recorded and exit. */ static void ucabort () { int i; for (i = 0; i < cCfiles; i++) (void) remove (pCaz[i]); ulog_close (); usysdep_exit (FALSE); } uucp-1.07/uudir.c0000664000076400007640000000620707665321757007433 /* uudir.c Create a directory owned by uucp. This is Unix specific. Copyright (C) 1992, 1993 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char uudir_rcsid[] = "$Id: uudir.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif #include "sysdep.h" #include /* External functions. */ #if GETPWNAM_DECLARATION_OK #ifndef getpwnam extern struct passwd *getpwnam (); #endif #endif /* This is a simple program which sets its real uid to uucp and then invokes /bin/mkdir. It is only used if the system does not support the mkdir system call. It must be installed suid to root. This program is needed because the UUCP programs will be run suid to uucp. On a system without the mkdir system call, /bin/mkdir is a suid root program. This means that /bin/mkdir always creates directories using the real uid, rather than the effective uid. This is wrong, since the UUCP programs always want to create directories that are owned by uucp. Therefore, this simple suid root program is used to force /bin/mkdir into making a directory owned by uucp. If we made the program publically executable, this would mean that anybody could create a directory owned by uucp. This is probably not a good thing, but since the program must be owned by root we can't simply make it executable only by uucp. Therefore, the Makefile hides the program away in /usr/lib/uucp/util, and makes that directory searchable only by uucp. This should prevent anybody else from getting to the program. This is not a perfect solution, since any suid root program is by definition a potential security hole. I really can't see any way to avoid this, though. */ int main (argc, argv) int argc; char **argv; { struct passwd *q; const char *zprog, *zname; /* We don't print any error messages, since this program should never be run directly by a user. */ if (argc != 2) exit (EXIT_FAILURE); /* OWNER is passed in from the Makefile. It will normally be "uucp". */ q = getpwnam (OWNER); if (q == NULL) exit (EXIT_FAILURE); if (setuid (q->pw_uid) < 0) exit (EXIT_FAILURE); zprog = MKDIR_PROGRAM; zname = strrchr (zprog, '/'); if (zname == NULL) zname = zprog; else ++zname; (void) execl (zprog, zname, argv[1], (char *) NULL); exit (EXIT_FAILURE); } uucp-1.07/uulog.c0000664000076400007640000002750607665321757007443 /* uulog.c Display the UUCP log file. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char uulog_rcsid[] = "$Id: uulog.c,v 1.29 2002/03/05 19:10:42 ian Rel $"; #endif #include #include #include "getopt.h" #include "uudefs.h" #include "uuconf.h" #include "system.h" /* This is a pretty bad implementation of uulog, which I don't think is a very useful program anyhow. It only takes a single -s and/or -u switch. When using HAVE_HDB_LOGGING it requires a system. */ /* Local functions. */ static void ulusage P((void)); static void ulhelp P((void)); /* Long getopt options. */ static const struct option asLlongopts[] = { { "debuglog", no_argument, NULL, 'D' }, { "follow", optional_argument, NULL, 2 }, { "lines", required_argument, NULL, 'n' }, { "system", required_argument, NULL, 's' }, { "statslog", no_argument, NULL, 'S' }, { "user", required_argument, NULL, 'u' }, { "uuxqtlog", no_argument, NULL, 'x' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'X' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; int main (argc, argv) int argc; char **argv; { /* -D: display Debug file */ boolean fdebug = FALSE; /* -f: keep displaying lines forever. */ boolean fforever = FALSE; /* -n lines: number of lines to display. */ int cshow = 0; /* -s: system name. */ const char *zsystem = NULL; /* -S: display Stats file */ boolean fstats = FALSE; /* -u: user name. */ const char *zuser = NULL; /* -I: configuration file name. */ const char *zconfig = NULL; /* -x: display uuxqt log file. */ boolean fuuxqt = FALSE; int i; int iopt; pointer puuconf; int iuuconf; const char *zlogfile; const char *zstatsfile; const char *zdebugfile; const char *zfile; FILE *e; char **pzshow = NULL; int ishow = 0; size_t csystem = 0; size_t cuser = 0; char *zline; size_t cline; zProgram = argv[0]; /* Look for a straight number argument, and convert it to -n before passing the arguments to getopt. */ for (i = 0; i < argc; i++) { if (argv[i][0] == '-' && isdigit (argv[i][1])) { size_t clen; char *znew; clen = strlen (argv[i]); znew = zbufalc (clen + 2); znew[0] = '-'; znew[1] = 'n'; memcpy (znew + 2, argv[i] + 1, clen); argv[i] = znew; } } while ((iopt = getopt_long (argc, argv, "Df:FI:n:s:Su:vxX:", asLlongopts, (int *) NULL)) != EOF) { switch (iopt) { case 'D': /* Show debugging file. */ fdebug = TRUE; break; case 'f': /* Keep displaying lines forever for a particular system. */ fforever = TRUE; zsystem = optarg; if (cshow == 0) cshow = 10; break; case 'F': /* Keep displaying lines forever. */ fforever = TRUE; if (cshow == 0) cshow = 10; break; case 'I': /* Configuration file name. */ if (fsysdep_other_config (optarg)) zconfig = optarg; break; case 'n': /* Number of lines to display. */ cshow = (int) strtol (optarg, (char **) NULL, 10); break; case 's': /* System name. */ zsystem = optarg; break; case 'S': /* Show statistics file. */ fstats = TRUE; break; case 'u': /* User name. */ zuser = optarg; break; case 'x': /* Display uuxqt log file. */ fuuxqt = TRUE; break; case 'X': #if DEBUG > 1 /* Set debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'v': /* Print version and exit. */ printf ("uulog (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 2: /* --follow. */ fforever = TRUE; if (cshow == 0) cshow = 10; if (optarg != NULL) zsystem = optarg; break; case 1: /* --help. */ ulhelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found and flag set. */ break; default: ulusage (); /*NOTREACHED*/ } } if (optind != argc || (fstats && fdebug)) ulusage (); iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); #if DEBUG > 1 { const char *zdebug; iuuconf = uuconf_debuglevel (puuconf, &zdebug); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (zdebug != NULL) iDebug |= idebug_parse (zdebug); } #endif iuuconf = uuconf_logfile (puuconf, &zlogfile); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); iuuconf = uuconf_statsfile (puuconf, &zstatsfile); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); iuuconf = uuconf_debugfile (puuconf, &zdebugfile); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); usysdep_initialize (puuconf, INIT_NOCHDIR); if (zsystem != NULL) { #if HAVE_HDB_LOGGING if (strcmp (zsystem, "ANY") != 0) #endif { struct uuconf_system ssys; /* Canonicalize the system name. If we can't find the system information, just use whatever we were given so that people can check on systems that logged in anonymously. */ iuuconf = uuconf_system_info (puuconf, zsystem, &ssys); if (iuuconf == UUCONF_SUCCESS) { zsystem = zbufcpy (ssys.uuconf_zname); (void) uuconf_system_free (puuconf, &ssys); } } } if (fstats) zfile = zstatsfile; else if (fdebug) zfile = zdebugfile; else { #if ! HAVE_HDB_LOGGING zfile = zlogfile; #else const char *zprogram; char *zalc; /* We need a system to find a HDB log file. */ if (zsystem == NULL) ulog (LOG_FATAL, "system name (-s argument) required for HDB format log files"); if (fuuxqt) zprogram = "uuxqt"; else zprogram = "uucico"; zalc = zbufalc (strlen (zlogfile) + strlen (zprogram) + strlen (zsystem) + 1); sprintf (zalc, zlogfile, zprogram, zsystem); zfile = zalc; if (! fsysdep_file_exists (zfile)) ulog (LOG_FATAL, "no log file available for system %s", zsystem); if (strcmp (zsystem, "ANY") == 0) zsystem = NULL; #endif } e = fopen (zfile, "r"); if (e == NULL) { ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno)); usysdep_exit (FALSE); } if (cshow > 0) { pzshow = (char **) xmalloc (cshow * sizeof (char *)); for (ishow = 0; ishow < cshow; ishow++) pzshow[ishow] = NULL; ishow = 0; } /* Read the log file and output the appropriate lines. */ if (zsystem != NULL) csystem = strlen (zsystem); if (zuser != NULL) cuser = strlen (zuser); zline = NULL; cline = 0; while (TRUE) { while (getline (&zline, &cline, e) > 0) { char *zluser, *zlsys, *znext; size_t cluser, clsys; /* Skip any leading whitespace (not that there should be any). */ znext = zline + strspn (zline, " \t"); if (! fstats) { #if ! HAVE_TAYLOR_LOGGING /* The user name is the first field on the line. */ zluser = znext; cluser = strcspn (znext, " \t"); #endif /* Skip the first field. */ znext += strcspn (znext, " \t"); znext += strspn (znext, " \t"); /* The system is the second field on the line. */ zlsys = znext; clsys = strcspn (znext, " \t"); /* Skip the second field. */ znext += clsys; znext += strspn (znext, " \t"); #if HAVE_TAYLOR_LOGGING /* The user is the third field on the line. */ zluser = znext; cluser = strcspn (znext, " \t"); #endif } else { #if ! HAVE_HDB_LOGGING /* The user name is the first field on the line, and the system name is the second. */ zluser = znext; cluser = strcspn (znext, " \t"); znext += cluser; znext += strspn (znext, " \t"); zlsys = znext; clsys = strcspn (znext, " \t"); #else /* The first field is system!user. */ zlsys = znext; clsys = strcspn (znext, "!"); znext += clsys + 1; zluser = znext; cluser = strcspn (znext, " \t"); #endif } /* See if we should print this line. */ if (zsystem != NULL && (csystem != clsys || strncmp (zsystem, zlsys, clsys) != 0)) continue; if (zuser != NULL && (cuser != cluser || strncmp (zuser, zluser, cluser) != 0)) continue; /* Output the line, or save it if we are outputting only a particular number of lines. */ if (cshow <= 0) printf ("%s", zline); else { ubuffree ((pointer) pzshow[ishow]); pzshow[ishow] = zbufcpy (zline); ishow = (ishow + 1) % cshow; } } /* Output the number of lines requested by the -n option. */ if (cshow > 0) { for (i = 0; i < cshow; i++) { if (pzshow[ishow] != NULL) printf ("%s", pzshow[ishow]); ishow = (ishow + 1) % cshow; } } /* If -f was not specified, or an error occurred while reading the file, get out. */ if (! fforever || ferror (e)) break; clearerr (e); cshow = 0; /* Sleep 1 second before going around the loop again. */ usysdep_sleep (1); } (void) fclose (e); ulog_close (); usysdep_exit (TRUE); /* Avoid errors about not returning a value. */ return 0; } /* Print a usage message and die. */ static void ulusage () { fprintf (stderr, "Usage: %s [-n #] [-sf system] [-u user] [-xDSF] [-I file] [-X debug]\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EXIT_FAILURE); } /* Print a help message. */ static void ulhelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); #if HAVE_HDB_LOGGING printf ("Usage: %s [-n #] [-sf system] [-u user] [-xDS] [-I file] [-X debug]\n", zProgram); #else printf ("Usage: %s [-n #] [-sf system] [-u user] [-DSF] [-I file] [-X debug]\n", zProgram); #endif printf (" -n,--lines: show given number of lines from end of log\n"); printf (" -s,--system: print entries for named system\n"); printf (" -f system,--follow=system: follow entries for named system\n"); printf (" -u,--user user: print entries for named user\n"); #if HAVE_HDB_LOGGING printf (" -x,--uuxqt: print uuxqt log rather than uucico log\n"); #else printf (" -F,--follow: follow entries for any system\n"); #endif printf (" -S,--statslog: show statistics file\n"); printf (" -D,--debuglog: show debugging file\n"); printf (" -X,--debug debug: Set debugging level\n"); #if HAVE_TAYLOR_CONFIG printf (" -I,--config file: Set configuration file to use\n"); #endif /* HAVE_TAYLOR_CONFIG */ printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } uucp-1.07/uuname.c0000664000076400007640000001166007665321757007574 /* uuname.c List the names of known remote UUCP sites. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char uuname_rcsid[] = "$Id: uuname.c,v 1.24 2002/03/05 19:10:42 ian Rel $"; #endif #include "getopt.h" #include "uudefs.h" #include "uuconf.h" #include "system.h" /* Local functions. */ static void unusage P((void)); static void unhelp P((void)); /* Long getopt options. */ static const struct option asNlongopts[] = { { "aliases", no_argument, NULL, 'a' }, { "local", no_argument, NULL, 'l' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; int main (argc, argv) int argc; char **argv; { /* -a: display aliases. */ boolean falias = FALSE; /* -l: if true, output local node name. */ boolean flocal = FALSE; /* -I: configuration file name. */ const char *zconfig = NULL; int iopt; pointer puuconf; int iuuconf; zProgram = argv[0]; while ((iopt = getopt_long (argc, argv, "alI:vx:", asNlongopts, (int *) NULL)) != EOF) { switch (iopt) { case 'a': /* Display aliases. */ falias = TRUE; break; case 'l': /* Output local node name. */ flocal = TRUE; break; case 'I': /* Configuration file name. */ if (fsysdep_other_config (optarg)) zconfig = optarg; break; case 'x': #if DEBUG > 1 /* Set debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'v': /* Print version and exit. */ printf ("uuname (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 1: /* --help. */ unhelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found and flag set. */ break; default: unusage (); /*NOTREACHED*/ } } if (optind != argc) unusage (); iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); #if DEBUG > 1 { const char *zdebug; iuuconf = uuconf_debuglevel (puuconf, &zdebug); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (zdebug != NULL) iDebug |= idebug_parse (zdebug); } #endif usysdep_initialize (puuconf, INIT_SUID | INIT_NOCHDIR); if (flocal) { const char *zlocalname; iuuconf = uuconf_localname (puuconf, &zlocalname); if (iuuconf == UUCONF_NOT_FOUND) { zlocalname = zsysdep_localname (); if (zlocalname == NULL) usysdep_exit (FALSE); } else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); printf ("%s\n", zlocalname); } else { char **pznames, **pz; iuuconf = uuconf_system_names (puuconf, &pznames, falias); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); for (pz = pznames; *pz != NULL; pz++) printf ("%s\n", *pz); } usysdep_exit (TRUE); /* Avoid warnings about not returning a value. */ return 0; } /* Print a usage message and die. */ static void unusage () { fprintf (stderr, "Usage: %s [-a] [-l] [-I file]\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EXIT_FAILURE); } /* Print a help message. */ static void unhelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); printf ("Usage: %s [-a] [-l] [-I file]\n", zProgram); printf (" -a,--aliases: display aliases\n"); printf (" -l,--local: print local name\n"); #if HAVE_TAYLOR_CONFIG printf (" -I,--config file: Set configuration file to use\n"); #endif /* HAVE_TAYLOR_CONFIG */ printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } uucp-1.07/uupick.c0000664000076400007640000002022607665321757007600 /* uupick.c Get files stored in the public directory by uucp -t. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char uupick_rcsid[] = "$Id: uupick.c,v 1.19 2002/03/05 19:10:42 ian Rel $"; #endif #include #include "getopt.h" #include "uudefs.h" #include "uuconf.h" #include "system.h" /* Local functions. */ static void upmovedir P((const char *zfull, const char *zrelative, pointer pinfo)); static void upmove P((const char *zfrom, const char *zto)); /* Long getopt options. */ static const struct option asPlongopts[] = { { "system", required_argument, NULL, 's' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; /* Local functions. */ static void upusage P((void)); static void uphelp P((void)); int main (argc, argv) int argc; char **argv; { /* -s: system name. */ const char *zsystem = NULL; /* -I: configuration file name. */ const char *zconfig = NULL; int iopt; pointer puuconf; int iuuconf; const char *zpubdir; char *zfile, *zfrom, *zfull; char *zallsys; char ab[1000]; boolean fquit; zProgram = "uupick"; while ((iopt = getopt_long (argc, argv, "I:s:vx:", asPlongopts, (int *) NULL)) != EOF) { switch (iopt) { case 's': /* System name to get files from. */ zsystem = optarg; break; case 'I': /* Name configuration file. */ if (fsysdep_other_config (optarg)) zconfig = optarg; break; case 'x': #if DEBUG > 1 /* Set debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'v': /* Print version and exit. */ printf ("uupick (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 1: /* --help. */ uphelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found and flag set. */ break; default: upusage (); /*NOTREACHED*/ } } if (argc != optind) upusage (); iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); usysdep_initialize (puuconf, INIT_GETCWD | INIT_NOCHDIR); zpubdir = NULL; if (zsystem != NULL) { struct uuconf_system ssys; /* Get the public directory for the system. If we can't find the system information, just use the standard public directory, since uupick is not setuid. */ iuuconf = uuconf_system_info (puuconf, zsystem, &ssys); if (iuuconf == UUCONF_SUCCESS) { zpubdir = zbufcpy (ssys.uuconf_zpubdir); (void) uuconf_system_free (puuconf, &ssys); } } if (zpubdir == NULL) { iuuconf = uuconf_pubdir (puuconf, &zpubdir); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); } if (! fsysdep_uupick_init (zsystem, zpubdir)) usysdep_exit (FALSE); zallsys = NULL; fquit = FALSE; while (! fquit && ((zfile = zsysdep_uupick (zsystem, zpubdir, &zfrom, &zfull)) != NULL)) { boolean fdir; char *zto, *zlocal; FILE *e; boolean fcontinue; fdir = fsysdep_directory (zfull); do { boolean fbadname; fcontinue = FALSE; if (zallsys == NULL || strcmp (zallsys, zfrom) != 0) { if (zallsys != NULL) { ubuffree (zallsys); zallsys = NULL; } printf ("from %s: %s %s ?\n", zfrom, fdir ? "dir" : "file", zfile); if (fgets (ab, sizeof ab, stdin) == NULL) break; } if (ab[0] == 'q') { fquit = TRUE; break; } switch (ab[0]) { case '\n': break; case 'd': if (fdir) (void) fsysdep_rmdir (zfull); else { if (remove (zfull) != 0) ulog (LOG_ERROR, "remove (%s): %s", zfull, strerror(errno)); } break; case 'm': case 'a': zto = ab + 1 + strspn (ab + 1, " \t"); zto[strcspn (zto, " \t\n")] = '\0'; zlocal = zsysdep_uupick_local_file (zto, &fbadname); if (zlocal == NULL) { if (! fbadname) usysdep_exit (FALSE); ulog (LOG_ERROR, "%s: bad local file name", zto); fcontinue = TRUE; break; } zto = zsysdep_in_dir (zlocal, zfile); ubuffree (zlocal); if (zto == NULL) usysdep_exit (FALSE); if (! fdir) upmove (zfull, zto); else { usysdep_walk_tree (zfull, upmovedir, (pointer) zto); (void) fsysdep_rmdir (zfull); } ubuffree (zto); if (ab[0] == 'a') { zallsys = zbufcpy (zfrom); ab[0] = 'm'; } break; case 'p': if (fdir) ulog (LOG_ERROR, "Can't print directory"); else { e = fopen (zfull, "r"); if (e == NULL) ulog (LOG_ERROR, "fopen (%s): %s", zfull, strerror (errno)); else { while (fgets (ab, sizeof ab, e) != NULL) (void) fputs (ab, stdout); (void) fclose (e); } } fcontinue = TRUE; break; case '!': (void) system (ab + 1); fcontinue = TRUE; break; default: printf ("uupick commands:\n"); printf ("q: quit\n"); printf (": skip file\n"); printf ("m [dir]: move file to directory\n"); printf ("a [dir]: move all files from this system to directory\n"); printf ("p: list file to stdout\n"); printf ("d: delete file\n"); printf ("! command: shell escape\n"); fcontinue = TRUE; break; } } while (fcontinue); ubuffree (zfull); ubuffree (zfrom); ubuffree (zfile); } (void) fsysdep_uupick_free (zsystem, zpubdir); usysdep_exit (TRUE); /* Avoid error about not returning. */ return 0; } /* Print usage message and die. */ static void upusage () { fprintf (stderr, "Usage: %s [-s system] [-I config] [-x debug]\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EXIT_FAILURE); } /* Print help message. */ static void uphelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); printf (" -s,--system system: Only consider files from named system\n"); printf (" -x,--debug debug: Set debugging level\n"); #if HAVE_TAYLOR_CONFIG printf (" -I,--config file: Set configuration file to use\n"); #endif /* HAVE_TAYLOR_CONFIG */ printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } /* This routine is called by usysdep_walk_tree when moving the contents of an entire directory. */ static void upmovedir (zfull, zrelative, pinfo) const char *zfull; const char *zrelative; pointer pinfo; { const char *ztodir = (const char *) pinfo; char *zto; zto = zsysdep_in_dir (ztodir, zrelative); if (zto == NULL) usysdep_exit (FALSE); upmove (zfull, zto); ubuffree (zto); } /* Move a file. */ static void upmove (zfrom, zto) const char *zfrom; const char *zto; { (void) fsysdep_move_file (zfrom, zto, TRUE, TRUE, FALSE, (const char *) NULL); } uucp-1.07/uustat.c0000664000076400007640000016401207665321757007627 /* uustat.c UUCP status program Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char uustat_rcsid[] = "$Id: uustat.c,v 1.61 2002/03/05 19:10:42 ian Rel $"; #endif #include #include #if TM_IN_SYS_TIME #include #else #include #endif #include "getopt.h" #include "uudefs.h" #include "uuconf.h" #include "system.h" /* The uustat program permits various listings and manipulations of files in the spool directory. This implementation supports the following switches: -a list all jobs -Blines number of lines of standard input to mail -ccommand list only executions of specified command -Ccommand list only jobs other than executions of specified command -e list execute jobs rather than command requests -i ask user whether to kill each listed job -Ifile set configuration file name -kjobid kill job with specified ID -K kill each listed job -m report status for all remote machines -M mail uucp about each job killed with -K -N mail requestor about each job killed with -K -ohour report jobs older than specified number of hours -p do "ps -flp" on all processes holding lock files (Unix specific) -q list number of jobs for all systems -Q don't list jobs, just do -K processing -rjobid rejuvenate job with specified ID -ssystem report on all jobs for specified system -Ssystem report on all jobs other than for specified system -uuser report on all jobs for specified user -Uuser report on all jobs other than for specified user -Wcomment comment to include in mail messages -xdebug set debugging level -yhour report jobs younger than specified number of hours */ /* What to do with a job that matches the selection criteria; these values may be or'red together. */ #define JOB_SHOW (01) #define JOB_INQUIRE (02) #define JOB_KILL (04) #define JOB_REJUVENATE (010) #define JOB_MAIL (020) #define JOB_NOTIFY (040) /* This structure is used to accumulate all the lines in a single command file, so that they can all be displayed at once and so that executions can be displayed reasonably. */ struct scmdlist { struct scmdlist *qnext; struct scmd s; long itime; }; /* Local functions. */ static void ususage P((void)); static void ushelp P((void)); static boolean fsxqt_file_read P((pointer puuconf, FILE *)); static void usxqt_file_free P((void)); static int isxqt_cmd P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int isxqt_file P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int isxqt_user P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static boolean fsworkfiles P((pointer puuconf, int icmd, int csystems, char **pazsystems, boolean fnotsystems, int cusers, char **pazusers, boolean fnotusers, long iold, long iyoung, int ccommands, char **pazcommands, boolean fnotcommands, const char *zcomment, int cstdin)); static boolean fsworkfiles_system P((pointer puuconf,int icmd, const struct uuconf_system *qsys, int cusers, char **pazusers, boolean fnotusers, long iold, long iyoung, int ccommands, char **pazcommands, boolean fnotcommands, const char *zcomment, int cstdin)); static boolean fsworkfile_show P((pointer puuconf, int icmd, const struct uuconf_system *qsys, const struct scmd *qcmd, long itime, int ccommands, char **pazcommands, boolean fnotcommands, const char *zcomment, int cstdin)); static void usworkfile_header P((const struct uuconf_system *qsys, const struct scmd *qcmd, const char *zjobid, long itime, boolean ffirst)); static boolean fsexecutions P((pointer puuconf, int icmd, int csystems, char **pazsystems, boolean fnotsystems, int cusers, char **pazusers, boolean fnotusers, long iold, long iyoung, int ccommands, char **pazcommands, boolean fnotcommands, const char *zcomment, int cstdin)); static boolean fsnotify P((pointer puuconf, int icmd, const char *zcomment, int cstdin, boolean fkilled, const char *zcmd, struct scmdlist *qcmd, const char *zid, long itime, const char *zuser, const struct uuconf_system *qsys, const char *zstdin, pointer pstdinseq, const char *zrequestor)); static boolean fsquery P((pointer puuconf, int csystems, char **pazsystems, boolean fnotsystems, long iold, long iyoung)); static int csunits_show P((long idiff)); static boolean fsmachines P((void)); /* Long getopt options. */ static const struct option asSlongopts[] = { { "all", no_argument, NULL, 'a' }, { "mail-lines", required_argument, NULL, 'B' }, { "command", required_argument, NULL, 'c' }, { "not-command", required_argument, NULL, 'C' }, { "executions", no_argument, NULL, 'e' }, { "prompt", no_argument, NULL, 'i' }, { "kill", required_argument, NULL, 'k' }, { "kill-all", no_argument, NULL, 'K' }, { "status", no_argument, NULL, 'm' }, { "mail", no_argument, NULL, 'M' }, { "notify", no_argument, NULL, 'N' }, { "older-than", required_argument, NULL, 'o' }, { "ps", no_argument, NULL, 'p' }, { "list", no_argument, NULL, 'q' }, { "no-list", no_argument, NULL, 'Q' }, { "rejuvenate", required_argument, NULL, 'r' }, { "rejuvenate-all", no_argument, NULL, 'R' }, { "system", required_argument, NULL, 's' }, { "not-system", required_argument, NULL, 'S' }, { "user", required_argument, NULL, 'u' }, { "not-user", required_argument, NULL, 'U' }, { "comment", required_argument, NULL, 'W' }, { "younger-than", required_argument, NULL, 'y' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; int main (argc, argv) int argc; char **argv; { /* -a: list all jobs. */ boolean fall = FALSE; /* -B lines: number of lines of standard input to mail. */ int cstdin = 100; /* -c,-C command: list only specified command. */ int ccommands = 0; char **pazcommands = NULL; boolean fnotcommands = FALSE; /* -e: list execute jobs. */ boolean fexecute = FALSE; /* -k jobid: kill specified job. */ int ckills = 0; char **pazkills = NULL; /* -m: report machine status. */ boolean fmachine = FALSE; /* -o hour: report jobs older than given number of hours. */ int ioldhours = -1; /* -p: report status of jobs holding lock files. */ boolean fps = FALSE; /* -q: list number of jobs for each system. */ boolean fquery = FALSE; /* -r jobid: rejuvenate specified job. */ int crejuvs = 0; char **pazrejuvs = NULL; /* -s,-S system: list all jobs for specified system. */ int csystems = 0; char **pazsystems = NULL; boolean fnotsystems = FALSE; /* -u,-U user: list all jobs for specified user. */ int cusers = 0; char **pazusers = NULL; boolean fnotusers = FALSE; /* -W comment: comment to include in mail messages. */ const char *zcomment = NULL; /* -y hour: report jobs younger than given number of hours. */ int iyounghours = -1; /* -I file: set configuration file. */ const char *zconfig = NULL; /* -Q, -i, -K, -M, -N: what to do with each job. */ int icmd = JOB_SHOW; int ccmds; int iopt; pointer puuconf; int iuuconf; long iold; long iyoung; const char *azoneuser[1]; boolean fret; zProgram = argv[0]; while ((iopt = getopt_long (argc, argv, "aB:c:C:eiI:k:KmMNo:pqQr:Rs:S:u:U:vW:x:y:", asSlongopts, (int *) NULL)) != EOF) { switch (iopt) { case 'a': /* List all jobs. */ fall = TRUE; break; case 'B': /* Number of lines of standard input to mail. */ cstdin = (int) strtol (optarg, (char **) NULL, 10); break; case 'C': /* List jobs for other than specified command. */ fnotcommands = TRUE; /* Fall through. */ case 'c': /* List specified command. */ ++ccommands; pazcommands = (char **) xrealloc ((pointer) pazcommands, ccommands * sizeof (char *)); pazcommands[ccommands - 1] = optarg; break; case 'e': /* List execute jobs. */ fexecute = TRUE; break; case 'i': /* Prompt the user whether to kill each job. */ icmd |= JOB_INQUIRE; break; case 'I': /* Set configuration file name. */ if (fsysdep_other_config (optarg)) zconfig = optarg; break; case 'k': /* Kill specified job. */ ++ckills; pazkills = (char **) xrealloc ((pointer) pazkills, ckills * sizeof (char *)); pazkills[ckills - 1] = optarg; break; case 'K': /* Kill each listed job. */ icmd |= JOB_KILL; break; case 'm': /* Report machine status. */ fmachine = TRUE; break; case 'M': /* Mail to uucp action taken on each job. */ icmd |= JOB_MAIL; break; case 'N': /* Mail to requestor action taken on each job. */ icmd |= JOB_NOTIFY; break; case 'o': /* Report old jobs. */ ioldhours = (int) strtol (optarg, (char **) NULL, 10); break; case 'p': /* Get status of processes holding locks. */ fps = TRUE; break; case 'q': /* List number of jobs for each system. */ fquery = TRUE; break; case 'Q': /* Don't list jobs, just do -K processing. */ icmd &=~ JOB_SHOW; break; case 'r': /* Rejuvenate specified job. */ ++crejuvs; pazrejuvs = (char **) xrealloc ((pointer) pazrejuvs, crejuvs * sizeof (char *)); pazrejuvs[crejuvs - 1] = optarg; break; case 'R': /* Rejuvenate each listed job. */ icmd |= JOB_REJUVENATE; break; case 'S': /* List jobs for other than specified system. */ fnotsystems = TRUE; /* Fall through. */ case 's': /* List jobs for specified system. */ ++csystems; pazsystems = (char **) xrealloc ((pointer) pazsystems, csystems * sizeof (char *)); pazsystems[csystems - 1] = optarg; break; case 'U': /* List jobs for other than specified user. */ fnotusers = TRUE; /* Fall through. */ case 'u': /* List jobs for specified user. */ ++cusers; pazusers = (char **) xrealloc ((pointer) pazusers, cusers * sizeof (char *)); pazusers[cusers - 1] = optarg; break; case 'W': /* Comment to include in mail messages. */ zcomment = optarg; break; case 'x': #if DEBUG > 1 /* Set debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'y': /* List jobs younger than given number of hours. */ iyounghours = (int) strtol (optarg, (char **) NULL, 10); break; case 'v': /* Print version and exit. */ printf ("uustat (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 1: /* --help. */ ushelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found and flag set. */ break; default: ususage (); /*NOTREACHED*/ } } if (optind != argc) ususage (); /* To avoid confusion, most options are only permitted by themselves. This restriction might be removed later, but it is imposed by most implementations. We do permit any combination of -c, -s, -u, -o and -y, and any combination of -k and -r. */ ccmds = 0; if (fall) ++ccmds; if (ckills > 0 || crejuvs > 0) ++ccmds; if (fmachine) ++ccmds; if (fps) ++ccmds; if (fexecute || fquery || csystems > 0 || cusers > 0 || ioldhours != -1 || iyounghours != -1 || ccommands > 0) ++ccmds; if (fexecute && fquery) ++ccmds; if (ccmds > 1) { fprintf (stderr, "%s: too many options\n", zProgram); ususage (); } if ((icmd & JOB_KILL) != 0 && (icmd & JOB_REJUVENATE) != 0) { fprintf (stderr, "%s: can not both rejuvenate and kill jobs\n", zProgram); ususage (); } iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); #if DEBUG > 1 { const char *zdebug; iuuconf = uuconf_debuglevel (puuconf, &zdebug); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (zdebug != NULL) iDebug |= idebug_parse (zdebug); } #endif usysdep_initialize (puuconf, INIT_SUID); /* If no commands were specified, we list all commands for the given user. */ if (ccmds == 0) { cusers = 1; azoneuser[0] = zsysdep_login_name (); pazusers = (char **) azoneuser; } /* Canonicalize the system names. */ if (csystems > 0) { int i; for (i = 0; i < csystems; i++) { struct uuconf_system ssys; iuuconf = uuconf_system_info (puuconf, pazsystems[i], &ssys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf == UUCONF_NOT_FOUND) ulog (LOG_FATAL, "%s: System not found", pazsystems[i]); else ulog_uuconf (LOG_FATAL, puuconf, iuuconf); } if (strcmp (pazsystems[i], ssys.uuconf_zname) != 0) pazsystems[i] = zbufcpy (ssys.uuconf_zname); (void) uuconf_system_free (puuconf, &ssys); } } if (ioldhours == -1) iold = (long) -1; else { iold = (ixsysdep_time ((long *) NULL) - (long) ioldhours * (long) 60 * (long) 60); if (iold < 0L) iold = 0L; } if (iyounghours == -1) iyoung = (long) -1; else { iyoung = (ixsysdep_time ((long *) NULL) - (long) iyounghours * (long) 60 * (long) 60); if (iyoung < 0L) iyoung = 0L; } if (! fexecute && ! fquery && (fall || csystems > 0 || cusers > 0 || ioldhours != -1 || iyounghours != -1 || ccommands > 0)) fret = fsworkfiles (puuconf, icmd, csystems, pazsystems, fnotsystems, cusers, pazusers, fnotusers, iold, iyoung, ccommands, pazcommands, fnotcommands, zcomment, cstdin); else if (fexecute) fret = fsexecutions (puuconf, icmd, csystems, pazsystems, fnotsystems, cusers, pazusers, fnotusers, iold, iyoung, ccommands, pazcommands, fnotcommands, zcomment, cstdin); else if (icmd != JOB_SHOW) { ulog (LOG_ERROR, "-i, -K, -M, -N, -Q, -R not supported with -k, -m, -p, -q, -r"); ususage (); fret = FALSE; } else if (fquery) { if (cusers > 0 || ccommands > 0) { ulog (LOG_ERROR, "-u, -c not supported with -q"); ususage (); fret = FALSE; } else fret = fsquery (puuconf, csystems, pazsystems, fnotsystems, iold, iyoung); } else if (fmachine) fret = fsmachines (); else if (ckills > 0 || crejuvs > 0) { int i; fret = TRUE; for (i = 0; i < ckills; i++) if (! fsysdep_kill_job (puuconf, pazkills[i])) fret = FALSE; for (i = 0; i < crejuvs; i++) if (! fsysdep_rejuvenate_job (puuconf, pazrejuvs[i])) fret = FALSE; } else if (fps) fret = fsysdep_lock_status (); else { #if DEBUG > 0 ulog (LOG_FATAL, "Can't happen"); #endif fret = FALSE; } ulog_close (); usysdep_exit (fret); /* Avoid errors about not returning a value. */ return 0; } /* Print a usage message and die. */ static void ususage () { fprintf (stderr, "Usage: %s [options]\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EXIT_FAILURE); } /* Print a help message. */ static void ushelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); printf ("Usage: %s [options]\n", zProgram); printf (" -a,--all: list all UUCP jobs\n"); printf (" -B,--mail-lines num: number of lines to return in -M or -N mail message\n"); printf (" -c,--command command: list requests for named command\n"); printf (" -C,--not-command command: list requests for other than named command\n"); printf (" -e,--executions: list queued executions rather than job requests\n"); printf (" -i,--prompt: prompt for whether to kill each listed job\n"); printf (" -k,--kill job: kill specified UUCP job\n"); printf (" -K,--kill-all: kill each listed job\n"); printf (" -m,--status: report status for all remote machines\n"); printf (" -M,--mail: mail report on each listed job to UUCP administrator\n"); printf (" -N,--notify: mail report on each listed job to requestor\n"); printf (" -o,--older-than hours: list all jobs older than given number of hours\n"); printf (" -p,--ps: show status of all processes holding UUCP locks\n"); printf (" -q,--list: list number of jobs for each system\n"); printf (" -Q,--no-list: don't list jobs, just take actions (-i, -K, -M, -N)\n"); printf (" -r,--rejuvenate job: rejuvenate specified UUCP job\n"); printf (" -R,--rejuvenate-all: rejuvenate each listed job\n"); printf (" -s,--system system: list all jobs for specified system\n"); printf (" -S,--not-system system: list all jobs for other than specified system\n"); printf (" -u,--user user: list all jobs for specified user\n"); printf (" -U,--not-user user: list all jobs for other than specified user\n"); printf (" -W,--comment comment: comment to include in mail messages\n"); printf (" -y,--younger-than hours: list all jobs younger than given number of hours\n"); printf (" -x,--debug debug: Set debugging level\n"); #if HAVE_TAYLOR_CONFIG printf (" -I,--config file: Set configuration file to use\n"); #endif /* HAVE_TAYLOR_CONFIG */ printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } /* We need to be able to read information from an execution file. */ /* The user name extracted from an execution file. */ static char *zSxqt_user; /* The system name from an execution file. */ static char *zSxqt_system; /* Address of requesting user (who to send mail to). */ static const char *zSxqt_requestor; /* The command (no arguments) from an execution file. */ static char *zSxqt_prog; /* The full command line from an execution file. */ static char *zSxqt_cmd; /* Number of files associated with an execution file. */ static int cSxqt_files; /* Names of files associated with execution file. */ static char **pazSxqt_files; /* Standard input file name. */ static const char *zSxqt_stdin; /* A command table used to dispatch an execution file. */ static const struct uuconf_cmdtab asSxqt_cmds[] = { { "C", UUCONF_CMDTABTYPE_FN | 0, NULL, isxqt_cmd }, { "I", UUCONF_CMDTABTYPE_STRING, (pointer) &zSxqt_stdin, NULL }, { "F", UUCONF_CMDTABTYPE_FN | 0, NULL, isxqt_file }, { "R", UUCONF_CMDTABTYPE_STRING, (pointer) &zSxqt_requestor, NULL }, { "U", UUCONF_CMDTABTYPE_FN | 3, NULL, isxqt_user }, { NULL, 0, NULL, NULL } }; /* Read an execution file, setting the above variables. */ static boolean fsxqt_file_read (puuconf, e) pointer puuconf; FILE *e; { int iuuconf; boolean fret; zSxqt_user = NULL; zSxqt_system = NULL; zSxqt_stdin = NULL; zSxqt_requestor = NULL; zSxqt_prog = NULL; zSxqt_cmd = NULL; cSxqt_files = 0; pazSxqt_files = NULL; iuuconf = uuconf_cmd_file (puuconf, e, asSxqt_cmds, (pointer) NULL, (uuconf_cmdtabfn) NULL, UUCONF_CMDTABFLAG_CASE, (pointer) NULL); if (iuuconf == UUCONF_SUCCESS) fret = TRUE; else { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fret = FALSE; } if (zSxqt_user == NULL) zSxqt_user = zbufcpy ("*unknown*"); if (zSxqt_system == NULL) zSxqt_system = zbufcpy ("*unknown*"); if (zSxqt_prog == NULL) { zSxqt_prog = zbufcpy ("*none*"); zSxqt_cmd = zbufcpy ("*none*"); } return fret; } /* Free up the information read from an execution file. */ static void usxqt_file_free () { int i; ubuffree (zSxqt_user); zSxqt_user = NULL; ubuffree (zSxqt_system); zSxqt_system = NULL; ubuffree (zSxqt_prog); zSxqt_prog = NULL; ubuffree (zSxqt_cmd); zSxqt_cmd = NULL; for (i = 0; i < cSxqt_files; i++) ubuffree (pazSxqt_files[i]); cSxqt_files = 0; xfree ((pointer) pazSxqt_files); pazSxqt_files = NULL; zSxqt_stdin = NULL; zSxqt_requestor = NULL; } /* Get the command from an execution file. */ /*ARGSUSED*/ static int isxqt_cmd (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { size_t clen; int i; if (argc <= 1) return UUCONF_CMDTABRET_CONTINUE; zSxqt_prog = zbufcpy (argv[1]); clen = 0; for (i = 1; i < argc; i++) clen += strlen (argv[i]) + 1; zSxqt_cmd = zbufalc (clen); zSxqt_cmd[0] = '\0'; for (i = 1; i < argc - 1; i++) { strcat (zSxqt_cmd, argv[i]); strcat (zSxqt_cmd, " "); } strcat (zSxqt_cmd, argv[i]); return UUCONF_CMDTABRET_CONTINUE; } /* Get the associated files from an execution file. */ /*ARGSUSED*/ static int isxqt_file (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { if (argc != 2 && argc != 3) return UUCONF_CMDTABRET_CONTINUE; /* If this file is not in the spool directory, just ignore it. */ if (! fspool_file (argv[1])) return UUCONF_CMDTABRET_CONTINUE; ++cSxqt_files; pazSxqt_files = (char **) xrealloc ((pointer) pazSxqt_files, cSxqt_files * sizeof (char *)); pazSxqt_files[cSxqt_files - 1] = zbufcpy (argv[1]); return UUCONF_CMDTABRET_CONTINUE; } /* Get the requesting user and system from an execution file. */ /*ARGSUSED*/ static int isxqt_user (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { zSxqt_user = zbufcpy (argv[1]); zSxqt_system = zbufcpy (argv[2]); return UUCONF_CMDTABRET_CONTINUE; } /* Handle various possible requests to look at work files. */ static boolean fsworkfiles (puuconf, icmd, csystems, pazsystems, fnotsystems, cusers, pazusers, fnotusers, iold, iyoung, ccommands, pazcommands, fnotcommands, zcomment, cstdin) pointer puuconf; int icmd; int csystems; char **pazsystems; boolean fnotsystems; int cusers; char **pazusers; boolean fnotusers; long iold; long iyoung; int ccommands; char **pazcommands; boolean fnotcommands; const char *zcomment; int cstdin; { boolean fret; int i; int iuuconf; struct uuconf_system ssys; fret = TRUE; if (csystems > 0 && ! fnotsystems) { for (i = 0; i < csystems; i++) { iuuconf = uuconf_system_info (puuconf, pazsystems[i], &ssys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf == UUCONF_NOT_FOUND) ulog (LOG_ERROR, "%s: System not found", pazsystems[i]); else ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fret = FALSE; continue; } if (! fsworkfiles_system (puuconf, icmd, &ssys, cusers, pazusers, fnotusers, iold, iyoung, ccommands, pazcommands, fnotcommands, zcomment, cstdin)) fret = FALSE; (void) uuconf_system_free (puuconf, &ssys); } } else { char **pznames, **pz; iuuconf = uuconf_system_names (puuconf, &pznames, 0); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } for (pz = pznames; *pz != NULL; pz++) { if (csystems > 0) { for (i = 0; i < csystems; i++) if (strcmp (*pz, pazsystems[i]) == 0) break; if (i < csystems) continue; } iuuconf = uuconf_system_info (puuconf, *pz, &ssys); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fret = FALSE; continue; } if (! fsworkfiles_system (puuconf, icmd, &ssys, cusers, pazusers, fnotusers, iold, iyoung, ccommands, pazcommands, fnotcommands, zcomment, cstdin)) fret = FALSE; (void) uuconf_system_free (puuconf, &ssys); xfree ((pointer) *pz); } xfree ((pointer) pznames); } return fret; } /* Look at the work files for a particular system. */ static boolean fsworkfiles_system (puuconf, icmd, qsys, cusers, pazusers, fnotusers, iold, iyoung, ccommands, pazcommands, fnotcommands, zcomment, cstdin) pointer puuconf; int icmd; const struct uuconf_system *qsys; int cusers; char **pazusers; boolean fnotusers; long iold; long iyoung; int ccommands; char **pazcommands; boolean fnotcommands; const char *zcomment; int cstdin; { boolean fret; if (! fsysdep_get_work_init (qsys, UUCONF_GRADE_LOW, 0)) return FALSE; while (TRUE) { struct scmd s; long itime; if (! fsysdep_get_work (qsys, UUCONF_GRADE_LOW, 0, &s)) { usysdep_get_work_free (qsys); return FALSE; } if (s.bcmd == 'H') break; if (cusers > 0) { boolean fmatch; int i; fmatch = fnotusers; for (i = 0; i < cusers; i++) { if (s.zuser != NULL && strcmp (pazusers[i], s.zuser) == 0) { fmatch = ! fmatch; break; } } if (! fmatch) continue; } itime = ixsysdep_work_time (qsys, s.pseq); if (iold != (long) -1 && itime > iold) continue; if (iyoung != (long) -1 && itime < iyoung) continue; if (! fsworkfile_show (puuconf, icmd, qsys, &s, itime, ccommands, pazcommands, fnotcommands, zcomment, cstdin)) { usysdep_get_work_free (qsys); return FALSE; } } fret = fsworkfile_show (puuconf, icmd, qsys, (const struct scmd *) NULL, 0L, ccommands, pazcommands, fnotcommands, zcomment, cstdin); usysdep_get_work_free (qsys); return fret; } /* Show a single workfile. This is actually called once for each line in the workfile, so we accumulate the lines and show them all at once. This lets us show an execution in a useful fashion. */ static boolean fsworkfile_show (puuconf, icmd, qsys, qcmd, itime, ccommands, pazcommands, fnotcommands, zcomment, cstdin) pointer puuconf; int icmd; const struct uuconf_system *qsys; const struct scmd *qcmd; long itime; int ccommands; char **pazcommands; boolean fnotcommands; const char *zcomment; int cstdin; { static struct scmdlist *qlist; static char *zlistid; char *zid; if (qcmd == NULL) zid = NULL; else { zid = zsysdep_jobid (qsys, qcmd->pseq); if (zid == NULL) return FALSE; } /* If this is the same jobid as the list, put it on the end. */ if (qcmd != NULL && qlist != NULL && strcmp (zlistid, zid) == 0) { struct scmdlist *qnew, **pq; ubuffree (zid); qnew = (struct scmdlist *) xmalloc (sizeof (struct scmdlist)); qnew->qnext = NULL; qnew->s = *qcmd; qnew->itime = itime; for (pq = &qlist; *pq != NULL; pq = &(*pq)->qnext) ; *pq = qnew; return TRUE; } /* Here we have found a different job ID, so we print the scmd structures that we have accumulated. We look for the special case of an execution (an E command, or one of the destination files begins with X.). We could be more clever about other situations as well. */ if (qlist != NULL) { boolean fmatch; const char *zprog, *zcmd, *zrequestor, *zstdin; char *zfree; struct scmdlist *qxqt; FILE *exqt = NULL; struct scmdlist *qfree; fmatch = FALSE; zprog = zcmd = zrequestor = zstdin = NULL; zfree = NULL; for (qxqt = qlist; qxqt != NULL; qxqt = qxqt->qnext) { if (qxqt->s.bcmd == 'E') break; if (qxqt->s.bcmd == 'S' && qxqt->s.zto[0] == 'X' && qxqt->s.zto[1] == '.' && fspool_file (qxqt->s.zfrom)) { char *zxqt; /* Open the file now, so that, if it does not exist, we can still report sensibly (the qxqt == NULL case) on any other files that may exist. */ zxqt = zsysdep_spool_file_name (qsys, qxqt->s.zfrom, qxqt->s.pseq); if (zxqt == NULL) return FALSE; exqt = fopen (zxqt, "r"); ubuffree (zxqt); if (exqt != NULL) break; } } if (qxqt == NULL) { if (ccommands == 0 || (fnotcommands && strcmp (pazcommands[0], "ALL") == 0)) { /* Show all the lines in a regular work file. */ fmatch = TRUE; if ((icmd & JOB_SHOW) != 0) { struct scmdlist *qshow; for (qshow = qlist; qshow != NULL; qshow = qshow->qnext) { char *zfile; long cbytes; usworkfile_header (qsys, &qshow->s, zlistid, qshow->itime, qshow == qlist); switch (qshow->s.bcmd) { case 'S': if (strchr (qshow->s.zoptions, 'C') != NULL || fspool_file (qshow->s.zfrom)) zfile = zsysdep_spool_file_name (qsys, qshow->s.ztemp, qshow->s.pseq); else zfile = zbufcpy (qshow->s.zfrom); if (zfile == NULL) cbytes = -1; else cbytes = csysdep_size (zfile); if (cbytes >= 0) printf ("Sending %s (%ld bytes) to %s", qshow->s.zfrom, cbytes, qshow->s.zto); ubuffree (zfile); break; case 'R': printf ("Requesting %s to %s", qshow->s.zfrom, qshow->s.zto); break; case 'X': printf ("Requesting %s to %s", qshow->s.zfrom, qshow->s.zto); break; case 'P': printf ("(poll file)"); break; #if DEBUG > 0 default: printf ("Bad line %d", qshow->s.bcmd); break; #endif } printf ("\n"); } } } } else { long csize; struct scmdlist *qsize; /* Show the command for an execution file. */ if (qxqt->s.bcmd == 'E') { zfree = zbufcpy (qxqt->s.zcmd); zfree[strcspn (zfree, " \t")] = '\0'; zprog = zfree; zcmd = qxqt->s.zcmd; if (strchr (qxqt->s.zoptions, 'R') != NULL) zrequestor = qxqt->s.znotify; } else { if (! fsxqt_file_read (puuconf, exqt)) { (void) fclose (exqt); return FALSE; } (void) fclose (exqt); zprog = zSxqt_prog; zcmd = zSxqt_cmd; zrequestor = zSxqt_requestor; } csize = 0L; for (qsize = qlist; qsize != NULL; qsize = qsize->qnext) { if (qsize->s.bcmd == 'S' || qsize->s.bcmd == 'E') { char *zfile; if (strchr (qsize->s.zoptions, 'C') != NULL || fspool_file (qsize->s.zfrom)) zfile = zsysdep_spool_file_name (qsys, qsize->s.ztemp, qsize->s.pseq); else zfile = zbufcpy (qsize->s.zfrom); if (zfile != NULL) { long cbytes; cbytes = csysdep_size (zfile); if (cbytes > 0) csize += cbytes; ubuffree (zfile); } } } if (ccommands == 0) fmatch = TRUE; else { int i; fmatch = fnotcommands; for (i = 0; i < ccommands; i++) { if (strcmp (pazcommands[i], "ALL") == 0 || strcmp (pazcommands[i], zprog) == 0) { fmatch = ! fmatch; break; } } } /* To get the name of the standard input file on this system we have to look through the list of file transfers to find the right one on the remote system. */ if (fmatch) { struct scmdlist *qstdin; if (qxqt->s.bcmd == 'E') qstdin = qxqt; else if (zSxqt_stdin != NULL) { for (qstdin = qlist; qstdin != NULL; qstdin = qstdin->qnext) if (qstdin->s.bcmd == 'S' && strcmp (qstdin->s.zto, zSxqt_stdin) == 0) break; } else qstdin = NULL; if (qstdin != NULL) { if (strchr (qstdin->s.zoptions, 'C') != NULL || fspool_file (qstdin->s.zfrom)) zstdin = qstdin->s.ztemp; else zstdin = qstdin->s.zfrom; } } if (fmatch && (icmd & JOB_SHOW) != 0) { usworkfile_header (qsys, &qxqt->s, zlistid, qxqt->itime, TRUE); printf ("Executing %s (sending %ld bytes)\n", zcmd, csize); } } if (fmatch) { boolean fkill_or_rejuv; fkill_or_rejuv = FALSE; if ((icmd & JOB_INQUIRE) != 0) { int b; /* Ask stdin whether this job should be killed. */ fprintf (stderr, "%s: %s %s? ", zProgram, (icmd & JOB_REJUVENATE) != 0 ? "Rejuvenate" : "Kill", zlistid); (void) fflush (stderr); b = getchar (); fkill_or_rejuv = b == 'y' || b == 'Y'; while (b != EOF && b != '\n') b = getchar (); } else if ((icmd & JOB_KILL) != 0 || (icmd & JOB_REJUVENATE) != 0) fkill_or_rejuv = TRUE; if (fkill_or_rejuv && (qlist->s.zuser == NULL || strcmp (zsysdep_login_name (), qlist->s.zuser) != 0) && ! fsysdep_privileged ()) ulog (LOG_ERROR, "%s: Not submitted by you", zlistid); else { if ((icmd & (JOB_MAIL | JOB_NOTIFY)) != 0) { if (! fsnotify (puuconf, icmd, zcomment, cstdin, (fkill_or_rejuv && (icmd & JOB_REJUVENATE) == 0), zcmd, qlist, zlistid, qlist->itime, qlist->s.zuser, qsys, zstdin, qlist->s.pseq, zrequestor)) return FALSE; } if (fkill_or_rejuv) { if ((icmd & JOB_REJUVENATE) == 0) { if (! fsysdep_kill_job (puuconf, zlistid)) return FALSE; } else { if (! fsysdep_rejuvenate_job (puuconf, zlistid)) return FALSE; } } } } if (qxqt != NULL) { if (qxqt->s.bcmd == 'E') ubuffree (zfree); else usxqt_file_free (); } /* Free up the list of entries. */ qfree = qlist; while (qfree != NULL) { struct scmdlist *qnext; qnext = qfree->qnext; xfree ((pointer) qfree); qfree = qnext; } ubuffree (zlistid); qlist = NULL; zlistid = NULL; } /* Start a new list with the entry we just got. */ if (qcmd != NULL) { qlist = (struct scmdlist *) xmalloc (sizeof (struct scmdlist)); qlist->qnext = NULL; qlist->s = *qcmd; qlist->itime = itime; zlistid = zid; } return TRUE; } /* Show the header of the line describing a workfile. */ static void usworkfile_header (qsys, qcmd, zjobid, itime, ffirst) const struct uuconf_system *qsys; const struct scmd *qcmd; const char *zjobid; long itime; boolean ffirst; { const char *zshowid; struct tm stime; if (ffirst) zshowid = zjobid; else zshowid = "-"; printf ("%s %s %s ", zshowid, qsys->uuconf_zname, qcmd->zuser != NULL ? qcmd->zuser : OWNER); usysdep_localtime (itime, &stime); printf ("%02d-%02d %02d:%02d ", stime.tm_mon + 1, stime.tm_mday, stime.tm_hour, stime.tm_min); } /* List queued executions that have not been processed by uuxqt for one reason or another. */ static boolean fsexecutions (puuconf, icmd, csystems, pazsystems, fnotsystems, cusers, pazusers, fnotusers, iold, iyoung, ccommands, pazcommands, fnotcommands, zcomment, cstdin) pointer puuconf; int icmd; int csystems; char **pazsystems; boolean fnotsystems; int cusers; char **pazusers; boolean fnotusers; long iold; long iyoung; int ccommands; char **pazcommands; boolean fnotcommands; const char *zcomment; int cstdin; { const char *zlocalname; int iuuconf; char *zfile; char *zsystem; boolean ferr; iuuconf = uuconf_localname (puuconf, &zlocalname); if (iuuconf == UUCONF_NOT_FOUND) { zlocalname = zsysdep_localname (); if (zlocalname == NULL) return FALSE; } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } if (! fsysdep_get_xqt_init ((const char *) NULL)) return FALSE; while ((zfile = zsysdep_get_xqt ((const char *) NULL, &zsystem, &ferr)) != NULL) { boolean fmatch; int i; long itime; FILE *e; if (csystems > 0) { fmatch = fnotsystems; for (i = 0; i < csystems; i++) { if (strcmp (pazsystems[i], zsystem) == 0) { fmatch = ! fmatch; break; } } if (! fmatch) { ubuffree (zfile); ubuffree (zsystem); continue; } } itime = ixsysdep_file_time (zfile); if ((iold != (long) -1 && itime > iold) || (iyoung != (long) -1 && itime < iyoung)) { ubuffree (zfile); ubuffree (zsystem); continue; } /* We need to read the execution file before we can check the user name. */ e = fopen (zfile, "r"); if (e == NULL) { /* Probably uucico just deleted the file. */ continue; } if (! fsxqt_file_read (puuconf, e)) { (void) fclose (e); ubuffree (zfile); ubuffree (zsystem); continue; } (void) fclose (e); if (cusers == 0) fmatch = TRUE; else { fmatch = fnotusers; for (i = 0; i < cusers; i++) { if (strcmp (zSxqt_user, pazusers[i]) == 0 || (zSxqt_requestor != NULL && strcmp (zSxqt_requestor, pazusers[i]) == 0)) { fmatch = ! fmatch; break; } } } if (fmatch && ccommands > 0) { fmatch = fnotcommands; for (i = 0; i < ccommands; i++) { if (strcmp (pazcommands[i], "ALL") == 0 || strcmp (pazcommands[i], zSxqt_prog) == 0) { fmatch = ! fmatch; break; } } } if (fmatch) { boolean fbad, fkill_or_rejuv; struct uuconf_system ssys; fbad = FALSE; if ((icmd & JOB_SHOW) != 0) { struct tm stime; printf ("%s %s!", zsystem, zSxqt_system); if (zSxqt_requestor != NULL) printf ("%s", zSxqt_requestor); else printf ("%s", zSxqt_user); usysdep_localtime (itime, &stime); printf (" %02d-%02d %02d:%02d ", stime.tm_mon + 1, stime.tm_mday, stime.tm_hour, stime.tm_min); printf ("%s\n", zSxqt_cmd); } fkill_or_rejuv = FALSE; if ((icmd & JOB_INQUIRE) != 0) { int b; /* Ask stdin whether this job should be killed. */ fprintf (stderr, "%s: %s %s? ", zProgram, (icmd & JOB_REJUVENATE) != 0 ? "Rejuvenate" : "Kill", zSxqt_cmd); (void) fflush (stderr); b = getchar (); fkill_or_rejuv = b == 'y' || b == 'Y'; while (b != EOF && b != '\n') b = getchar (); } else if ((icmd & JOB_KILL) != 0 || (icmd & JOB_REJUVENATE) != 0) fkill_or_rejuv = TRUE; if (fkill_or_rejuv) { if ((strcmp (zSxqt_user, zsysdep_login_name ()) != 0 || strcmp (zsystem, zlocalname) != 0) && ! fsysdep_privileged ()) { ulog (LOG_ERROR, "Job not submitted by you\n"); fbad = TRUE; } } if (! fbad) { iuuconf = uuconf_system_info (puuconf, zsystem, &ssys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fbad = TRUE; } else if (strcmp (zsystem, zlocalname) == 0) { iuuconf = uuconf_system_local (puuconf, &ssys); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fbad = TRUE; } ssys.uuconf_zname = (char *) zlocalname; } else if (! funknown_system (puuconf, zsystem, &ssys)) { ulog (LOG_ERROR, "Job for unknown system %s", zsystem); fbad = TRUE; } } } if (! fbad && (icmd & (JOB_MAIL | JOB_NOTIFY)) != 0) { if (! fsnotify (puuconf, icmd, zcomment, cstdin, fkill_or_rejuv && (icmd & JOB_REJUVENATE) == 0, zSxqt_cmd, (struct scmdlist *) NULL, (const char *) NULL, itime, zSxqt_user, &ssys, zSxqt_stdin, (pointer) NULL, zSxqt_requestor)) { ferr = TRUE; usxqt_file_free (); ubuffree (zfile); ubuffree (zsystem); break; } } if (! fbad && fkill_or_rejuv) { for (i = 0; i < cSxqt_files; i++) { char *z; z = zsysdep_spool_file_name (&ssys, pazSxqt_files[i], (pointer) NULL); if (z != NULL) { if ((icmd & JOB_REJUVENATE) != 0) (void) fsysdep_touch_file (z); else (void) remove (z); ubuffree (z); } } if ((icmd & JOB_REJUVENATE) != 0) (void) fsysdep_touch_file (zfile); else { if (remove (zfile) != 0) ulog (LOG_ERROR, "remove (%s): %s", zfile, strerror (errno)); } } if (! fbad) (void) uuconf_system_free (puuconf, &ssys); } usxqt_file_free (); ubuffree (zfile); ubuffree (zsystem); } usysdep_get_xqt_free ((const char *) NULL); return ferr; } /* When a job is killed, send mail to the appropriate people. */ static boolean fsnotify (puuconf, icmd, zcomment, cstdin, fkilled, zcmd, qcmd, zid, itime, zuser, qsys, zstdin, pstdinseq, zrequestor) pointer puuconf; int icmd; const char *zcomment; int cstdin; boolean fkilled; const char *zcmd; struct scmdlist *qcmd; const char *zid; long itime; const char *zuser; const struct uuconf_system *qsys; const char *zstdin; pointer pstdinseq; const char *zrequestor; { const char **pz; int cgot; int i, istdin; struct tm stime; char ab[sizeof "1991-12-31 12:00:00"]; const char *zsubject; boolean fret; pz = (const char **) xmalloc (20 * sizeof (const char *)); cgot = 20; i = 0; if (zid == NULL) pz[i++] = "A UUCP execution request"; else { pz[i++] = "UUCP job\n\t"; pz[i++] = zid; pz[i++] = "\nfor system\n\t"; pz[i++] = qsys->uuconf_zname; } pz[i++] = "\nrequested by\n\t"; pz[i++] = zuser != NULL ? zuser : OWNER; if (zid == NULL) { pz[i++] = "\non system\n\t"; pz[i++] = qsys->uuconf_zname; } pz[i++] = "\n"; if (fkilled) pz[i++] = "has been killed.\n"; if (zcomment != NULL) { pz[i++] = zcomment; pz[i++] = "\n"; } pz[i++] = "The job was queued at "; usysdep_localtime (itime, &stime); sprintf (ab, "%04d-%02d-%02d %02d:%02d:%02d", stime.tm_year + 1900, stime.tm_mon + 1, stime.tm_mday, stime.tm_hour, stime.tm_min, stime.tm_sec); pz[i++] = ab; pz[i++] = ".\nIt "; if (fkilled) pz[i++] = "was\n"; else pz[i++] = "is\n"; if (zcmd != NULL) { pz[i++] = "\t"; pz[i++] = zcmd; } else { struct scmdlist *qshow; for (qshow = qcmd; qshow != NULL; qshow = qshow->qnext) { if (i + 10 > cgot) { cgot += 20; pz = (const char **) xrealloc ((pointer) pz, cgot * sizeof (const char *)); } switch (qshow->s.bcmd) { case 'S': pz[i++] = "\tsend "; break; default: case 'R': case 'X': pz[i++] = "\trequest "; break; case 'P': pz[i++] = "\tpoll "; break; #if DEBUG > 0 case 'E': ulog (LOG_FATAL, "fsnotify: Can't happen"); break; #endif } if (qshow->s.zfrom != NULL && qshow->s.zto != NULL) { pz[i++] = qshow->s.zfrom; pz[i++] = " to "; pz[i++] = qshow->s.zto; } } } istdin = i; if (cstdin > 0 && zstdin != NULL) { boolean fspool; char *zfile; FILE *e; fspool = fspool_file (zstdin); if (fspool) zfile = zsysdep_spool_file_name (qsys, zstdin, pstdinseq); else zfile = zsysdep_local_file (zstdin, qsys->uuconf_zpubdir, (boolean *) NULL); if (zfile != NULL && (fspool || fin_directory_list (zfile, qsys->uuconf_pzremote_send, qsys->uuconf_zpubdir, TRUE, TRUE, (const char *) NULL))) { e = fopen (zfile, "r"); if (e != NULL) { int clines, clen; char *zline; size_t cline; pz[i++] = "\n"; istdin = i; clines = 0; zline = NULL; cline = 0; while ((clen = getline (&zline, &cline, e)) > 0) { if (memchr (zline, '\0', (size_t) clen) != NULL) { int ifree; /* A null character means this is probably a binary file. */ for (ifree = istdin; ifree < i; ifree++) ubuffree ((char *) pz[ifree]); i = istdin - 1; break; } ++clines; if (clines > cstdin) break; if (i >= cgot) { cgot += 20; pz = (const char **) xrealloc ((pointer) pz, (cgot * sizeof (char *))); } if (strncmp (zline, "From ", sizeof "From " - 1) != 0) pz[i++] = zbufcpy (zline); else { char *zalc; /* Escape "From " at the start of a line. This should really be the responsibility of the mail transfer agent. On some systems, though, the mail transfer agent does not do it, but user mail programs expect it. We help them out here, since it doesn't matter much--we're already truncating the message anyhow. */ zalc = zbufalc (strlen (zline) + 2); zalc[0] = '>'; strcpy (zalc + 1, zline); pz[i++] = zalc; } } xfree ((pointer) zline); (void) fclose (e); } } ubuffree (zfile); } if (fkilled) zsubject = "UUCP job killed"; else zsubject = "UUCP notification"; fret = TRUE; if ((icmd & JOB_MAIL) != 0) { if (! fsysdep_mail (OWNER, zsubject, i, pz)) fret = FALSE; } if ((icmd & JOB_NOTIFY) != 0 && (zrequestor != NULL || zuser != NULL)) { const char *zmail; char *zfree; if (zrequestor != NULL) zmail = zrequestor; else zmail = zuser; zfree = NULL; if (zid == NULL) { int iuuconf; const char *zloc; /* This is an execution request, which may be from another system. If it is, we must prepend that system name to the user name extracted from the X. file. */ iuuconf = uuconf_localname (puuconf, &zloc); if (iuuconf == UUCONF_NOT_FOUND) { zloc = zsysdep_localname (); if (zloc == NULL) return FALSE; } else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (strcmp (qsys->uuconf_zname, zloc) != 0 #if HAVE_INTERNET_MAIL && strchr (zmail, '@') == NULL #endif ) { zfree = zbufalc (strlen (qsys->uuconf_zname) + strlen (zmail) + sizeof "!"); sprintf (zfree, "%s!%s", qsys->uuconf_zname, zmail); zmail = zfree; } } if (! fsysdep_mail (zmail, zsubject, i, pz)) fret = FALSE; ubuffree (zfree); } while (istdin < i) { ubuffree ((char *) pz[istdin]); istdin++; } xfree ((pointer) pz); return fret; } /* Handle the -q option. For each remote system this lists the number of jobs queued, the number of executions queued, and the current call status. We get the executions all at once, because they are not accessed by system. They could be, but it is possible to have executions pending for an unknown system, so special handling would still be required. */ struct sxqtlist { struct sxqtlist *qnext; char *zsystem; int cxqts; long ifirst; }; /* These local functions need the definition of sxqtlist for the prototype. */ static boolean fsquery_system P((const struct uuconf_system *qsys, struct sxqtlist **pq, long inow, const char *zlocalname, int csystems, char **pazsystems, boolean fnotsystems, long iold, long iyoung)); static boolean fsquery_show P((const struct uuconf_system *qsys, int cwork, long ifirstwork, struct sxqtlist *qxqt, long inow, const char *zlocalname, int csystems, char **pazsystems, boolean fnotsystems, long iold, long iyoung)); static boolean fsquery (puuconf, csystems, pazsystems, fnotsystems, iold, iyoung) pointer puuconf; int csystems; char **pazsystems; boolean fnotsystems; long iold; long iyoung; { int iuuconf; const char *zlocalname; struct sxqtlist *qlist; char *zfile, *zsystem; boolean ferr; long inow; char **pznames, **pz; boolean fret; iuuconf = uuconf_localname (puuconf, &zlocalname); if (iuuconf == UUCONF_NOT_FOUND) { zlocalname = zsysdep_localname (); if (zlocalname == NULL) return FALSE; } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } /* Get a count of all the execution files. */ if (! fsysdep_get_xqt_init ((const char *) NULL)) return FALSE; qlist = NULL; while ((zfile = zsysdep_get_xqt ((const char *) NULL, &zsystem, &ferr)) != NULL) { struct sxqtlist *qlook; for (qlook = qlist; qlook != NULL; qlook = qlook->qnext) if (strcmp (zsystem, qlook->zsystem) == 0) break; if (qlook != NULL) { long itime; ubuffree (zsystem); ++qlook->cxqts; itime = ixsysdep_file_time (zfile); if (itime < qlook->ifirst) qlook->ifirst = itime; } else { struct sxqtlist *qnew; qnew = (struct sxqtlist *) xmalloc (sizeof (struct sxqtlist)); qnew->qnext = qlist; qnew->zsystem = zsystem; qnew->cxqts = 1; qnew->ifirst = ixsysdep_file_time (zfile); qlist = qnew; } ubuffree (zfile); } usysdep_get_xqt_free ((const char *) NULL); if (ferr) return FALSE; inow = ixsysdep_time ((long *) NULL); /* Show the information for each system. */ iuuconf = uuconf_system_names (puuconf, &pznames, 0); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); return FALSE; } fret = TRUE; for (pz = pznames; *pz != NULL; pz++) { struct uuconf_system ssys; iuuconf = uuconf_system_info (puuconf, *pz, &ssys); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fret = FALSE; continue; } if (! fsquery_system (&ssys, &qlist, inow, zlocalname, csystems, pazsystems, fnotsystems, iold, iyoung)) fret = FALSE; (void) uuconf_system_free (puuconf, &ssys); xfree ((pointer) *pz); } /* Check for the local system in the list of execution files. */ if (qlist != NULL) { struct sxqtlist **pq; for (pq = &qlist; *pq != NULL; pq = &(*pq)->qnext) { if (strcmp ((*pq)->zsystem, zlocalname) == 0) { struct uuconf_system ssys; struct sxqtlist *qfree; iuuconf = uuconf_system_info (puuconf, zlocalname, &ssys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fret = FALSE; break; } iuuconf = uuconf_system_local (puuconf, &ssys); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fret = FALSE; break; } ssys.uuconf_zname = (char *) zlocalname; } if (! fsquery_show (&ssys, 0, 0L, *pq, inow, zlocalname, csystems, pazsystems, fnotsystems, iold, iyoung)) fret = FALSE; (void) uuconf_system_free (puuconf, &ssys); qfree = *pq; *pq = qfree->qnext; ubuffree (qfree->zsystem); xfree ((pointer) qfree); break; } } } /* Print out information for any unknown systems for which we have execution files. */ while (qlist != NULL) { struct uuconf_system ssys; struct sxqtlist *qnext; if (! funknown_system (puuconf, qlist->zsystem, &ssys)) { ulog (LOG_ERROR, "Executions queued up for unknown systems"); fret = FALSE; break; } if (! fsquery_show (&ssys, 0, 0L, qlist, inow, zlocalname, csystems, pazsystems, fnotsystems, iold, iyoung)) fret = FALSE; (void) uuconf_system_free (puuconf, &ssys); qnext = qlist->qnext; ubuffree (qlist->zsystem); xfree ((pointer) qlist); qlist = qnext; } return fret; } /* Query a single known system. */ static boolean fsquery_system (qsys, pq, inow, zlocalname, csystems, pazsystems, fnotsystems, iold, iyoung) const struct uuconf_system *qsys; struct sxqtlist **pq; long inow; const char *zlocalname; int csystems; char **pazsystems; boolean fnotsystems; long iold; long iyoung; { int cwork; long ifirstwork; char *zid; boolean fret; if (! fsysdep_get_work_init (qsys, UUCONF_GRADE_LOW, 0)) return FALSE; cwork = 0; ifirstwork = 0L; zid = NULL; while (TRUE) { struct scmd s; long itime; char *zthisid; if (! fsysdep_get_work (qsys, UUCONF_GRADE_LOW, 0, &s)) return FALSE; if (s.bcmd == 'H') break; zthisid = zsysdep_jobid (qsys, s.pseq); if (zid != NULL && strcmp (zid, zthisid) == 0) ubuffree (zthisid); else { ++cwork; ubuffree (zid); zid = zthisid; } itime = ixsysdep_work_time (qsys, s.pseq); if (ifirstwork == 0L || ifirstwork > itime) ifirstwork = itime; } usysdep_get_work_free (qsys); ubuffree (zid); /* Find the execution information, if any. */ while (*pq != NULL) { if (strcmp ((*pq)->zsystem, qsys->uuconf_zname) == 0) break; pq = &(*pq)->qnext; } /* If there are no commands and no executions, don't print any information for this system. */ if (cwork == 0 && *pq == NULL) return TRUE; fret = fsquery_show (qsys, cwork, ifirstwork, *pq, inow, zlocalname, csystems, pazsystems, fnotsystems, iold, iyoung); if (*pq != NULL) { struct sxqtlist *qfree; qfree = *pq; *pq = qfree->qnext; ubuffree (qfree->zsystem); xfree ((pointer) qfree); } return fret; } /* Print out the query information for a single system. We handle the local system specially. */ static boolean fsquery_show (qsys, cwork, ifirstwork, qxqt, inow, zlocalname, csystems, pazsystems, fnotsystems, iold, iyoung) const struct uuconf_system *qsys; int cwork; long ifirstwork; struct sxqtlist *qxqt; long inow; const char *zlocalname; int csystems; char **pazsystems; boolean fnotsystems; long iold; long iyoung; { boolean flocal; struct sstatus sstat; boolean fnostatus; struct tm stime; int cpad; /* Make sure this is one of the systems we are printing. */ if (csystems > 0) { boolean fmatch; int i; fmatch = fnotsystems; for (i = 0; i < csystems; i++) { if (strcmp (pazsystems[i], qsys->uuconf_zname) == 0) { fmatch = ! fmatch; break; } } if (! fmatch) return TRUE; } /* Make sure the commands are within the time bounds. */ if ((iold != (long) -1 && (cwork == 0 || ifirstwork > iold) && (qxqt == NULL || qxqt->ifirst > iold)) || (iyoung != (long) -1 && (cwork == 0 || ifirstwork < iyoung) && (qxqt == NULL || qxqt->ifirst < iyoung))) return TRUE; flocal = strcmp (qsys->uuconf_zname, zlocalname) == 0; if (! flocal) { if (! fsysdep_get_status (qsys, &sstat, &fnostatus)) return FALSE; } printf ("%-10s %3dC (", qsys->uuconf_zname, cwork); if (cwork == 0) { printf ("0 secs"); cpad = 3; } else cpad = csunits_show (inow - ifirstwork); printf (") "); while (cpad-- != 0) printf (" "); if (qxqt == NULL) printf (" 0X (0 secs) "); else { printf ("%3dX (", qxqt->cxqts); cpad = csunits_show (inow - qxqt->ifirst); printf (")"); while (cpad-- != 0) printf (" "); } if (flocal || fnostatus) { printf ("\n"); if (! flocal) ubuffree (sstat.zstring); return TRUE; } usysdep_localtime (sstat.ilast, &stime); printf (" %02d-%02d %02d:%02d ", stime.tm_mon + 1,stime.tm_mday, stime.tm_hour, stime.tm_min); if (sstat.zstring == NULL) printf ("%s\n", azStatus[(int) sstat.ttype]); else { printf ("%s\n", sstat.zstring); ubuffree (sstat.zstring); } return TRUE; } /* Print a time difference in the largest applicable units. */ static int csunits_show (idiff) long idiff; { const char *zunit; long iunits; int cpad; if (idiff > (long) 24 * (long) 60 * (long) 60) { iunits = idiff / ((long) 24 * (long) 60 * (long) 60); zunit = "day"; cpad = 4; } else if (idiff > (long) 60 * 60) { iunits = idiff / (long) (60 * 60); zunit = "hour"; cpad = 3; } else if (idiff > (long) 60) { iunits = idiff / (long) 60; zunit = "min"; cpad = 4; } else { iunits = idiff; zunit = "sec"; cpad = 4; } printf ("%ld %s%s", iunits, zunit, iunits == 1 ? "" : "s"); if (iunits != 1) --cpad; if (iunits > 99) --cpad; if (iunits > 9) --cpad; return cpad; } /* Give a list of all status entries for all machines that we have status entries for. We need to get a list of status entries in a system dependent fashion, since we may have status for unknown systems. */ static boolean fsmachines () { pointer phold; char *zsystem; boolean ferr; struct sstatus sstat; if (! fsysdep_all_status_init (&phold)) return FALSE; while ((zsystem = zsysdep_all_status (phold, &ferr, &sstat)) != NULL) { struct tm stime; usysdep_localtime (sstat.ilast, &stime); printf ("%-14s %02d-%02d %02d:%02d ", zsystem, stime.tm_mon + 1, stime.tm_mday, stime.tm_hour, stime.tm_min); if (sstat.zstring == NULL) printf ("%s", azStatus[(int) sstat.ttype]); else { printf ("%s", sstat.zstring); ubuffree (sstat.zstring); } ubuffree (zsystem); if (sstat.ttype != STATUS_TALKING && sstat.cwait > 0) { printf (" (%d %s", sstat.cretries, sstat.cretries == 1 ? "try" : "tries"); if (sstat.ilast + sstat.cwait > ixsysdep_time ((long *) NULL)) { usysdep_localtime (sstat.ilast + sstat.cwait, &stime); printf (", next after %02d-%02d %02d:%02d", stime.tm_mon + 1, stime.tm_mday, stime.tm_hour, stime.tm_min); } printf (")"); } printf ("\n"); } usysdep_all_status_free (phold); return ! ferr; } uucp-1.07/uux.c0000664000076400007640000012563107665321760007121 /* uux.c Prepare to execute a command on a remote system. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char uux_rcsid[] = "$Id: uux.c,v 1.89 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "sysdep.h" #include "getopt.h" #include #include #if HAVE_SYSEXITS_H #include #endif #ifndef EX_OK #define EX_OK (0) #endif #ifndef EX_USAGE #define EX_USAGE (64) #endif #ifndef EX_DATAERR #define EX_DATAERR (65) #endif #ifndef EX_NOINPUT #define EX_NOINPUT (66) #endif #ifndef EX_UNAVAILABLE #define EX_UNAVAILABLE (69) #endif #ifndef EX_OSERR #define EX_OSERR (71) #endif #ifndef EX_CANTCREAT #define EX_CANTCREAT (73) #endif #ifndef EX_TEMPFAIL #define EX_TEMPFAIL (75) #endif #ifndef EX_CONFIG #define EX_CONFIG (78) #endif /* These character lists should, perhaps, be in sysdep.h. */ /* This is the list of shell metacharacters that we check for. If one of these is present, we request uuxqt to execute the command with /bin/sh. Otherwise we let it execute using execve. */ #define ZSHELLCHARS "\"'`*?[;&()|<>\\$" /* This is the list of word separators. We break filename arguments at these characters. */ #define ZSHELLSEPS ";&*|<> \t" /* This is the list of word separators without the redirection operators. */ #define ZSHELLNONREDIRSEPS ";&*| \t" /* Whether we need to backslash quote the entries in the execution file. */ static boolean fXquote; /* Whether we have output the 'Q' command required for quoting. */ static boolean fXquote_output; /* Whether this execution is occurring on the local system. */ static boolean fXxqtlocal; /* The execution system. */ static struct uuconf_system sXxqtsys; /* The name of local system from the point of view of the execution system. */ static const char *zXxqtloc; /* The job grade to use. */ static char bXgrade = BDEFAULT_UUX_GRADE; /* The temporary file name of the execute file. */ static char abXxqt_tname[CFILE_NAME_LEN]; /* The name of the execute file on the remote system. */ static char abXxqt_xname[CFILE_NAME_LEN]; /* The execute file we are creating. */ static FILE *eXxqt_file; /* A list of commands to be spooled. */ static struct scmd *pasXcmds; static int cXcmds; /* A file to close if we're forced to exit. */ static FILE *eXclose; /* A list of file names which will match the file names which appear in the uucico logs. */ static char *zXnames; /* Local functions. */ static void uxusage P((void)); static void uxhelp P((void)); static void uxadd_xqt_line P((int bchar, const char *z1, const char *z2)); static void uxadd_send_file P((const char *zfrom, const char *zto, const char *zoptions, const char *ztemp, const char *zforward)); static void uxcopy_stdin P((FILE *e)); static void uxrecord_file P((const char *zfile)); static void uxfatal P((void)); static void uxabort P((int istat)); static void uxadd_name P((const char *)); /* Long getopt options. */ static const struct option asXlongopts[] = { { "requestor", required_argument, NULL, 'a' }, { "return-stdin", no_argument, NULL, 'b' }, { "nocopy", no_argument, NULL, 'c' }, { "copy", no_argument, NULL, 'C' }, { "grade", required_argument, NULL, 'g' }, { "jobid", no_argument, NULL, 'j' }, { "link", no_argument, NULL, 'l' }, { "notification", required_argument, NULL, 2 }, { "stdin", no_argument, NULL, 'p' }, { "nouucico", no_argument, NULL, 'r' }, { "status", required_argument, NULL, 's' }, { "noexpand", no_argument, NULL, 'W' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; /* The main routine. */ int main (argc, argv) int argc; char **argv; { /* -a: requestor address for status reports. */ const char *zrequestor = NULL; /* -b: if true, return standard input on error. */ boolean fretstdin = FALSE; /* -c,-C: if true, copy to spool directory. */ boolean fcopy = FALSE; /* -c: set if -c appears explicitly; if it and -l appear, then if the link fails we don't copy the file. */ boolean fdontcopy = FALSE; /* -I: configuration file name. */ const char *zconfig = NULL; /* -j: output job id. */ boolean fjobid = FALSE; /* -l: link file to spool directory. */ boolean flink = FALSE; /* -n: do not notify upon command completion. */ boolean fno_ack = FALSE; /* -p: read standard input for command standard input. */ boolean fread_stdin = FALSE; /* -r: do not start uucico when finished. */ boolean fuucico = TRUE; /* -s: report status to named file. */ const char *zstatus_file = NULL; /* -W: only expand local file names. */ boolean fexpand = TRUE; /* -z: report status only on error. */ boolean ferror_ack = FALSE; int iopt; pointer puuconf; int iuuconf; const char *zlocalname; int i; size_t clen; char *zargs; char *zarg; char *zcmd; const char *zsys; char *zexclam; boolean fgetcwd; const char *zuser; char *zforward; char **pzargs; int calloc_args; int cargs; const char *zinput_from; const char *zinput_to; const char *zinput_temp; boolean finputcopied; char *zcall_system; boolean fcall_any; struct uuconf_system slocalsys; boolean fneedshell; char *zfullcmd; boolean fpoll; char aboptions[10]; zProgram = argv[0]; ulog_fatal_fn (uxfatal); /* We need to be able to read a single - as an option, which getopt won't do. We handle this by using getopt to scan the argument list multiple times, replacing any single "-" with "-p". */ opterr = 0; while (1) { while (getopt_long (argc, argv, "+a:bcCg:I:jlnprs:Wvx:z", asXlongopts, (int *) NULL) != EOF) ; if (optind >= argc || strcmp (argv[optind], "-") != 0) break; argv[optind] = zbufcpy ("-p"); optind = 0; } opterr = 1; optind = 0; /* The leading + in the getopt string means to stop processing options as soon as a non-option argument is seen. */ while ((iopt = getopt_long (argc, argv, "+a:bcCg:I:jlnprs:Wvx:z", asXlongopts, (int *) NULL)) != EOF) { switch (iopt) { case 'a': /* Set requestor name: mail address to which status reports should be sent. */ zrequestor = optarg; break; case 'b': /* Return standard input on error. */ fretstdin = TRUE; break; case 'c': /* Do not copy local files to spool directory. */ fcopy = FALSE; fdontcopy = TRUE; break; case 'C': /* Copy local files to spool directory. */ fcopy = TRUE; break; case 'I': /* Configuration file name. */ if (fsysdep_other_config (optarg)) zconfig = optarg; break; case 'j': /* Output jobid. */ fjobid = TRUE; break; case 'g': /* Set job grade. */ bXgrade = optarg[0]; break; case 'l': /* Link file to spool directory. */ flink = TRUE; break; case 'n': /* Do not notify upon command completion. */ fno_ack = TRUE; break; case 'p': /* Read standard input for command standard input. */ fread_stdin = TRUE; break; case 'r': /* Do not start uucico when finished. */ fuucico = FALSE; break; case 's': /* Report status to named file. */ zstatus_file = optarg; break; case 'W': /* Only expand local file names. */ fexpand = FALSE; break; case 'x': #if DEBUG > 1 /* Set debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'z': /* Report status only on error. */ ferror_ack = TRUE; break; case 2: /* --notify={true,false,error}. */ if (*optarg == 't' || *optarg == 'T' || *optarg == 'y' || *optarg == 'Y' || *optarg == 'e' || *optarg == 'E') { ferror_ack = TRUE; fno_ack = FALSE; } else if (*optarg == 'f' || *optarg == 'F' || *optarg == 'n' || *optarg == 'N') { ferror_ack = FALSE; fno_ack = TRUE; } break; case 'v': /* Print version and exit. */ printf ("uux (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EX_OK); /*NOTREACHED*/ case 1: /* --help. */ uxhelp (); exit (EX_OK); /*NOTREACHED*/ case 0: /* Long option found and flag set. */ break; default: uxusage (); break; } } if (! UUCONF_GRADE_LEGAL (bXgrade) || ((bXgrade < '0' || bXgrade > '9') && (bXgrade < 'a' || bXgrade > 'z') && (bXgrade < 'A' || bXgrade > 'Z'))) { ulog (LOG_ERROR, "Ignoring illegal grade"); bXgrade = BDEFAULT_UUX_GRADE; } /* Check whether we need backslash quoting in the executable file. We always break up the command arguments at spaces anyhow, so we don't have to worry about them. Note that this means that certain commands aren't supported. */ if ((zrequestor != NULL && zrequestor[strcspn (zrequestor, " \t\n")] != '\0') || (zstatus_file != NULL && zstatus_file[strcspn (zstatus_file, " \t\n")] != '\0')) fXquote = TRUE; if (optind == argc) uxusage (); iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); #if DEBUG > 1 { const char *zdebug; iuuconf = uuconf_debuglevel (puuconf, &zdebug); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (zdebug != NULL) iDebug |= idebug_parse (zdebug); } #endif /* The command and files arguments could be quoted in any number of ways, so we split them apart ourselves. We do this before calling usysdep_initialize because we want to set fgetcwd correctly. */ clen = 1; for (i = optind; i < argc; i++) clen += strlen (argv[i]) + 1; zargs = zbufalc (clen); *zargs = '\0'; for (i = optind; i < argc; i++) { strcat (zargs, argv[i]); strcat (zargs, " "); } /* The first argument is the command to execute. */ clen = strcspn (zargs, ZSHELLSEPS); zcmd = zbufalc (clen + 1); strncpy (zcmd, zargs, clen); zcmd[clen] = '\0'; zargs += clen; /* Split the arguments out into an array. We break the arguments into alternating sequences of characters not in ZSHELLSEPS and characters in ZSHELLSEPS. We remove whitespace. We separate the redirection characters '>' and '<' into their own arguments to make them easier to process below. */ calloc_args = 10; pzargs = (char **) xmalloc (calloc_args * sizeof (char *)); cargs = 0; for (zarg = strtok (zargs, " \t"); zarg != NULL; zarg = strtok ((char *) NULL, " \t")) { while (*zarg != '\0') { if (cargs + 1 >= calloc_args) { calloc_args += 10; pzargs = (char **) xrealloc ((pointer) pzargs, calloc_args * sizeof (char *)); } if (*zarg == '(') clen = strlen (zarg); else clen = strcspn (zarg, ZSHELLSEPS); if (clen > 0) { pzargs[cargs] = zbufalc (clen + 1); memcpy (pzargs[cargs], zarg, clen); pzargs[cargs][clen] = '\0'; ++cargs; zarg += clen; } /* We deliberately separate '>' and '<' out. */ if (*zarg != '\0') { clen = strspn (zarg, ZSHELLNONREDIRSEPS); if (clen == 0) clen = 1; pzargs[cargs] = zbufalc (clen + 1); memcpy (pzargs[cargs], zarg, clen); pzargs[cargs][clen] = '\0'; ++cargs; zarg += clen; } } } /* Now look through the arguments to see if we are going to need the current working directory. We don't try to make a precise determination, just a conservative one. The basic idea is that we don't want to get the cwd for 'foo!rmail - user' (note that we don't examine the command itself). */ fgetcwd = FALSE; for (i = 0; i < cargs; i++) { if (pzargs[i][0] == '(') continue; zexclam = strrchr (pzargs[i], '!'); if (zexclam != NULL && fsysdep_needs_cwd (zexclam + 1)) { fgetcwd = TRUE; break; } if ((pzargs[i][0] == '<' || pzargs[i][0] == '>') && i + 1 < cargs && strchr (pzargs[i + 1], '!') == NULL && fsysdep_needs_cwd (pzargs[i + 1])) { fgetcwd = TRUE; break; } } #ifdef SIGINT usysdep_signal (SIGINT); #endif #ifdef SIGHUP usysdep_signal (SIGHUP); #endif #ifdef SIGQUIT usysdep_signal (SIGQUIT); #endif #ifdef SIGTERM usysdep_signal (SIGTERM); #endif #ifdef SIGPIPE usysdep_signal (SIGPIPE); #endif usysdep_initialize (puuconf, INIT_SUID | (fgetcwd ? INIT_GETCWD : 0)); zuser = zsysdep_login_name (); /* Get the local system name. */ iuuconf = uuconf_localname (puuconf, &zlocalname); if (iuuconf == UUCONF_NOT_FOUND) { zlocalname = zsysdep_localname (); if (zlocalname == NULL) exit (EX_CONFIG); } else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); /* Get the local system information. */ iuuconf = uuconf_system_info (puuconf, zlocalname, &slocalsys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); iuuconf = uuconf_system_local (puuconf, &slocalsys); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); slocalsys.uuconf_zname = (char *) zlocalname; } /* Figure out which system the command is to be executed on. */ zcmd = zremove_local_sys (&slocalsys, zcmd); zexclam = strchr (zcmd, '!'); if (zexclam == NULL) { zsys = zlocalname; fXxqtlocal = TRUE; zforward = NULL; } else { *zexclam = '\0'; zsys = zcmd; zcmd = zexclam + 1; fXxqtlocal = FALSE; /* See if we must forward this command through other systems (e.g. uux a!b!cmd). */ zexclam = strrchr (zcmd, '!'); if (zexclam == NULL) zforward = NULL; else { clen = zexclam - zcmd; zforward = zbufalc (clen + 1); memcpy (zforward, zcmd, clen); zforward[clen] = '\0'; zcmd = zexclam + 1; } } if (fXxqtlocal) sXxqtsys = slocalsys; else { iuuconf = uuconf_system_info (puuconf, zsys, &sXxqtsys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (! funknown_system (puuconf, zsys, &sXxqtsys)) ulog (LOG_FATAL, "%s: System not found", zsys); } } /* Get the local name the remote system know us as. */ zXxqtloc = sXxqtsys.uuconf_zlocalname; if (zXxqtloc == NULL) zXxqtloc = zlocalname; /* Look through the arguments. Any argument containing an exclamation point character is interpreted as a file name, and is sent to the appropriate system. */ zinput_from = NULL; zinput_to = NULL; zinput_temp = NULL; finputcopied = FALSE; zcall_system = NULL; fcall_any = FALSE; for (i = 0; i < cargs; i++) { const char *zsystem; char *zfile; char *zforw; boolean finput, foutput; boolean flocal, fonxqt; /* Check for a parenthesized argument; remove the parentheses and otherwise ignore it (this is how an exclamation point is quoted). */ if (pzargs[i][0] == '(') { clen = strlen (pzargs[i]); if (pzargs[i][clen - 1] != ')') ulog (LOG_ERROR, "Mismatched parentheses"); else pzargs[i][clen - 1] = '\0'; ++pzargs[i]; continue; } /* Check whether we are doing a redirection. */ finput = FALSE; foutput = FALSE; if (i + 1 < cargs) { if (pzargs[i][0] == '<') finput = TRUE; else if (pzargs[i][0] == '>') foutput = TRUE; if (finput || foutput) { pzargs[i] = NULL; i++; } } zexclam = strchr (pzargs[i], '!'); /* If there is no exclamation point and no redirection, this argument is left untouched. */ if (zexclam == NULL && ! finput && ! foutput) continue; if (zexclam != NULL) { pzargs[i] = zremove_local_sys (&slocalsys, pzargs[i]); zexclam = strchr (pzargs[i], '!'); } /* Get the system name and file name for this file. */ if (zexclam == NULL) { zsystem = zlocalname; zfile = pzargs[i]; flocal = TRUE; zforw = NULL; } else { *zexclam = '\0'; zsystem = pzargs[i]; zfile = zexclam + 1; flocal = FALSE; zexclam = strrchr (zfile, '!'); if (zexclam == NULL) zforw = NULL; else { *zexclam = '\0'; zforw = zfile; zfile = zexclam + 1; } } /* Check if the file is already on the execution system. */ if (flocal) fonxqt = fXxqtlocal; else if (fXxqtlocal) fonxqt = FALSE; else if (zforward == NULL ? zforw != NULL : zforw == NULL) fonxqt = FALSE; else if (zforward != NULL && zforw != NULL && strcmp (zforward, zforw) != 0) fonxqt = FALSE; else if (strcmp (zsystem, sXxqtsys.uuconf_zname) == 0) fonxqt = TRUE; else if (sXxqtsys.uuconf_pzalias == NULL) fonxqt = FALSE; else { char **pzal; fonxqt = FALSE; for (pzal = sXxqtsys.uuconf_pzalias; *pzal != NULL; pzal++) { if (strcmp (zsystem, *pzal) == 0) { fonxqt = TRUE; break; } } } /* Turn the file into an absolute path. */ if (flocal) zfile = zsysdep_local_file_cwd (zfile, sXxqtsys.uuconf_zpubdir, (boolean *) NULL); else if (fexpand) zfile = zsysdep_add_cwd (zfile); if (zfile == NULL) uxabort (EX_OSERR); /* Check for output redirection. */ if (foutput) { if (flocal) { if (! fin_directory_list (zfile, sXxqtsys.uuconf_pzremote_receive, sXxqtsys.uuconf_zpubdir, TRUE, FALSE, (const char *) NULL)) ulog (LOG_FATAL, "Not permitted to create %s", zfile); } /* There are various cases of output redirection. uux cmd >out: The command is executed on the local system, and the output file is placed on the local system (fonxqt is TRUE). uux cmd >a!out: The command is executed on the local system, and the output file is sent to a. uux a!cmd >out: The command is executed on a, and the output file is returned to the local system (flocal is TRUE). uux a!cmd >a!out: The command is executed on a, and the output file is left on a (fonxqt is TRUE). uux a!cmd >b!out: The command is executed on a, and the output file is sent to b; traditionally, I believe that b is relative to a, rather than to the local system. However, this essentially contradicts the previous two cases, in which the output file is relative to the local system. Now, the cases that we don't handle. uux cmd >a!b!out: The command is executed on the local system, and the output file is sent to b via a. This requires the local uuxqt to support forwarding of the output file. uux a!b!cmd >out: The command is executed on b, which is reached via a. Probably the output file is intended for the local system, in which case the uuxqt on b must support forwarding of the output file. uux a!b!cmd >c!out: Is c relative to b or to the local system? If it's relative to b this is easy to handle. Otherwise, we must arrange for the file to be sent back to the local system and for the local system to send it on to c. There are many variations of the last case. It's not at all clear to me how they should be handled. */ if (zforward != NULL || zforw != NULL) ulog (LOG_FATAL, "May not forward standard output"); if (fonxqt) uxadd_xqt_line ('O', zfile, (const char *) NULL); else if (flocal) uxadd_xqt_line ('O', zfile, zXxqtloc); else uxadd_xqt_line ('O', zfile, zsystem); pzargs[i] = NULL; continue; } if (finput) { if (fread_stdin) ulog (LOG_FATAL, "Standard input specified twice"); pzargs[i] = NULL; } if (flocal) { char *zuse; char *zdata; char abtname[CFILE_NAME_LEN]; char abdname[CFILE_NAME_LEN]; /* It's a local file. If requested by -C, copy the file to the spool directory. If requested by -l, link the file to the spool directory; if the link fails, we copy the file, unless -c was explictly used. If the execution is occurring on the local system, we force the copy as well, because otherwise we would have to have some way to tell uuxqt not to move the file. If the file is being shipped to another system, we must set up a transfer request. First make sure the user has legitimate access, since we are running setuid. */ if (! fsysdep_access (zfile)) uxabort (EX_NOINPUT); zdata = zsysdep_data_file_name (&sXxqtsys, zXxqtloc, bXgrade, FALSE, abtname, abdname, (char *) NULL); if (zdata == NULL) uxabort (EX_OSERR); if (fcopy || flink || fXxqtlocal) { boolean fdid; uxrecord_file (zdata); fdid = FALSE; if (flink) { boolean fworked; if (! fsysdep_link (zfile, zdata, &fworked)) uxabort (EX_OSERR); if (fworked) fdid = TRUE; else if (fdontcopy) ulog (LOG_FATAL, "%s: Can't link to spool directory", zfile); } if (! fdid) { openfile_t efile; efile = esysdep_user_fopen (zfile, TRUE, TRUE); if (! ffileisopen (efile)) uxabort (EX_NOINPUT); if (! fcopy_open_file (efile, zdata, FALSE, TRUE, TRUE)) uxabort (EX_CANTCREAT); (void) ffileclose (efile); } zuse = abtname; } else { /* We don't actually use the spool file name, but we need a name to use as the destination. */ ubuffree (zdata); /* Make sure the daemon can access the file. */ if (! fsysdep_daemon_access (zfile)) uxabort (EX_NOINPUT); if (! fin_directory_list (zfile, sXxqtsys.uuconf_pzlocal_send, sXxqtsys.uuconf_zpubdir, TRUE, TRUE, zuser)) ulog (LOG_FATAL, "Not permitted to send from %s", zfile); zuse = zfile; } if (fXxqtlocal) { if (finput) uxadd_xqt_line ('I', zuse, (char *) NULL); else pzargs[i] = zuse; } else { finputcopied = fcopy || flink; if (finput) { zinput_from = zuse; zinput_to = zbufcpy (abdname); zinput_temp = zbufcpy (abtname); } else { char *zbase; uxadd_send_file (zuse, abdname, finputcopied ? "C" : "c", abtname, zforward); zbase = zsysdep_base_name (zfile); if (zbase == NULL) uxabort (EX_OSERR); uxadd_xqt_line ('F', abdname, zbase); pzargs[i] = zbase; } } } else if (fonxqt) { /* The file is already on the system where the command is to be executed. */ if (finput) uxadd_xqt_line ('I', zfile, (const char *) NULL); else pzargs[i] = zfile; } else { struct uuconf_system sfromsys; char abtname[CFILE_NAME_LEN]; struct scmd s; char *zjobid; /* We need to request a remote file. */ iuuconf = uuconf_system_info (puuconf, zsystem, &sfromsys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (! funknown_system (puuconf, zsystem, &sfromsys)) ulog (LOG_FATAL, "%s: System not found", zsystem); } if (fonxqt) { /* The file is already on the system where the command is to be executed. */ if (finput) uxadd_xqt_line ('I', zfile, (const char *) NULL); else pzargs[i] = zfile; } else { char *zdata; boolean ftemp; if (! sfromsys.uuconf_fcall_transfer && ! sfromsys.uuconf_fcalled_transfer) ulog (LOG_FATAL, "Not permitted to transfer files to or from %s", sfromsys.uuconf_zname); if (zforw != NULL) { /* This is ``uux cmd a!b!file''. To make this work, we would have to be able to set up a request to a to fetch file from b and send it to us. But it turns out that that will not work, because when a sends us the file we will put it in a's spool directory, not the local system spool directory. So we won't have any way to find it. This is not a conceptual problem, and it could doubtless be solved. Please feel free to solve it and send me the solution. */ ulog (LOG_FATAL, "File forwarding not supported"); } /* We must request the file from the remote system to this one. */ zdata = zsysdep_data_file_name (&slocalsys, zXxqtloc, bXgrade, FALSE, abtname, (char *) NULL, (char *) NULL); if (zdata == NULL) uxabort (EX_OSERR); ubuffree (zdata); /* Request the file. The special option '9' is a signal to uucico that it's OK to receive a file into the spool directory; normally such requests are rejected. This privilege is easy to abuse. */ s.bcmd = 'R'; s.bgrade = bXgrade; s.pseq = NULL; s.zfrom = zfile; s.zto = zbufcpy (abtname); s.zuser = zuser; s.zoptions = "9"; s.ztemp = ""; s.imode = 0600; s.znotify = ""; s.cbytes = -1; s.zcmd = NULL; s.ipos = 0; zjobid = zsysdep_spool_commands (&sfromsys, bXgrade, 1, &s, &ftemp); if (zjobid == NULL) uxabort (ftemp ? EX_TEMPFAIL : EX_DATAERR); if (fjobid) printf ("%s\n", zjobid); ubuffree (zjobid); if (fcall_any) { ubuffree (zcall_system); zcall_system = NULL; } else { fcall_any = TRUE; zcall_system = zbufcpy (sfromsys.uuconf_zname); } if (fXxqtlocal) { /* Tell the command execution to wait until the file has been received, and tell it the real file name. */ if (finput) { uxadd_xqt_line ('F', abtname, (char *) NULL); uxadd_xqt_line ('I', abtname, (char *) NULL); } else { char *zbase; zbase = zsysdep_base_name (zfile); if (zbase == NULL) uxabort (EX_OSERR); uxadd_xqt_line ('F', abtname, zbase); pzargs[i] = zbase; } } else { char abxtname[CFILE_NAME_LEN]; char *zbase; char *zxqt; FILE *e; /* Now we must arrange to forward the file on to the execution system. We need to get a name to give the file on the execution system (abxtname). */ zdata = zsysdep_data_file_name (&sXxqtsys, zXxqtloc, bXgrade, TRUE, abxtname, (char *) NULL, (char *) NULL); if (zdata == NULL) uxabort (EX_OSERR); ubuffree (zdata); zbase = zsysdep_base_name (zfile); if (zbase == NULL) uxabort (EX_OSERR); zxqt = zsysdep_xqt_file_name (); if (zxqt == NULL) uxabort (EX_OSERR); e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE); if (e == NULL) uxabort (EX_OSERR); uxrecord_file (zxqt); fprintf (e, "U %s %s\n", zsysdep_login_name (), zlocalname); fprintf (e, "F %s %s\n", abtname, zbase); fprintf (e, "C uucp -C -W -d -g %c %s %s!", bXgrade, zbase, sXxqtsys.uuconf_zname); if (zforward != NULL) fprintf (e, "%s!", zforward); fprintf (e, "%s\n", abxtname); if (! fstdiosync (e, zxqt)) ulog (LOG_FATAL, "fsync failed"); if (fclose (e) != 0) ulog (LOG_FATAL, "fclose: %s", strerror (errno)); if (finput) { uxadd_xqt_line ('F', abxtname, (char *) NULL); uxadd_xqt_line ('I', abxtname, (char *) NULL); ubuffree (zbase); } else { uxadd_xqt_line ('F', abxtname, zbase); pzargs[i] = zbase; } } } (void) uuconf_system_free (puuconf, &sfromsys); } } /* If standard input is to be read from the stdin of uux, we read it here into a temporary file and send it to the execute system. */ if (fread_stdin) { char *zdata; char abtname[CFILE_NAME_LEN]; char abdname[CFILE_NAME_LEN]; FILE *e; zdata = zsysdep_data_file_name (&sXxqtsys, zXxqtloc, bXgrade, FALSE, abtname, abdname, (char *) NULL); if (zdata == NULL) uxabort (EX_OSERR); e = esysdep_fopen (zdata, FALSE, FALSE, TRUE); if (e == NULL) uxabort (EX_OSERR); eXclose = e; uxrecord_file (zdata); uxcopy_stdin (e); if (! fstdiosync (e, zdata)) ulog (LOG_FATAL, "fsync failed"); eXclose = NULL; if (fclose (e) != 0) ulog (LOG_FATAL, "fclose: %s", strerror (errno)); if (fXxqtlocal) uxadd_xqt_line ('I', abtname, (const char *) NULL); else { zinput_from = zbufcpy (abtname); zinput_to = zbufcpy (abdname); zinput_temp = zinput_from; finputcopied = TRUE; } } /* If we are returning standard input, or we're putting the status in a file, we can't use an E command. */ if (fretstdin) uxadd_xqt_line ('B', (const char *) NULL, (const char *) NULL); if (zstatus_file != NULL) uxadd_xqt_line ('M', zstatus_file, (const char *) NULL); /* Get the complete command line, and decide whether the command needs to be executed by the shell. */ fneedshell = FALSE; if (zcmd[strcspn (zcmd, ZSHELLCHARS)] != '\0') fneedshell = TRUE; clen = strlen (zcmd) + 1; for (i = 0; i < cargs; i++) { if (pzargs[i] != NULL) { clen += strlen (pzargs[i]) + 1; if (pzargs[i][strcspn (pzargs[i], ZSHELLCHARS)] != '\0') fneedshell = TRUE; } } zfullcmd = zbufalc (clen); strcpy (zfullcmd, zcmd); for (i = 0; i < cargs; i++) { if (pzargs[i] != NULL) { strcat (zfullcmd, " "); strcat (zfullcmd, pzargs[i]); } } fpoll = FALSE; /* If we haven't written anything to the execution file yet, and we have a standard input file, and we're not forwarding, then every other option can be handled in an E command. */ if (eXxqt_file == NULL && zinput_from != NULL && zforward == NULL) { struct scmd s; char *zoptions; /* Set up an E command. */ s.bcmd = 'E'; s.bgrade = bXgrade; s.pseq = NULL; s.zuser = zuser; s.zfrom = zinput_from; s.zto = zinput_to; s.zoptions = aboptions; zoptions = aboptions; *zoptions++ = finputcopied ? 'C' : 'c'; if (fno_ack) *zoptions++ = 'N'; if (ferror_ack) *zoptions++ = 'Z'; if (zrequestor != NULL) *zoptions++ = 'R'; if (fneedshell) *zoptions++ = 'e'; *zoptions = '\0'; s.ztemp = zinput_temp; s.imode = 0666; if (zrequestor == NULL) zrequestor = "\"\""; s.znotify = zrequestor; s.cbytes = -1; s.zcmd = zfullcmd; s.ipos = 0; ++cXcmds; pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds, cXcmds * sizeof (struct scmd)); pasXcmds[cXcmds - 1] = s; uxadd_name (zinput_from); } else if (*zfullcmd == '\0' && eXxqt_file == NULL && zinput_from == NULL && cXcmds == 0) { /* As a special case, if we are asked to execute an empty command, we create a poll file instead. */ fpoll = TRUE; } else { /* Finish up the execute file. */ uxadd_xqt_line ('U', zuser, zXxqtloc); if (zinput_from != NULL) { uxadd_xqt_line ('F', zinput_to, (char *) NULL); uxadd_xqt_line ('I', zinput_to, (char *) NULL); uxadd_send_file (zinput_from, zinput_to, finputcopied ? "C" : "c", zinput_temp, zforward); } if (fno_ack) uxadd_xqt_line ('N', (const char *) NULL, (const char *) NULL); if (ferror_ack) uxadd_xqt_line ('Z', (const char *) NULL, (const char *) NULL); if (zrequestor != NULL) uxadd_xqt_line ('R', zrequestor, (const char *) NULL); if (fneedshell) uxadd_xqt_line ('e', (const char *) NULL, (const char *) NULL); uxadd_xqt_line ('C', zfullcmd, (const char *) NULL); if (! fstdiosync (eXxqt_file, "execution file")) ulog (LOG_FATAL, "fsync failed"); if (fclose (eXxqt_file) != 0) ulog (LOG_FATAL, "fclose: %s", strerror (errno)); eXxqt_file = NULL; /* If the execution is to occur on another system, we must now arrange to copy the execute file to this system. */ if (! fXxqtlocal) uxadd_send_file (abXxqt_tname, abXxqt_xname, "C", abXxqt_tname, zforward); } /* If we got a signal, get out before spooling anything. */ if (FGOT_SIGNAL ()) uxabort (EX_OSERR); /* From here on in, it's too late. We don't call uxabort. */ if (cXcmds > 0 || fpoll) { char *zjobid; boolean ftemp; if (! fpoll && ! sXxqtsys.uuconf_fcall_transfer && ! sXxqtsys.uuconf_fcalled_transfer) ulog (LOG_FATAL, "Not permitted to transfer files to or from %s", sXxqtsys.uuconf_zname); zjobid = zsysdep_spool_commands (&sXxqtsys, bXgrade, cXcmds, pasXcmds, &ftemp); if (zjobid == NULL) { ulog_close (); exit (ftemp ? EX_TEMPFAIL : EX_DATAERR); } if (fjobid) printf ("%s\n", zjobid); ubuffree (zjobid); if (fcall_any) { ubuffree (zcall_system); zcall_system = NULL; } else { fcall_any = TRUE; zcall_system = zbufcpy (sXxqtsys.uuconf_zname); } } if (! fpoll) { /* If all that worked, make a log file entry. All log file reports up to this point went to stderr. */ ulog_to_file (puuconf, TRUE); ulog_system (sXxqtsys.uuconf_zname); ulog_user (zuser); if (zXnames == NULL) ulog (LOG_NORMAL, "Queuing %s", zfullcmd); else ulog (LOG_NORMAL, "Queuing %s (%s)", zfullcmd, zXnames); ulog_close (); } if (! fuucico || (zcall_system == NULL && ! fcall_any)) { if (fXxqtlocal && fuucico) { char *zconfigarg; if (zconfig == NULL) zconfigarg = NULL; else { zconfigarg = zbufalc (sizeof "-I" + strlen (zconfig)); sprintf (zconfigarg, "-I%s", zconfig); } (void) fsysdep_run (FALSE, "uuxqt", zconfigarg, (const char *) NULL); } } else { const char *zcicoarg; char *zconfigarg; if (zcall_system == NULL) zcicoarg = "-r1"; else { char *z; z = zbufalc (sizeof "-Cs" + strlen (zcall_system)); sprintf (z, "-Cs%s", zcall_system); zcicoarg = z; } if (zconfig == NULL) zconfigarg = NULL; else { zconfigarg = zbufalc (sizeof "-I" + strlen (zconfig)); sprintf (zconfigarg, "-I%s", zconfig); } (void) fsysdep_run (FALSE, "uucico", zcicoarg, zconfigarg); } exit (EX_OK); /* Avoid error about not returning a value. */ return 0; } /* Report command usage. */ static void uxhelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); printf ("Usage: %s [options] [-] command\n", zProgram); printf (" -,-p,--stdin: Read standard input for standard input of command\n"); printf (" -c,--nocopy: Do not copy local files to spool directory (default)\n"); printf (" -C,--copy: Copy local files to spool directory\n"); printf (" -l,--link: link local files to spool directory\n"); printf (" -g,--grade grade: Set job grade (must be alphabetic)\n"); printf (" -n,--notification=no: Do not report completion status\n"); printf (" -z,--notification=error: Report completion status only on error\n"); printf (" -r,--nouucico: Do not start uucico daemon\n"); printf (" -a,--requestor address: Address to mail status report to\n"); printf (" -b,--return-stdin: Return standard input with status report\n"); printf (" -s,--status file: Report completion status to file\n"); printf (" -j,--jobid: Report job id\n"); printf (" -x,--debug debug: Set debugging level\n"); #if HAVE_TAYLOR_CONFIG printf (" -I,--config file: Set configuration file to use\n"); #endif /* HAVE_TAYLOR_CONFIG */ printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } static void uxusage () { fprintf (stderr, "Usage: %s [options] [-] command\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EX_USAGE); } /* Add a line to the execute file. */ static void uxadd_xqt_line (bchar, z1, z2) int bchar; const char *z1; const char *z2; { char *z1q; char *z2q; if (eXxqt_file == NULL) { const char *zxqt_name; if (fXxqtlocal) zxqt_name = zsysdep_xqt_file_name (); else zxqt_name = zsysdep_data_file_name (&sXxqtsys, zXxqtloc, bXgrade, TRUE, abXxqt_tname, (char *) NULL, abXxqt_xname); if (zxqt_name == NULL) uxabort (EX_OSERR); uxrecord_file (zxqt_name); eXxqt_file = esysdep_fopen (zxqt_name, FALSE, FALSE, TRUE); if (eXxqt_file == NULL) uxabort (EX_OSERR); } z1q = NULL; z2q = NULL; if (fXquote) { if (! fXquote_output) { fprintf (eXxqt_file, "Q\n"); fXquote_output = TRUE; } if (z1 != NULL) { z1q = zquote_cmd_string (z1, FALSE); z1 = z1q; } if (z2 != NULL) { z2q = zquote_cmd_string (z2, FALSE); z2 = z2q; } } if (z1 == NULL) fprintf (eXxqt_file, "%c\n", bchar); else if (z2 == NULL) fprintf (eXxqt_file, "%c %s\n", bchar, z1); else fprintf (eXxqt_file, "%c %s %s\n", bchar, z1, z2); if (z1q != NULL) ubuffree (z1q); if (z2q != NULL) ubuffree (z2q); } /* Add a file to be sent to the execute system. */ static void uxadd_send_file (zfrom, zto, zoptions, ztemp, zforward) const char *zfrom; const char *zto; const char *zoptions; const char *ztemp; const char *zforward; { struct scmd s; if (zforward != NULL) { char *zbase; char *zxqt; char abtname[CFILE_NAME_LEN]; char abdname[CFILE_NAME_LEN]; char abxname[CFILE_NAME_LEN]; FILE *e; /* We want to forward this file through the first execution system to other systems. We set up a remote execution of uucp to forward the file. */ zbase = zsysdep_base_name (zfrom); if (zbase == NULL) uxabort (EX_OSERR); zxqt = zsysdep_data_file_name (&sXxqtsys, zXxqtloc, bXgrade, TRUE, abtname, abdname, abxname); if (zxqt == NULL) uxabort (EX_OSERR); e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE); if (e == NULL) uxabort (EX_OSERR); uxrecord_file (zxqt); fprintf (e, "U %s %s\n", zsysdep_login_name (), zXxqtloc); fprintf (e, "F %s %s\n", abdname, zbase); fprintf (e, "C uucp -C -W -d -g %c %s %s!%s\n", bXgrade, zbase, zforward, zto); ubuffree (zbase); if (! fstdiosync (e, zxqt)) ulog (LOG_FATAL, "fsync failed"); if (fclose (e) != 0) ulog (LOG_FATAL, "fclose: %s", strerror (errno)); /* Send the execution file. */ s.bcmd = 'S'; s.bgrade = bXgrade; s.pseq = NULL; s.zfrom = zbufcpy (abtname); s.zto = zbufcpy (abxname); s.zuser = zsysdep_login_name (); s.zoptions = "C"; s.ztemp = s.zfrom; s.imode = 0666; s.znotify = NULL; s.cbytes = -1; s.zcmd = NULL; s.ipos = 0; ++cXcmds; pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds, cXcmds * sizeof (struct scmd)); pasXcmds[cXcmds - 1] = s; uxadd_name (abtname); /* Send the data file to abdname where the execution file will expect it. */ zto = abdname; } s.bcmd = 'S'; s.bgrade = bXgrade; s.pseq = NULL; s.zfrom = zbufcpy (zfrom); s.zto = zbufcpy (zto); s.zuser = zsysdep_login_name (); s.zoptions = zbufcpy (zoptions); s.ztemp = zbufcpy (ztemp); s.imode = 0666; s.znotify = ""; s.cbytes = -1; s.zcmd = NULL; s.ipos = 0; ++cXcmds; pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds, cXcmds * sizeof (struct scmd)); pasXcmds[cXcmds - 1] = s; uxadd_name (zfrom); } /* Copy stdin to a file. This is a separate function because it may call setjmp. */ static void uxcopy_stdin (e) FILE *e; { CATCH_PROTECT size_t cread; char ab[1024]; do { size_t cwrite; /* I want to use fread here, but there is a bug in some versions of SVR4 which causes fread to return less than a complete buffer even if EOF has not been reached. This is not online time, so speed is not critical, but it's still quite annoying to have to use an inefficient algorithm. */ cread = 0; if (fsysdep_catch ()) { usysdep_start_catch (); while (cread < sizeof (ab)) { int b; if (FGOT_SIGNAL ()) uxabort (EX_OSERR); /* There's an unimportant race here. If the user hits ^C between the FGOT_SIGNAL we just did and the time we enter getchar, we won't know about the signal (unless we're doing a longjmp, but we normally aren't). It's not a big problem, because the user can just hit ^C again. */ b = getchar (); if (b == EOF) break; ab[cread] = b; ++cread; } } usysdep_end_catch (); if (FGOT_SIGNAL ()) uxabort (EX_OSERR); if (cread > 0) { cwrite = fwrite (ab, sizeof (char), cread, e); if (cwrite != cread) ulog (LOG_FATAL, "fwrite: Wrote %d when attempted %d", (int) cwrite, (int) cread); } } while (cread == sizeof ab); } /* Keep track of all files we have created so that we can delete them if we get a signal. The argument will be on the heap. */ static int cXfiles; static const char **pXaz; static void uxrecord_file (zfile) const char *zfile; { pXaz = (const char **) xrealloc ((pointer) pXaz, (cXfiles + 1) * sizeof (const char *)); pXaz[cXfiles] = zfile; ++cXfiles; } /* The function called for a LOG_FATAL error. */ static void uxfatal () { uxabort (EX_UNAVAILABLE); } /* Delete all the files we have recorded and exit. */ static void uxabort (istat) int istat; { int i; if (eXxqt_file != NULL) (void) fclose (eXxqt_file); if (eXclose != NULL) (void) fclose (eXclose); for (i = 0; i < cXfiles; i++) (void) remove (pXaz[i]); ulog_close (); exit (istat); } /* Add a name to the list of file names we are going to log. We log all the file names which will appear in the uucico log file. This permits people to associate the file send in the uucico log with the uux entry which created the file. Normally only one file name will appear. */ static void uxadd_name (z) const char *z; { if (zXnames == NULL) zXnames = zbufcpy (z); else { size_t cold, cadd; char *znew; cold = strlen (zXnames); cadd = strlen (z); znew = zbufalc (cold + 2 + cadd); memcpy (znew, zXnames, cold); znew[cold] = ' '; memcpy (znew + cold + 1, z, cadd + 1); ubuffree (zXnames); zXnames = znew; } } uucp-1.07/uuxqt.c0000664000076400007640000013077707665321760007475 /* uuxqt.c Run uux commands. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char uuxqt_rcsid[] = "$Id: uuxqt.c,v 1.92 2002/03/05 19:10:42 ian Rel $"; #endif #include #include #include "getopt.h" #include "uudefs.h" #include "uuconf.h" #include "system.h" /* Static variables used to unlock things if we get a fatal error. */ static int iQlock_seq = -1; static const char *zQunlock_cmd; static const char *zQunlock_file; static boolean fQunlock_directory; int cQmaxuuxqts; /* Static variables to free in uqcleanup. */ static char *zQoutput; static char *zQmail; /* Local functions. */ static void uqusage P((void)); static void uqhelp P((void)); static void uqabort P((void)); static void uqdo_xqt_file P((pointer puuconf, const char *zfile, const char *zbase, const struct uuconf_system *qsys, const char *zlocalname, const char *zcmd, boolean *pfprocessed)); static void uqcleanup P((const char *zfile, int iflags)); static int isave_files P((const struct uuconf_system *, const char *zmail, const char *zfile, int iclean)); static boolean fqforward P((const char *zfile, char **pzallowed, const char *zlog, const char *zmail)); /* Long getopt options. */ static const struct option asQlongopts[] = { { "command", required_argument, 0, 'c' }, { "system", required_argument, 0, 's' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; int main (argc, argv) int argc; char **argv; { /* The type of command to execute (NULL for any type). */ const char *zcmd = NULL; /* The configuration file name. */ const char *zconfig = NULL; /* The system to execute commands for. */ const char *zdosys = NULL; int iopt; pointer puuconf; int iuuconf; const char *zlocalname; boolean fany; char *z, *zgetsys; boolean ferr; boolean fsys; struct uuconf_system ssys; zProgram = argv[0]; while ((iopt = getopt_long (argc, argv, "c:I:s:vx:", asQlongopts, (int *) NULL)) != EOF) { switch (iopt) { case 'c': /* Set the type of command to execute. */ zcmd = optarg; break; case 'I': /* Set the configuration file name. */ if (fsysdep_other_config (optarg)) zconfig = optarg; break; case 's': zdosys = optarg; break; case 'x': #if DEBUG > 1 /* Set the debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'v': /* Print version and exit. */ printf ("uuxqt (Taylor UUCP) %s\n", VERSION); printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n"); printf ("This program is free software; you may redistribute it under the terms of\n"); printf ("the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY.\n"); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 1: /* --help. */ uqhelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found and flag set. */ break; default: uqusage (); break; } } if (optind != argc) uqusage (); iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); #if DEBUG > 1 { const char *zdebug; iuuconf = uuconf_debuglevel (puuconf, &zdebug); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (zdebug != NULL) iDebug |= idebug_parse (zdebug); } #endif iuuconf = uuconf_maxuuxqts (puuconf, &cQmaxuuxqts); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); #ifdef SIGINT usysdep_signal (SIGINT); #endif #ifdef SIGHUP usysdep_signal (SIGHUP); #endif #ifdef SIGQUIT usysdep_signal (SIGQUIT); #endif #ifdef SIGTERM usysdep_signal (SIGTERM); #endif #ifdef SIGPIPE usysdep_signal (SIGPIPE); #endif usysdep_initialize (puuconf, INIT_SUID); ulog_to_file (puuconf, TRUE); ulog_fatal_fn (uqabort); iuuconf = uuconf_localname (puuconf, &zlocalname); if (iuuconf == UUCONF_NOT_FOUND) { zlocalname = zsysdep_localname (); if (zlocalname == NULL) exit (EXIT_FAILURE); } else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); fsys = FALSE; /* If we were given a system name, canonicalize it. */ if (zdosys != NULL) { iuuconf = uuconf_system_info (puuconf, zdosys, &ssys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (strcmp (zdosys, zlocalname) == 0) { iuuconf = uuconf_system_local (puuconf, &ssys); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); ssys.uuconf_zname = (char *) zlocalname; } else { if (! funknown_system (puuconf, zdosys, &ssys)) ulog (LOG_FATAL, "%s: system not found", zdosys); } } zdosys = zbufcpy (ssys.uuconf_zname); fsys = TRUE; } /* Limit the number of uuxqt processes, and make sure we're the only uuxqt daemon running for this command. */ iQlock_seq = ixsysdep_lock_uuxqt (zcmd, cQmaxuuxqts); if (iQlock_seq < 0) { ulog_close (); usysdep_exit (TRUE); } zQunlock_cmd = zcmd; /* Keep scanning the execute files until we don't process any of them. */ do { fany = FALSE; /* Look for each execute file, and run it. */ if (! fsysdep_get_xqt_init (zdosys)) { ulog_close (); usysdep_exit (FALSE); } while ((z = zsysdep_get_xqt (zdosys, &zgetsys, &ferr)) != NULL) { const char *zloc; boolean fprocessed; char *zbase; /* Get the system information for the system returned by zsysdep_get_xqt. */ if (! fsys || strcmp (ssys.uuconf_zname, zgetsys) != 0) { if (fsys) (void) uuconf_system_free (puuconf, &ssys); iuuconf = uuconf_system_info (puuconf, zgetsys, &ssys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); ubuffree (z); ubuffree (zgetsys); continue; } else if (strcmp (zgetsys, zlocalname) == 0) { iuuconf = uuconf_system_local (puuconf, &ssys); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); ubuffree (z); ubuffree (zgetsys); continue; } ssys.uuconf_zname = (char *) zlocalname; } else { if (! funknown_system (puuconf, zgetsys, &ssys)) { ulog (LOG_ERROR, "%s: Execute file for unknown system %s", z, zgetsys); (void) remove (z); ubuffree (z); ubuffree (zgetsys); continue; } } } fsys = TRUE; } /* If we've received a signal, get out of the loop. */ if (FGOT_SIGNAL ()) { ubuffree (z); ubuffree (zgetsys); break; } /* Make sure we are supposed to be executing jobs for this system. */ if (zdosys != NULL && strcmp (zdosys, ssys.uuconf_zname) != 0) { ubuffree (z); ubuffree (zgetsys); continue; } zloc = ssys.uuconf_zlocalname; if (zloc == NULL) zloc = zlocalname; ulog_system (ssys.uuconf_zname); zbase = zsysdep_base_name (z); uqdo_xqt_file (puuconf, z, zbase, &ssys, zloc, zcmd, &fprocessed); ubuffree (zbase); ulog_system ((const char *) NULL); ulog_user ((const char *) NULL); if (fprocessed) fany = TRUE; ubuffree (z); ubuffree (zgetsys); } usysdep_get_xqt_free (zdosys); } while (fany && ! FGOT_SIGNAL ()); (void) fsysdep_unlock_uuxqt (iQlock_seq, zcmd, cQmaxuuxqts); iQlock_seq = -1; ulog_close (); if (FGOT_SIGNAL ()) ferr = TRUE; usysdep_exit (! ferr); /* Avoid errors about not returning a value. */ return 0; } static void uqhelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n", VERSION); printf ("Usage: %s [-c,--command cmd] [-s,--system system]\n", zProgram); printf (" -c,--command cmd: Set type of command to execute\n"); printf (" -s,--system system: Execute commands only for named system\n"); printf (" -x,--debug debug: Set debugging level\n"); #if HAVE_TAYLOR_CONFIG printf (" -I,--config file: Set configuration file to use\n"); #endif /* HAVE_TAYLOR_CONFIG */ printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); printf ("Report bugs to taylor-uucp@gnu.org\n"); } static void uqusage () { fprintf (stderr, "Usage: %s [-c,--command cmd] [-s,--system system]\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EXIT_FAILURE); } /* This is the abort function called when we get a fatal error. */ static void uqabort () { #if ! HAVE_HDB_LOGGING /* When using HDB logging, it's a pain to have no system name. */ ulog_system ((const char *) NULL); #endif ulog_user ((const char *) NULL); if (fQunlock_directory) (void) fsysdep_unlock_uuxqt_dir (iQlock_seq); if (zQunlock_file != NULL) (void) fsysdep_unlock_uuxqt_file (zQunlock_file); if (iQlock_seq >= 0) (void) fsysdep_unlock_uuxqt (iQlock_seq, zQunlock_cmd, cQmaxuuxqts); ulog_close (); usysdep_exit (FALSE); } /* An execute file is a series of lines. The first character of each line is a command. The following commands are defined: C command-line I standard-input O standard-output [ system ] F required-file filename-to-use R requestor-address U user system Z (acknowledge if command failed; default) N (no acknowledgement on failure) n (acknowledge if command succeeded) B (return command input on error) e (process with sh) E (process with exec) M status-file Q (C, I, O, F, R, U, M arguments are backslash quoted) # comment Unrecognized commands are ignored. We actually do not recognize the Z command, since it requests default behaviour. We always send mail on failure, unless the N command appears. We never send mail on success, unless the n command appears. This code does not currently support the B or M commands. */ /* Command arguments. */ static char **azQargs; /* Command as a complete string. */ static char *zQcmd; /* Standard input file name. */ static char *zQinput; /* Standard output file name. */ static char *zQoutfile; /* Standard output system. */ static char *zQoutsys; /* Number of required files. */ static int cQfiles; /* Names of required files. */ static char **azQfiles; /* Names required files should be renamed to (NULL if original is OK). */ static char **azQfiles_to; /* Requestor address (this is where mail should be sent). */ static char *zQrequestor; /* User name. */ static const char *zQuser; /* System name. */ static const char *zQsystem; /* This is set by the N flag, meaning that no acknowledgement should be mailed on failure. */ static boolean fQno_ack; /* This is set by the n flag, meaning that acknowledgement should be mailed if the command succeeded. */ static boolean fQsuccess_ack; /* This is set by the B flag, meaning that command input should be mailed to the requestor if an error occurred. */ static boolean fQsend_input; /* This is set by the E flag, meaning that exec should be used to execute the command. */ static boolean fQuse_exec; /* The status should be copied to this file on the requesting host. */ static const char *zQstatus_file; /* Whether the entries in the file are backslash quoted. */ static boolean fQquoted; #if ALLOW_SH_EXECUTION /* This is set by the e flag, meaning that sh should be used to execute the command. */ static boolean fQuse_sh; #endif /* ALLOW_SH_EXECUTION */ static int iqcmd P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int iqout P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int iqfile P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int iqrequestor P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int iquser P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int iqset P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); /* We are lax about the number of arguments the functions accept, because there is a lot of variation in what other (buggy) UUCP packages generate. Unused arguments are ignored. */ static const struct uuconf_cmdtab asQcmds[] = { { "C", UUCONF_CMDTABTYPE_FN | 0, NULL, iqcmd }, { "I", UUCONF_CMDTABTYPE_STRING, (pointer) &zQinput, NULL }, { "O", UUCONF_CMDTABTYPE_FN | 0, NULL, iqout }, { "F", UUCONF_CMDTABTYPE_FN | 0, NULL, iqfile }, { "R", UUCONF_CMDTABTYPE_FN | 0, NULL, iqrequestor }, { "U", UUCONF_CMDTABTYPE_FN | 0, NULL, iquser }, { "N", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQno_ack, iqset }, { "n", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQsuccess_ack, iqset }, { "B", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQsend_input, iqset }, #if ALLOW_SH_EXECUTION { "e", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQuse_sh, iqset }, #endif { "E", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQuse_exec, iqset }, { "M", UUCONF_CMDTABTYPE_STRING, (pointer) &zQstatus_file, NULL }, { "Q", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQquoted, iqset }, { NULL, 0, NULL, NULL } }; /* Handle the C command: store off the arguments. */ /*ARGSUSED*/ static int iqcmd (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { int i; size_t clen; if (argc <= 1) return UUCONF_CMDTABRET_CONTINUE; azQargs = (char **) xmalloc (argc * sizeof (char *)); clen = 0; for (i = 1; i < argc; i++) { azQargs[i - 1] = zbufcpy (argv[i]); clen += strlen (argv[i]) + 1; } azQargs[i - 1] = NULL; zQcmd = (char *) xmalloc (clen); zQcmd[0] = '\0'; for (i = 1; i < argc - 1; i++) { strcat (zQcmd, argv[i]); strcat (zQcmd, " "); } strcat (zQcmd, argv[i]); return UUCONF_CMDTABRET_CONTINUE; } /* Handle the O command, which may have one or two arguments. */ /*ARGSUSED*/ static int iqout (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { if (argc > 1) zQoutfile = zbufcpy (argv[1]); if (argc > 2) zQoutsys = zbufcpy (argv[2]); return UUCONF_CMDTABRET_CONTINUE; } /* Handle the F command, which may have one or two arguments. */ /*ARGSUSED*/ static int iqfile (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { if (argc < 2) return UUCONF_CMDTABRET_CONTINUE; /* If this file is not in the spool directory, just ignore it. */ if (! fspool_file (argv[1])) return UUCONF_CMDTABRET_CONTINUE; ++cQfiles; azQfiles = (char **) xrealloc ((pointer) azQfiles, cQfiles * sizeof (char *)); azQfiles_to = (char **) xrealloc ((pointer) azQfiles_to, cQfiles * sizeof (char *)); azQfiles[cQfiles - 1] = zbufcpy (argv[1]); if (argc > 2) azQfiles_to[cQfiles - 1] = zbufcpy (argv[2]); else azQfiles_to[cQfiles - 1] = NULL; return UUCONF_CMDTABRET_CONTINUE; } /* Handle the R command, which may have one or two arguments. */ /*ARGSUSED*/ static int iqrequestor (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { /* We normally have a single argument, which is the ``requestor'' address, to which we should send any success or error messages. Apparently the DOS program UUPC sends two arguments, which are the username and the host. */ if (argc == 2) zQrequestor = zbufcpy (argv[1]); else if (argc > 2) { zQrequestor = zbufalc (strlen (argv[1]) + strlen (argv[2]) + sizeof "!"); sprintf (zQrequestor, "%s!%s", argv[2], argv[1]); } return UUCONF_CMDTABRET_CONTINUE; } /* Handle the U command, which takes two arguments. */ /*ARGSUSED*/ static int iquser (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { if (argc > 1) zQuser = argv[1]; if (argc > 2) zQsystem = argv[2]; return UUCONF_CMDTABRET_KEEP; } /* Handle various commands which just set boolean variables. */ /*ARGSUSED*/ static int iqset (puuconf, argc, argv, pvar, pinfo) pointer puuconf ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { boolean *pf = (boolean *) pvar; *pf = TRUE; return UUCONF_CMDTABRET_CONTINUE; } /* The execution processing does a lot of things that have to be cleaned up. Rather than try to add the appropriate statements to each return point, we keep a set of flags indicating what has to be cleaned up. The actual clean up is done by the function uqcleanup. */ #define REMOVE_FILE (01) #define REMOVE_NEEDED (02) #define FREE_QINPUT (04) #define REMOVE_QINPUT (010) #define FREE_OUTPUT (020) #define FREE_MAIL (040) /* Process an execute file. The zfile argument is the name of the execute file. The zbase argument is the base name of zfile. The qsys argument describes the system it came from. The zcmd argument is the name of the command we are executing (from the -c option) or NULL if any command is OK. This sets *pfprocessed to TRUE if the file is ready to be executed. */ static void uqdo_xqt_file (puuconf, zfile, zbase, qsys, zlocalname, zcmd, pfprocessed) pointer puuconf; const char *zfile; const char *zbase; const struct uuconf_system *qsys; const char *zlocalname; const char *zcmd; boolean *pfprocessed; { char *zabsolute; boolean ferr; FILE *e; int iuuconf; int i; int iclean; const char *zmail; char *zoutput; char *zinput; boolean fbadname; char abtemp[CFILE_NAME_LEN]; char abdata[CFILE_NAME_LEN]; char *zerror; struct uuconf_system soutsys; const struct uuconf_system *qoutsys; boolean fshell; size_t clen; char *zfullcmd; boolean ftemp; *pfprocessed = FALSE; e = fopen (zfile, "r"); if (e == NULL) return; azQargs = NULL; zQcmd = NULL; zQinput = NULL; zQoutfile = NULL; zQoutsys = NULL; cQfiles = 0; azQfiles = NULL; azQfiles_to = NULL; zQrequestor = NULL; zQuser = NULL; zQsystem = NULL; fQno_ack = FALSE; fQsuccess_ack = FALSE; fQsend_input = FALSE; fQuse_exec = FALSE; zQstatus_file = NULL; fQquoted = FALSE; #if ALLOW_SH_EXECUTION fQuse_sh = FALSE; #endif iuuconf = uuconf_cmd_file (puuconf, e, asQcmds, (pointer) zbase, (uuconf_cmdtabfn) NULL, (UUCONF_CMDTABFLAG_CASE | UUCONF_CMDTABFLAG_NOCOMMENTS), (pointer) NULL); (void) fclose (e); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); /* If we got a non-transient error, we notify the administrator. We can't bounce it back to the original requestor, because we don't know how to read the file to figure out who it is (it would probably be possible to read the file and work it out, but it doesn't seem worth it for such an unlikely error). */ if (UUCONF_ERROR_VALUE (iuuconf) == UUCONF_SYNTAX_ERROR || UUCONF_ERROR_VALUE (iuuconf) == UUCONF_UNKNOWN_COMMAND) { const char *az[20]; char *znew; i = 0; az[i++] = "The execution file\n\t"; az[i++] = zfile; az[i++] = "\nfor system\n\t"; az[i++] = qsys->uuconf_zname; az[i++] = "\nwas corrupt. "; znew = zsysdep_save_corrupt_file (zfile); if (znew == NULL) { az[i++] = "The file could not be preserved.\n"; (void) remove (zfile); } else { az[i++] = "It has been moved to\n\t"; az[i++] = znew; az[i++] = "\n"; } (void) fsysdep_mail (OWNER, "Corrupt execution file", i, az); ubuffree (znew); } return; } if (fQquoted) { if (azQargs != NULL) { for (i = 0; azQargs[i] != NULL; ++i) (void) cescape (azQargs[i]); } if (zQcmd != NULL) (void) cescape (zQcmd); if (zQinput != NULL) (void) cescape (zQinput); if (zQoutfile != NULL) (void) cescape (zQoutfile); if (zQoutsys != NULL) (void) cescape (zQoutsys); for (i = 0; i < cQfiles; ++i) { (void) cescape (azQfiles[i]); if (azQfiles_to[i] != NULL) (void) cescape (azQfiles_to[i]); } if (zQrequestor != NULL) (void) cescape (zQrequestor); if (zQuser != NULL) (void) cescape ((char *) zQuser); if (zQsystem != NULL) (void) cescape ((char *) zQsystem); if (zQstatus_file != NULL) (void) cescape ((char *) zQstatus_file); } iclean = 0; if (azQargs == NULL) { ulog (LOG_ERROR, "%s: No command given", zbase); uqcleanup (zfile, iclean | REMOVE_FILE); return; } if (zcmd != NULL) { if (strcmp (zcmd, azQargs[0]) != 0) { uqcleanup (zfile, iclean); return; } } else { /* If there is a lock file for this particular command already, it means that some other uuxqt is supposed to handle it. */ if (fsysdep_uuxqt_locked (azQargs[0])) { uqcleanup (zfile, iclean); return; } } /* Lock this particular file. */ if (! fsysdep_lock_uuxqt_file (zfile)) { uqcleanup (zfile, iclean); return; } zQunlock_file = zfile; /* Now that we have the file locked, make sure it still exists. Otherwise another uuxqt could have just finished processing it and removed the lock file. */ if (! fsysdep_file_exists (zfile)) { uqcleanup (zfile, iclean); return; } if (zQuser != NULL) ulog_user (zQuser); else if (zQrequestor != NULL) ulog_user (zQrequestor); else ulog_user ("unknown"); /* zQsystem, if it is set, comes from the execution file, which means that we do not trust it. We only retain it if qsys->uuconf_zname is a prefix of it, since that can happen with a job from an anonymous system on certain spool directory types, and is unlikely to cause any trouble anyhow. */ if (zQsystem == NULL || strncmp (zQsystem, qsys->uuconf_zname, strlen (qsys->uuconf_zname)) != 0) zQsystem = qsys->uuconf_zname; /* Make sure that all the required files exist, and get their full names in the spool directory. */ for (i = 0; i < cQfiles; i++) { char *zreal; zreal = zsysdep_spool_file_name (qsys, azQfiles[i], (pointer) NULL); if (zreal == NULL) { uqcleanup (zfile, iclean); return; } if (! fsysdep_file_exists (zreal)) { uqcleanup (zfile, iclean); return; } ubuffree (azQfiles[i]); azQfiles[i] = zbufcpy (zreal); ubuffree (zreal); } /* Lock the execution directory. */ if (! fsysdep_lock_uuxqt_dir (iQlock_seq)) { ulog (LOG_ERROR, "Could not lock execute directory"); uqcleanup (zfile, iclean); return; } fQunlock_directory = TRUE; iclean |= REMOVE_FILE | REMOVE_NEEDED; *pfprocessed = TRUE; /* Get the address to mail results to. Prepend the system from which the execute file originated, since mail addresses are relative to it. */ zmail = NULL; if (zQrequestor != NULL) zmail = zQrequestor; else if (zQuser != NULL) zmail = zQuser; if (zmail != NULL #if HAVE_INTERNET_MAIL && strchr (zmail, '@') == NULL #endif && strcmp (zQsystem, zlocalname) != 0) { char *zset; zset = zbufalc (strlen (zQsystem) + strlen (zmail) + 2); sprintf (zset, "%s!%s", zQsystem, zmail); zmail = zset; zQmail = zset; iclean |= FREE_MAIL; } /* The command "uucp" is handled specially. We make sure that the appropriate forwarding is permitted, and we add a -u argument to specify the user. */ if (strcmp (azQargs[0], "uucp") == 0) { char *zfrom, *zto; boolean fmany; boolean finoptions; char **azargs; const char *zuser; zfrom = NULL; zto = NULL; fmany = FALSE; finoptions = TRUE; /* Skip all the options, and get the from and to specs. We don't permit multiple arguments. We have to do mini-getopt processing here. */ for (i = 1; azQargs[i] != NULL; i++) { if (azQargs[i][0] == '-' && finoptions) { if (azQargs[i][1] == '-') { if (azQargs[i][2] == '\0') finoptions = FALSE; /* The --grade, --notify, and --status options take an argument. */ else if (strncmp (azQargs[i] + 2, "g", 1) == 0 || strncmp (azQargs[i] + 2, "not", 3) == 0 || strncmp (azQargs[i] + 2, "s", 1) == 0) { if (strchr (azQargs[i] + 2, '=') == NULL) ++i; } /* The --config, --user, and --debug options are not permitted. */ else if (strncmp (azQargs[i] + 2, "con", 3) == 0 || strncmp (azQargs[i] + 2, "us", 2) == 0 || strncmp (azQargs[i] + 2, "de", 2) == 0) { azQargs[i][1] = 'r'; azQargs[i][2] = '\0'; if (strchr (azQargs[i] + 3, '=') == NULL) { ++i; azQargs[i] = zbufcpy ("-r"); } } } else { char *zopts; for (zopts = azQargs[i] + 1; *zopts != '\0'; zopts++) { /* The -g, -n, and -s options take an argument. */ if (*zopts == 'g' || *zopts == 'n' || *zopts == 's') { if (zopts[1] == '\0') ++i; break; } /* The -I, -u and -x options are not permitted. */ if (*zopts == 'I' || *zopts == 'u' || *zopts == 'x') { *zopts = 'r'; if (zopts[1] != '\0') zopts[1] = '\0'; else { ++i; azQargs[i] = zbufcpy ("-r"); } break; } } } } else if (zfrom == NULL) zfrom = azQargs[i]; else if (zto == NULL) zto = azQargs[i]; else { fmany = TRUE; break; } } /* Add the -u argument. This is required to let uucp do the correct permissions checking on the file transfer. */ for (i = 0; azQargs[i] != NULL; i++) ; azargs = (char **) xmalloc ((i + 2) * sizeof (char *)); azargs[0] = azQargs[0]; zuser = zQuser; if (zuser == NULL) zuser = "uucp"; azargs[1] = zbufalc (strlen (zQsystem) + strlen (zuser) + sizeof "-u!"); sprintf (azargs[1], "-u%s!%s", zQsystem, zuser); memcpy (azargs + 2, azQargs + 1, i * sizeof (char *)); xfree ((pointer) azQargs); azQargs = azargs; /* Find the uucp binary. */ zabsolute = zsysdep_find_command ("uucp", qsys->uuconf_pzcmds, qsys->uuconf_pzpath, &ferr); if (zabsolute == NULL && ! ferr) { const char *azcmds[2]; /* If "uucp" is not a permitted command, then the forwarding entries must be set. */ if (! fqforward (zfrom, qsys->uuconf_pzforward_from, "from", zmail) || ! fqforward (zto, qsys->uuconf_pzforward_to, "to", zmail)) { uqcleanup (zfile, iclean); return; } /* If "uucp" is not a permitted command, then only uucp requests with a single source are permitted, since that is all that will be generated by uucp or uux. */ if (fmany || zfrom == NULL || zto == NULL) { ulog (LOG_ERROR, "Bad uucp request %s", zQcmd); if (zmail != NULL && ! fQno_ack) { const char *az[20]; i = 0; az[i++] = "Your execution request failed because it was an"; az[i++] = " unsupported uucp request.\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } uqcleanup (zfile, iclean); return; } azcmds[0] = "uucp"; azcmds[1] = NULL; zabsolute = zsysdep_find_command ("uucp", (char **) azcmds, qsys->uuconf_pzpath, &ferr); } if (zabsolute == NULL) { if (! ferr) ulog (LOG_ERROR, "Can't find uucp executable"); uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } } else { /* Get the pathname to execute. */ zabsolute = zsysdep_find_command (azQargs[0], qsys->uuconf_pzcmds, qsys->uuconf_pzpath, &ferr); if (zabsolute == NULL) { if (ferr) { /* If we get an error, try again later. */ uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } /* Not permitted. Send mail to requestor. */ ulog (LOG_ERROR, "Not permitted to execute %s", azQargs[0]); if (zmail != NULL && ! fQno_ack) { const char *az[20]; i = 0; az[i++] = "Your execution request failed because you are not"; az[i++] = " permitted to execute\n\t"; az[i++] = azQargs[0]; az[i++] = "\non this system.\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } iclean = isave_files (qsys, zmail, zfile, iclean); uqcleanup (zfile, iclean); return; } } ubuffree (azQargs[0]); azQargs[0] = zabsolute; for (i = 1; azQargs[i] != NULL; i++) { char *zlocal; zlocal = zsysdep_xqt_local_file (qsys, azQargs[i]); if (zlocal != NULL) { ubuffree (azQargs[i]); azQargs[i] = zlocal; } } #if ! ALLOW_FILENAME_ARGUMENTS /* Check all the arguments to make sure they don't try to specify files they are not permitted to access. */ for (i = 1; azQargs[i] != NULL; i++) { if (! fsysdep_xqt_check_file (qsys, azQargs[i])) { if (zmail != NULL && ! fQno_ack) { const char *az[20]; const char *zfailed; zfailed = azQargs[i]; i = 0; az[i++] = "Your execution request failed because you are not"; az[i++] = " permitted to refer to file\n\t"; az[i++] = zfailed; az[i++] = "\non this system.\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } iclean = isave_files (qsys, zmail, zfile, iclean); uqcleanup (zfile, iclean); return; } } #endif /* ! ALLOW_FILENAME_ARGUMENTS */ ulog (LOG_NORMAL, "Executing %s (%s)", zbase, zQcmd); if (zQinput != NULL) { boolean fspool; char *zreal; fspool = fspool_file (zQinput); if (! fspool) zreal = zsysdep_local_file (zQinput, qsys->uuconf_zpubdir, &fbadname); else { zreal = zsysdep_spool_file_name (qsys, zQinput, (pointer) NULL); fbadname = FALSE; } if (zreal == NULL && ! fbadname) { /* If we get an error, try again later. */ uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } if (zreal != NULL) { zQinput = zreal; iclean |= FREE_QINPUT; if (fspool) iclean |= REMOVE_QINPUT; } if (zreal == NULL || (! fspool && ! fin_directory_list (zQinput, qsys->uuconf_pzremote_send, qsys->uuconf_zpubdir, TRUE, TRUE, (const char *) NULL))) { ulog (LOG_ERROR, "Not permitted to read %s", zQinput); if (zmail != NULL && ! fQno_ack) { const char *az[20]; i = 0; az[i++] = "Your execution request failed because you are"; az[i++] = " not permitted to read\n\t"; az[i++] = zQinput; az[i++] = "\non this system.\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } uqcleanup (zfile, iclean); return; } } zoutput = NULL; if (zQoutfile == NULL) qoutsys = NULL; else if (zQoutsys != NULL && strcmp (zQoutsys, zlocalname) != 0) { char *zdata; /* The output file is destined for some other system, so we must use a temporary file to catch standard output. */ if (strcmp (zQoutsys, qsys->uuconf_zname) == 0) qoutsys = qsys; else { iuuconf = uuconf_system_info (puuconf, zQoutsys, &soutsys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } if (! funknown_system (puuconf, zQoutsys, &soutsys)) { ulog (LOG_ERROR, "Can't send standard output to unknown system %s", zQoutsys); /* We don't send mail to unknown systems, either. Maybe we should. */ uqcleanup (zfile, iclean); return; } } qoutsys = &soutsys; } zdata = zsysdep_data_file_name (qoutsys, zlocalname, BDEFAULT_UUX_GRADE, FALSE, abtemp, abdata, (char *) NULL); if (zdata == NULL) { /* If we get an error, try again later. */ uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } zoutput = zdata; zQoutput = zoutput; iclean |= FREE_OUTPUT; } else { boolean fok; qoutsys = NULL; /* If we permitted the standard output to be redirected into the spool directory, people could set up phony commands. */ if (fspool_file (zQoutfile)) fok = FALSE; else { zoutput = zsysdep_local_file (zQoutfile, qsys->uuconf_zpubdir, &fbadname); if (zoutput == NULL) { if (! fbadname) { /* If we get an error, try again later. */ uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } fok = FALSE; } else { ubuffree (zQoutfile); zQoutfile = zoutput; /* Make sure it's OK to receive this file. */ fok = fin_directory_list (zQoutfile, qsys->uuconf_pzremote_receive, qsys->uuconf_zpubdir, TRUE, FALSE, (const char *) NULL); } } if (! fok) { ulog (LOG_ERROR, "Not permitted to write to %s", zQoutfile); if (zmail != NULL && ! fQno_ack) { const char *az[20]; i = 0; az[i++] = "Your execution request failed because you are"; az[i++] = " not permitted to write to\n\t"; az[i++] = zQoutfile; az[i++] = "\non this system.\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } uqcleanup (zfile, iclean); return; } } /* Move the required files to the execution directory if necessary. */ zinput = zQinput; if (! fsysdep_copy_uuxqt_files (cQfiles, (const char **) azQfiles, (const char **) azQfiles_to, iQlock_seq, &zinput)) { /* If we get an error, try again later. */ uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } if (zQinput != NULL && strcmp (zQinput, zinput) != 0) { if ((iclean & FREE_QINPUT) != 0) ubuffree (zQinput); zQinput = zinput; iclean |= FREE_QINPUT; } #if ALLOW_SH_EXECUTION fshell = fQuse_sh; #else fshell = FALSE; #endif /* Get a shell command which uses the full path of the command to execute. */ clen = 0; for (i = 0; azQargs[i] != NULL; i++) clen += strlen (azQargs[i]) + 1; zfullcmd = zbufalc (clen); strcpy (zfullcmd, azQargs[0]); for (i = 1; azQargs[i] != NULL; i++) { strcat (zfullcmd, " "); strcat (zfullcmd, azQargs[i]); } if (! fsysdep_execute (qsys, zQuser == NULL ? (const char *) "uucp" : zQuser, (const char **) azQargs, zfullcmd, zQinput, zoutput, fshell, iQlock_seq, &zerror, &ftemp)) { ubuffree (zfullcmd); if (ftemp) { ulog (LOG_NORMAL, "Will retry later (%s)", zbase); if (zoutput != NULL) (void) remove (zoutput); if (zerror != NULL) { (void) remove (zerror); ubuffree (zerror); } uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } ulog (LOG_NORMAL, "Execution failed (%s)", zbase); if (zmail != NULL && ! fQno_ack) { const char **pz; int cgot; FILE *eerr; int istart; cgot = 20; pz = (const char **) xmalloc (cgot * sizeof (const char *)); i = 0; pz[i++] = "Execution request failed:\n\t"; pz[i++] = zQcmd; pz[i++] = "\n"; if (zerror == NULL) eerr = NULL; else eerr = fopen (zerror, "r"); if (eerr == NULL) { pz[i++] = "There was no output on standard error\n"; istart = i; } else { char *zline; size_t cline; pz[i++] = "Standard error output was:\n"; istart = i; zline = NULL; cline = 0; while (getline (&zline, &cline, eerr) > 0) { if (i >= cgot) { cgot += 20; pz = ((const char **) xrealloc ((pointer) pz, cgot * sizeof (const char *))); } pz[i++] = zbufcpy (zline); } (void) fclose (eerr); xfree ((pointer) zline); } (void) fsysdep_mail (zmail, "Execution failed", i, pz); for (; istart < i; istart++) ubuffree ((char *) pz[istart]); xfree ((pointer) pz); } if (qoutsys != NULL) (void) remove (zoutput); iclean = isave_files (qsys, zmail, zfile, iclean); } else { ubuffree (zfullcmd); if (zmail != NULL && fQsuccess_ack) { const char *az[20]; i = 0; az[i++] = "\nExecution request succeeded:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution succeded", i, az); } /* Now we may have to uucp the output to some other machine. */ if (qoutsys != NULL) { struct scmd s; /* Fill in the command structure. */ s.bcmd = 'S'; s.bgrade = BDEFAULT_UUX_GRADE; s.pseq = NULL; s.zfrom = abtemp; s.zto = zQoutfile; if (zQuser != NULL) s.zuser = zQuser; else s.zuser = "uucp"; if (zmail != NULL && fQsuccess_ack) s.zoptions = "Cn"; else s.zoptions = "C"; s.ztemp = abtemp; s.imode = 0666; if (zmail != NULL && fQsuccess_ack) s.znotify = zmail; else s.znotify = ""; s.cbytes = -1; s.zcmd = NULL; s.ipos = 0; ubuffree (zsysdep_spool_commands (qoutsys, BDEFAULT_UUX_GRADE, 1, &s, (boolean *) NULL)); } } if (zerror != NULL) { (void) remove (zerror); ubuffree (zerror); } uqcleanup (zfile, iclean); } /* If we have enough disk space, save the data files so that the UUCP administrator can examine them. Send a mail message listing the saved files. */ static int isave_files (qsys, zmail, zfile, iclean) const struct uuconf_system *qsys; const char *zmail; const char *zfile; int iclean; { long cspace; char *zsavecmd; char **pzsave; int c; int ifile; char *zsaveinput; const char **pz; int i; /* Save the files if there is 1.5 times the amount of required free space. */ cspace = csysdep_bytes_free (zfile); if (cspace == -1) cspace = FREE_SPACE_DELTA; cspace -= qsys->uuconf_cfree_space + qsys->uuconf_cfree_space / 2; if (cspace < 0) return iclean; zsavecmd = zsysdep_save_failed_file (zfile); if (zsavecmd == NULL) return iclean; c = 1; pzsave = (char **) xmalloc (cQfiles * sizeof (char *)); for (ifile = 0; ifile < cQfiles; ifile++) { if (azQfiles[ifile] != NULL) { ++c; pzsave[ifile] = zsysdep_save_failed_file (azQfiles[ifile]); if (pzsave[ifile] == NULL) { ubuffree (zsavecmd); for (i = 0; i < ifile; i++) if (azQfiles[i] != NULL) ubuffree (pzsave[i]); xfree ((pointer) pzsave); return iclean; } } } zsaveinput = NULL; if ((iclean & REMOVE_QINPUT) != 0 && fsysdep_file_exists (zQinput)) { zsaveinput = zsysdep_save_failed_file (zQinput); if (zsaveinput == NULL) { ubuffree (zsavecmd); for (i = 0; i < cQfiles; i++) if (azQfiles[i] != NULL) ubuffree (pzsave[i]); xfree ((pointer) pzsave); return iclean; } } pz = (const char **) xmalloc ((20 + 2 * cQfiles) * sizeof (char *)); i = 0; pz[i++] = "A UUCP execution request failed:\n\t"; pz[i++] = zQcmd; if (zmail != NULL) { pz[i++] = "\nThe request was made by\n\t"; pz[i++] = zmail; } else { pz[i++] = "\nThe request came from system\n\t"; pz[i++] = qsys->uuconf_zname; } if (c == 1 && zsaveinput == NULL) pz[i++] = "\nThe following file has been saved:\n\t"; else pz[i++] = "\nThe following files have been saved:\n\t"; pz[i++] = zsavecmd; for (ifile = 0; ifile < cQfiles; ifile++) { if (azQfiles[ifile] != NULL) { pz[i++] = "\n\t"; pz[i++] = pzsave[ifile]; } } if (zsaveinput != NULL) { pz[i++] = "\n\t"; pz[i++] = zsaveinput; } pz[i++] = "\n"; (void) fsysdep_mail (OWNER, "UUCP execution files saved after failure", i, pz); xfree ((pointer) pz); ubuffree (zsavecmd); for (ifile = 0; ifile < cQfiles; ifile++) if (azQfiles[ifile] != NULL) ubuffree (pzsave[ifile]); xfree ((pointer) pzsave); ubuffree (zsaveinput); return iclean &~ (REMOVE_FILE | REMOVE_NEEDED); } /* Clean up the results of uqdo_xqt_file. */ static void uqcleanup (zfile, iflags) const char *zfile; int iflags; { int i; DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, "uqcleanup: %s, %d", zfile, iflags); if ((iflags & REMOVE_FILE) != 0) (void) remove (zfile); if ((iflags & REMOVE_NEEDED) != 0) { for (i = 0; i < cQfiles; i++) { if (azQfiles[i] != NULL) (void) remove (azQfiles[i]); } if ((iflags & REMOVE_QINPUT) != 0) (void) remove (zQinput); } if (zQunlock_file != NULL) { (void) fsysdep_unlock_uuxqt_file (zQunlock_file); zQunlock_file = NULL; } if ((iflags & FREE_QINPUT) != 0) ubuffree (zQinput); if ((iflags & FREE_OUTPUT) != 0) ubuffree (zQoutput); if ((iflags & FREE_MAIL) != 0) ubuffree (zQmail); if (fQunlock_directory) { (void) fsysdep_unlock_uuxqt_dir (iQlock_seq); fQunlock_directory = FALSE; } for (i = 0; i < cQfiles; i++) { ubuffree (azQfiles[i]); ubuffree (azQfiles_to[i]); } ubuffree (zQoutfile); ubuffree (zQoutsys); ubuffree (zQrequestor); if (azQargs != NULL) { for (i = 0; azQargs[i] != NULL; i++) ubuffree (azQargs[i]); xfree ((pointer) azQargs); azQargs = NULL; } xfree ((pointer) zQcmd); zQcmd = NULL; xfree ((pointer) azQfiles); azQfiles = NULL; xfree ((pointer) azQfiles_to); azQfiles_to = NULL; } /* Check whether forwarding is permitted. */ static boolean fqforward (zfile, pzallowed, zlog, zmail) const char *zfile; char **pzallowed; const char *zlog; const char *zmail; { const char *zexclam; if (zfile == NULL) return TRUE; zexclam = strchr (zfile, '!'); if (zexclam != NULL) { size_t clen; char *zsys; boolean fret; clen = zexclam - zfile; zsys = zbufalc (clen + 1); memcpy (zsys, zfile, clen); zsys[clen] = '\0'; fret = FALSE; if (pzallowed != NULL) { char **pz; for (pz = pzallowed; *pz != NULL; pz++) { if (strcmp (*pz, "ANY") == 0 || strcmp (*pz, zsys) == 0) { fret = TRUE; break; } } } if (! fret) { ulog (LOG_ERROR, "Not permitted to forward %s %s (%s)", zlog, zsys, zQcmd); if (zmail != NULL && ! fQno_ack) { int i; const char *az[20]; i = 0; az[i++] = "Your execution request failed because you are"; az[i++] = " not permitted to forward files\n"; az[i++] = zlog; az[i++] = " the system\n\t"; az[i++] = zsys; az[i++] = "\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } } ubuffree (zsys); return fret; } return TRUE; } uucp-1.07/uucp.texi0000664000076400007640000123456707665321757010023 \input texinfo @c -*-texinfo-*- @c %**start of header @setfilename uucp.info @settitle Taylor UUCP @setchapternewpage odd @c %**end of header @iftex @finalout @end iftex @ifinfo @format START-INFO-DIR-ENTRY * UUCP: (uucp). Transfer mail and news across phone lines. END-INFO-DIR-ENTRY @end format This file documents Taylor UUCP, version 1.07. Copyright @copyright{} 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. @ignore Permission is granted to process this file through TeX and print the results, provided the printed document carries a copying permission notice identical to this one except for the removal of this paragraph (this paragraph not being relevant to the printed manual). @end ignore Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled ``Copying'' are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled ``Copying'' may be included in a translation approved by the author instead of in the original English. @end ifinfo @titlepage @title Taylor UUCP @subtitle Version 1.07 @author Ian Lance Taylor @email{ian@@airs.com} @page @vskip 0pt plus 1filll Copyright @copyright{} 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Published by Ian Lance Taylor @email{ian@@airs.com}. Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled ``Copying'' are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled ``Copying'' may be included in a translation approved by the author instead of in the original English. @end titlepage @node Top, Copying, (dir), (dir) @top Taylor UUCP 1.07 This is the documentation for the Taylor UUCP package, version 1.07. The programs were written by Ian Lance Taylor. The author can be reached at @email{ian@@airs.com}. There is a mailing list for discussion of the package. The list is hosted by Eric Schnoebelen at @email{cirr.com}. To join (or get off) the list, send mail to @email{taylor-uucp-request@@gnu.org}. Mail to this address is answered by the majordomo program. To join the list, send the message @samp{subscribe @var{address}} where @var{address} is your e-mail address. To send a message to the list, send it to @email{taylor-uucp@@gnu.org}. There is an archive of all messages sent to the mailing list at @url{http://lists.cirr.com}. @menu * Copying:: Taylor UUCP Copying Conditions * Introduction:: Introduction to Taylor UUCP * Invoking the UUCP Programs:: Invoking the UUCP Programs * Installing Taylor UUCP:: Installing Taylor UUCP * Using Taylor UUCP:: Using Taylor UUCP * Configuration Files:: Taylor UUCP Configuration Files * Protocols:: UUCP Protocol Descriptions * Hacking:: Hacking Taylor UUCP * Acknowledgements:: Acknowledgements * Index (concepts):: Concept Index * Index (configuration file):: Index to New Configuration Files --- The Detailed Node Listing --- Invoking the UUCP Programs * Standard Options:: Standard Options for the UUCP Programs * Invoking uucp:: Invoking uucp * Invoking uux:: Invoking uux * Invoking uustat:: Invoking uustat * Invoking uuname:: Invoking uuname * Invoking uulog:: Invoking uulog * Invoking uuto:: Invoking uuto * Invoking uupick:: Invoking uupick * Invoking cu:: Invoking cu * Invoking uucico:: Invoking uucico * Invoking uuxqt:: Invoking uuxqt * Invoking uuchk:: Invoking uuchk * Invoking uuconv:: Invoking uuconv * Invoking uusched:: Invoking uusched Invoking uucp * uucp Description:: Description of uucp * uucp Options:: Options Supported by uucp Invoking uux * uux Description:: Description of uux * uux Options:: Options Supported by uux * uux Examples:: Examples of uux Usage Invoking uustat * uustat Description:: Description of uustat * uustat Options:: Options Supported by uustat * uustat Examples:: Examples of uustat Usage Invoking cu * cu Description:: Description of cu * cu Commands:: Commands Supported by cu * cu Variables:: Variables Supported by cu * cu Options:: Options Supported by cu Invoking uucico * uucico Description:: Description of uucico * uucico Options:: Options Supported by uucico Installing Taylor UUCP * Compilation:: Compiling Taylor UUCP * Testing the Compilation:: Testing the Compilation * Installing the Binaries:: Installing the Binaries * Configuration:: Configuring Taylor UUCP * Testing the Installation:: Testing the Installation Using Taylor UUCP * Calling Other Systems:: Calling Other Systems * Accepting Calls:: Accepting Calls * Mail and News:: Using UUCP for Mail and News * The Spool Directory Layout:: The Spool Directory Layout * Spool Directory Cleaning:: Cleaning the UUCP Spool Directory Using UUCP for Mail and News. * Sending mail or news:: Sending mail or news via UUCP * Receiving mail or news:: Receiving mail or news via UUCP The Spool Directory Layout * System Spool Directories:: System Spool Directories * Status Directory:: Status Spool Directory * Execution Subdirectories:: Execution Spool Subdirectories * Other Spool Subdirectories:: Other Spool Subdirectories * Spool Lock Files:: Spool Directory Lock Files Taylor UUCP Configuration Files * Configuration Overview:: Configuration File Overview * Configuration File Format:: Configuration File Format * Configuration Examples:: Examples of Configuration Files * Time Strings:: How to Write Time Strings * Chat Scripts:: How to Write Chat Scripts * config File:: The Main Configuration File * sys File:: The System Configuration File * port File:: The Port Configuration Files * dial File:: The Dialer Configuration Files * UUCP Over TCP:: UUCP Over TCP * Security:: Security Issues Examples of Configuration Files * config File Examples:: Examples of the Main Configuration File * Leaf Example:: Call a Single Remote Site * Gateway Example:: The Gateway for Several Local Systems The Main Configuration File * Miscellaneous (config):: Miscellaneous config File Commands * Configuration File Names:: Using Different Configuration Files * Log File Names:: Using Different Log Files * Debugging Levels:: Debugging Levels The System Configuration File * Defaults and Alternates:: Using Defaults and Alternates * Naming the System:: Naming the System * Calling Out:: Calling Out * Accepting a Call:: Accepting a Call * Protocol Selection:: Protocol Selection * File Transfer Control:: File Transfer Control * Miscellaneous (sys):: Miscellaneous sys File Commands * Default sys File Values:: Default Values Calling Out * When to Call:: When to Call * Placing the Call:: Placing the Call * Logging In:: Logging In UUCP Over TCP * TCP Client:: Connecting to Another System Over TCP * TCP Server:: Running a TCP Server UUCP Protocol Internals * UUCP Protocol Sources:: Sources for UUCP Protocol Information * UUCP Grades:: UUCP Grades * UUCP Lock Files:: UUCP Lock Files * Execution File Format:: Execution File Format * UUCP Protocol:: UUCP Protocol * g Protocol:: g protocol * f Protocol:: f protocol * t Protocol:: t protocol * e Protocol:: e protocol * Big G Protocol:: G protocol * i Protocol:: i protocol * j Protocol:: j protocol * x Protocol:: x protocol * y Protocol:: y protocol * d Protocol:: d protocol * h Protocol:: h protocol * v Protocol:: v protocol UUCP Protocol * The Initial Handshake:: The Initial Handshake * UUCP Protocol Commands:: UUCP Protocol Commands * The Final Handshake:: The Final Handshake UUCP Protocol Commands * The S Command:: The S Command * The R Command:: The R Command * The X Command:: The X Command * The E Command:: The E Command * The H Command:: The H Command Hacking Taylor UUCP * System Dependence:: System Dependence * Naming Conventions:: Naming Conventions * Patches:: Patches @end menu @node Copying, Introduction, Top, Top @unnumbered Taylor UUCP Copying Conditions This package is covered by the GNU Public License. See the file @file{COPYING} for details. If you would like to do something with this package that you feel is reasonable, but you feel is prohibited by the license, contact me to see if we can work it out. The rest of this section is some descriptive text from the Free Software Foundation. All the programs, scripts and documents relating to Taylor UUCP are @dfn{free}; this means that everyone is free to use them and free to redistribute them on a free basis. The Taylor UUCP-related programs are not in the public domain; they are copyrighted and there are restrictions on their distribution, but these restrictions are designed to permit everything that a good cooperating citizen would want to do. What is not allowed is to try to prevent others from further sharing any version of these programs that they might get from you. Specifically, we want to make sure that you have the right to give away copies of the programs that relate to Taylor UUCP, that you receive source code or else can get it if you want it, that you can change these programs or use pieces of them in new free programs, and that you know you can do these things. To make sure that everyone has such rights, we have to forbid you to deprive anyone else of these rights. For example, if you distribute copies of the Taylor UUCP related programs, 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 tell them their rights. Also, for our own protection, we must make certain that everyone finds out that there is no warranty for the programs that relate to Taylor UUCP. If these programs are modified by someone else and passed on, we want their recipients to know that what they have is not what we distributed, so that any problems introduced by others will not reflect on our reputation. The precise conditions of the licenses for the programs currently being distributed that relate to Taylor UUCP are found in the General Public Licenses that accompany them. @node Introduction, Invoking the UUCP Programs, Copying, Top @chapter Introduction to Taylor UUCP General introductions to UUCP are available, and perhaps one day I will write one. In the meantime, here is a very brief one that concentrates on the programs provided by Taylor UUCP. Taylor UUCP is a complete UUCP package. It is covered by the GNU Public License, which means that the source code is always available. It is composed of several programs; most of the names of these programs are based on earlier UUCP packages. @table @command @item uucp The @command{uucp} program is used to copy file between systems. It is similar to the standard Unix @command{cp} program, except that you can refer to a file on a remote system by using @samp{system!} before the file name. For example, to copy the file @file{notes.txt} to the system @samp{airs}, you would say @samp{uucp notes.txt airs!~/notes.txt}. In this example @samp{~} is used to name the UUCP public directory on @samp{airs}. For more details, see @ref{Invoking uucp, uucp}. @item uux The @command{uux} program is used to request the execution of a program on a remote system. This is how mail and news are transferred over UUCP. As with @command{uucp}, programs and files on remote systems may be named by using @samp{system!}. For example, to run the @command{rnews} program on @samp{airs}, passing it standard input, you would say @samp{uux - airs!rnews}. The @option{-} means to read standard input and set things up such that when @command{rnews} runs on @samp{airs} it will receive the same standard input. For more details, see @ref{Invoking uux, uux}. @end table Neither @command{uucp} nor @command{uux} actually do any work immediately. Instead, they queue up requests for later processing. They then start a daemon process which processes the requests and calls up the appropriate systems. Normally the system will also start the daemon periodically to check if there is any work to be done. The advantage of this approach is that it all happens automatically. You don't have to sit around waiting for the files to be transferred. The disadvantage is that if anything goes wrong it might be a while before anybody notices. @table @command @item uustat The @command{uustat} program does many things. By default it will simply list all the jobs you have queued with @command{uucp} or @command{uux} that have not yet been processed. You can use @command{uustat} to remove any of your jobs from the queue. You can also it use it to show the status of the UUCP system in various ways, such as showing the connection status of all the remote systems your system knows about. The system administrator can use @command{uustat} to automatically discard old jobs while sending mail to the user who requested them. For more details, see @ref{Invoking uustat, uustat}. @item uuname The @command{uuname} program by default lists all the remote systems your system knows about. You can also use it to get the name of your local system. It is mostly useful for shell scripts. For more details, see @ref{Invoking uuname, uuname}. @item uulog The @command{uulog} program can be used to display entries in the UUCP log file. It can select the entries for a particular system or a particular user. You can use it to see what has happened to your queued jobs in the past. For more details, see @ref{Invoking uulog, uulog}. @item uuto @item uupick @command{uuto} is a simple shell script interface to @command{uucp}. It will transfer a file, or the contents of a directory, to a remote system, and notify a particular user on the remote system when it arrives. The remote user can then retrieve the file(s) with @command{uupick}. For more details, see @ref{Invoking uuto, uuto}, and see @ref{Invoking uupick, uupick}. @item cu The @command{cu} program can be used to call up another system and communicate with it as though you were directly connected. It can also do simple file transfers, though it does not provide any error checking. For more details, @ref{Invoking cu, cu}. @end table These eight programs just described, @command{uucp}, @command{uux}, @command{uuto}, @command{uupick}, @command{uustat}, @command{uuname}, @command{uulog}, and @command{cu} are the user programs provided by Taylor UUCP@. @command{uucp}, @command{uux}, and @command{uuto} add requests to the work queue, @command{uupick} extracts files from the UUCP public directory, @command{uustat} examines the work queue, @command{uuname} examines the configuration files, @command{uulog} examines the log files, and @command{cu} just uses the UUCP configuration files. The real work is actually done by two daemon processes, which are normally run automatically rather than by a user. @table @command @item uucico The @command{uucico} daemon is the program which actually calls the remote system and transfers files and requests. @command{uucico} is normally started automatically by @command{uucp} and @command{uux}. Most systems will also start it periodically to make sure that all work requests are handled. @command{uucico} checks the queue to see what work needs to be done, and then calls the appropriate systems. If the call fails, perhaps because the phone line is busy, @command{uucico} leaves the requests in the queue and goes on to the next system to call. It is also possible to force @command{uucico} to call a remote system even if there is no work to be done for it, so that it can pick up any work that may be queued up remotely. For more details, see @ref{Invoking uucico, uucico}. @need 1000 @item uuxqt The @command{uuxqt} daemon processes execution requests made by the @command{uux} program on remote systems. It also processes requests made on the local system which require files from a remote system. It is normally started by @command{uucico}. For more details, see @ref{Invoking uuxqt, uuxqt}. @end table Suppose you, on the system @samp{bantam}, want to copy a file to the system @samp{airs}. You would run the @command{uucp} command locally, with a command like @samp{uucp notes.txt airs!~/notes.txt}. This would queue up a request on @samp{bantam} for @samp{airs}, and would then start the @command{uucico} daemon. @command{uucico} would see that there was a request for @samp{airs} and attempt to call it. When the call succeeded, another copy of @command{uucico} would be started on @samp{airs}. The two copies of @command{uucico} would tell each other what they had to do and transfer the file from @samp{bantam} to @samp{airs}. When the file transfer was complete the @command{uucico} on @samp{airs} would move it into the UUCP public directory. UUCP is often used to transfer mail. This is normally done automatically by mailer programs. When @samp{bantam} has a mail message to send to @samp{ian} at @samp{airs}, it executes @samp{uux - airs!rmail ian} and writes the mail message to the @command{uux} process as standard input. The @command{uux} program, running on @samp{bantam}, will read the standard input and store it, as well as the @command{rmail} request itself, on the work queue for @samp{airs}. @command{uux} will then start the @command{uucico} daemon. The @command{uucico} daemon will call up @samp{airs}, just as in the @command{uucp} example, and transfer the work request and the mail message. The @command{uucico} daemon on @samp{airs} will put the files on a local work queue. When the communication session is over, the @command{uucico} daemon on @samp{airs} will start the @command{uuxqt} daemon. @command{uuxqt} will see the request on the work queue, and will run @samp{rmail ian} with the mail message as standard input. The @command{rmail} program, which is not part of the UUCP package, is then responsible for either putting the message in the right mailbox on @samp{airs} or forwarding the message on to another system. Taylor UUCP comes with a few other programs that are useful when installing and configuring UUCP. @table @command @item uuchk The @command{uuchk} program reads the UUCP configuration files and displays a rather lengthy description of what it finds. This is useful when configuring UUCP to make certain that the UUCP package will do what you expect it to do. For more details, see @ref{Invoking uuchk, uuchk}. @item uuconv The @command{uuconv} program can be used to convert UUCP configuration files from one format to another. This can be useful for administrators converting from an older UUCP package. Taylor UUCP is able to read and use old configuration file formats, but some new features can not be selected using the old formats. For more details, see @ref{Invoking uuconv, uuconv}. @item uusched The @command{uusched} script is provided for compatibility with older UUCP releases. It starts @command{uucico} to call, one at a time, all the systems for which work has been queued. For more details, see @ref{Invoking uusched, uusched}. @item tstuu The @command{tstuu} program is a test harness for the UUCP package; it can help check that the package has been configured and compiled correctly. However, it uses pseudo-terminals, which means that it is less portable than the rest of the package. If it works, it can be useful when initially installing Taylor UUCP. For more details, see @ref{Testing the Compilation, tstuu}. @end table @node Invoking the UUCP Programs, Installing Taylor UUCP, Introduction, Top @chapter Invoking the UUCP Programs This chapter describes how to run the UUCP programs. @menu * Standard Options:: Standard Options for the UUCP Programs * Invoking uucp:: Invoking uucp * Invoking uux:: Invoking uux * Invoking uustat:: Invoking uustat * Invoking uuname:: Invoking uuname * Invoking uulog:: Invoking uulog * Invoking uuto:: Invoking uuto * Invoking uupick:: Invoking uupick * Invoking cu:: Invoking cu * Invoking uucico:: Invoking uucico * Invoking uuxqt:: Invoking uuxqt * Invoking uuchk:: Invoking uuchk * Invoking uuconv:: Invoking uuconv * Invoking uusched:: Invoking uusched @end menu @node Standard Options, Invoking uucp, Invoking the UUCP Programs, Invoking the UUCP Programs @section Standard Options All of the UUCP programs support a few standard options. @table @option @item -x type @itemx --debug type Turn on particular debugging types. The following types are recognized: @samp{abnormal}, @samp{chat}, @samp{handshake}, @samp{uucp-proto}, @samp{proto}, @samp{port}, @samp{config}, @samp{spooldir}, @samp{execute}, @samp{incoming}, @samp{outgoing}. Not all types of debugging are effective for all programs. See the @code{debug} configuration command for details (@pxref{Debugging Levels}). Multiple types may be given, separated by commas, and the @option{--debug} option may appear multiple times. A number may also be given, which will turn on that many types from the foregoing list; for example, @samp{--debug 2} is equivalent to @samp{--debug abnormal,chat}. To turn on all types of debugging, use @samp{-x all}. The @command{uulog} program uses @option{-X} rather than @option{-x} to select the debugging type; for @command{uulog}, @option{-x} has a different meaning, for reasons of historical compatibility. @item -I file @itemx --config file Set the main configuration file to use. @xref{config File}. When this option is used, the programs will revoke any setuid privileges. @item -v @itemx --version Report version information and exit. @item --help Print a help message and exit. @end table @need 2000 @node Invoking uucp, Invoking uux, Standard Options, Invoking the UUCP Programs @section Invoking uucp @menu * uucp Description:: Description of uucp * uucp Options:: Options Supported by uucp @end menu @node uucp Description, uucp Options, Invoking uucp, Invoking uucp @subsection uucp Description @example uucp [options] @file{source-file} @file{destination-file} uucp [options] @file{source-file}... @file{destination-directory} @end example The @command{uucp} command copies files between systems. Each @file{file} argument is either a file name on the local machine or is of the form @samp{system!file}. The latter is interpreted as being on a remote system. When @command{uucp} is used with two non-option arguments, the contents of the first file are copied to the second. With more than two non-option arguments, each source file is copied into the destination directory. A file may be transferred to or from @samp{system2} via @samp{system1} by using @samp{system1!system2!file}. Any file name that does not begin with @samp{/} or @samp{~} will be prepended with the current directory (unless the @option{-W} or @option{--noexpand} options are used). For example, if you are in the directory @samp{/home/ian}, then @samp{uucp foo remote!bar} is equivalent to @samp{uucp /home/ian/foo remote!/home/ian/bar}. Note that the resulting file name may not be valid on a remote system. A file name beginning with a simple @samp{~} starts at the UUCP public directory; a file name beginning with @samp{~name} starts at the home directory of the named user. The @samp{~} is interpreted on the appropriate system. Note that some shells will interpret an initial @samp{~} before @command{uucp} sees it; to avoid this the @samp{~} must be quoted. The shell metacharacters @samp{?} @samp{*} @samp{[} and @samp{]} are interpreted on the appropriate system, assuming they are quoted to prevent the shell from interpreting them first. The file copy does not take place immediately, but is queued up for the @command{uucico} daemon; the daemon is started immediately unless the @option{-r} or @option{--nouucico} option is given. The next time the remote system is called, the file(s) will be copied. @xref{Invoking uucico}. The file mode is not preserved, except for the execute bit. The resulting file is owned by the uucp user. @node uucp Options, , uucp Description, Invoking uucp @subsection uucp Options The following options may be given to @command{uucp}. @table @option @item -c @itemx --nocopy Do not copy local source files to the spool directory. If they are removed before being processed by the @command{uucico} daemon, the copy will fail. The files must be readable by the @command{uucico} daemon, and by the invoking user. @item -C @itemx --copy Copy local source files to the spool directory. This is the default. @item -d @itemx --directories Create all necessary directories when doing the copy. This is the default. @item -f @itemx --nodirectories If any necessary directories do not exist for the destination file name, abort the copy. @item -R @itemx --recursive If any of the source file names are directories, copy their contents recursively to the destination (which must itself be a directory). @item -g grade @itemx --grade grade Set the grade of the file transfer command. Jobs of a higher grade are executed first. Grades run @kbd{0} to @kbd{9}, @kbd{A} to @kbd{Z}, @kbd{a} to @kbd{z}, from high to low. @xref{When to Call}. @item -m @itemx --mail Report completion or failure of the file transfer by sending mail. @item -n user @itemx --notify user Report completion or failure of the file transfer by sending mail to the named user on the destination system. @item -r @itemx --nouucico Do not start the @command{uucico} daemon immediately; merely queue up the file transfer for later execution. @item -j @itemx --jobid Print the jobid on standard output. The job may be later cancelled by passing this jobid to the @option{-kill} switch of @command{uustat}. @xref{Invoking uustat}. It is possible for some complex operations to produce more than one jobid, in which case each will be printed on a separate line. For example @example uucp sys1!~user1/file1 sys2!~user2/file2 ~user3 @end example will generate two separate jobs, one for the system @samp{sys1} and one for the system @samp{sys2}. @item -W @itemx --noexpand Do not prepend remote relative file names with the current directory. @item -t @itemx --uuto This option is used by the @command{uuto} shell script; see @ref{Invoking uuto}. It causes @command{uucp} to interpret the final argument as @samp{system!user}. The file(s) are sent to @samp{~/receive/@var{user}/@var{local}} on the remote system, where @var{user} is from the final argument and @var{local} is the local UUCP system name. Also, @command{uucp} will act as though @option{--notify user} were specified. @item -x type @itemx --debug type @itemx -I file @itemx --config file @itemx -v @itemx --version @itemx --help @xref{Standard Options}. @end table @node Invoking uux, Invoking uustat, Invoking uucp, Invoking the UUCP Programs @section Invoking uux @menu * uux Description:: Description of uux * uux Options:: Options Supported by uux * uux Examples:: Examples of uux Usage @end menu @node uux Description, uux Options, Invoking uux, Invoking uux @subsection uux Description @example uux [options] command @end example The @command{uux} command is used to execute a command on a remote system, or to execute a command on the local system using files from remote systems. The command is not executed immediately; the request is queued until the @command{uucico} daemon calls the system and transfers the necessary files. The daemon is started automatically unless one of the @option{-r} or @option{--nouucico} options is given. The actual command execution is done by the @command{uuxqt} daemon on the appropriate system. File arguments can be gathered from remote systems to the execution system, as can standard input. Standard output may be directed to a file on a remote system. The command name may be preceded by a system name followed by an exclamation point if it is to be executed on a remote system. An empty system name is taken as the local system. Each argument that contains an exclamation point is treated as naming a file. The system which the file is on is before the exclamation point, and the file name on that system follows it. An empty system name is taken as the local system; this form must be used to transfer a file to a command being executed on a remote system. If the file name is not absolute, the current working directory will be prepended to it; the result may not be meaningful on the remote system. A file name may begin with @samp{~/}, in which case it is relative to the UUCP public directory on the appropriate system. A file name may begin with @samp{~name/}, in which case it is relative to the home directory of the named user on the appropriate system. Standard input and output may be redirected as usual; the file names used may contain exclamation points to indicate that they are on remote systems. Note that the redirection characters must be quoted so that they are passed to @command{uux} rather than interpreted by the shell. Append redirection (@samp{>>}) does not work. All specified files are gathered together into a single directory before execution of the command begins. This means that each file must have a distinct name. For example, @example uux 'sys1!diff sys2!~user1/foo sys3!~user2/foo >!foo.diff' @end example will fail because both files will be copied to @samp{sys1} and stored under the name @file{foo}. Arguments may be quoted by parentheses to avoid interpretation of exclamation points. This is useful when executing the @command{uucp} command on a remote system. Most systems restrict the commands which may be executed using @samp{uux}. Many permit only the execution of @samp{rmail} and @samp{rnews}. A request to execute an empty command (e.g., @samp{uux sys!}) will create a poll file for the specified system; see @ref{Calling Other Systems} for an example of why this might be useful. The exit status of @command{uux} is one of the codes found in the header file @file{sysexits.h}. In particular, @samp{EX_OK} (@samp{0}) indicates success, and @samp{EX_TEMPFAIL} (@samp{75}) indicates a temporary failure. @node uux Options, uux Examples, uux Description, Invoking uux @subsection uux Options The following options may be given to @command{uux}. @table @option @item - @itemx -p @itemx --stdin Read standard input up to end of file, and use it as the standard input for the command to be executed. @item -c @itemx --nocopy Do not copy local files to the spool directory. This is the default. If they are removed before being processed by the @command{uucico} daemon, the copy will fail. The files must be readable by the @command{uucico} daemon, as well as the by the invoker of @command{uux}. @item -C @itemx --copy Copy local files to the spool directory. @item -l @itemx --link Link local files into the spool directory. If a file can not be linked because it is on a different device, it will be copied unless one of the @option{-c} or @option{--nocopy} options also appears (in other words, use of @option{--link} switches the default from @option{--nocopy} to @option{--copy}). If the files are changed before being processed by the @command{uucico} daemon, the changed versions will be used. The files must be readable by the @command{uucico} daemon, as well as by the invoker of @command{uux}. @item -g grade @itemx --grade grade Set the grade of the file transfer command. Jobs of a higher grade are executed first. Grades run @kbd{0} to @kbd{9}, @kbd{A} to @kbd{Z}, @kbd{a} to @kbd{z}, from high to low. @xref{When to Call}. @item -n @itemx --notification=no Do not send mail about the status of the job, even if it fails. @item -z @itemx --notification=error Send mail about the status of the job if an error occurs. For many @command{uuxqt} daemons, including the Taylor UUCP @command{uuxqt}, this is the default action; for those, @option{--notification=error} will have no effect. However, some @command{uuxqt} daemons will send mail if the job succeeds, unless the @option{--notification=error} option is used. Some other @command{uuxqt} daemons will not send mail even if the job fails, unless the @option{--notification=error} option is used. @item -a address @itemx --requestor address Report job status, as controlled by the @option{--notification} option, to the specified mail address. @item -r @itemx --nouucico Do not start the @command{uucico} daemon immediately; merely queue up the execution request for later processing. @item -j @itemx --jobid Print the jobid on standard output. A jobid will be generated for each file copy operation required to execute the command. These file copies may be later cancelled by passing the jobid to the @option{-kill} switch of @command{uustat}. @xref{Invoking uustat}. Cancelling any file copies will make it impossible to complete execution of the job. @item -x type @itemx --debug type @itemx -v @itemx --version @itemx --help @xref{Standard Options}. @end table @node uux Examples, , uux Options, Invoking uux @subsection uux Examples Here are some examples of using @command{uux}. @example uux -z - sys1!rmail user1 @end example This will execute the command @samp{rmail user1} on the system @samp{sys1}, giving it as standard input whatever is given to @command{uux} as standard input. If a failure occurs, mail will be sent to the user who ran the command. @example uux 'diff -c sys1!~user1/file1 sys2!~user2/file2 >!file.diff' @end example This will fetch the two named files from system @samp{sys1} and system @samp{sys2} and execute @samp{diff}, putting the result in @file{file.diff} in the current directory on the local system. The current directory must be writable by the @command{uuxqt} daemon for this to work. @example uux 'sys1!uucp ~user1/file1 (sys2!~user2/file2)' @end example Execute @command{uucp} on the system @samp{sys1} copying @file{file1} (on system @samp{sys1}) to @samp{sys2}. This illustrates the use of parentheses for quoting. @node Invoking uustat, Invoking uuname, Invoking uux, Invoking the UUCP Programs @section Invoking uustat @menu * uustat Description:: Description of uustat * uustat Options:: Options Supported by uustat * uustat Examples:: Examples of uustat Usage @end menu @node uustat Description, uustat Options, Invoking uustat, Invoking uustat @subsection uustat Description @example uustat -a uustat --all uustat [-eKRiMNQ] [-sS system] [-uU user] [-cC command] [-oy hours] [-B lines] [--executions] [--kill-all] [--rejuvenate-all] [--prompt] [--mail] [--notify] [--no-list] [--system system] [--not-system system] [--user user] [--not-user user] [--command command] [--not-command command] [--older-than hours] [--younger-than hours] [--mail-lines lines] uustat [-kr jobid] [--kill jobid] [--rejuvenate jobid] uustat -q [-sS system] [-oy hours] [--system system] [--not-system system ] [--older-than hours] [--younger-than hours] uustat --list [-sS system] [-oy hours] [--system system ] [--not-system system] [--older-than hours] [--younger-than hours] uustat -m uustat --status uustat -p uustat --ps @end example The @command{uustat} command can display various types of status information about the UUCP system. It can also be used to cancel or rejuvenate requests made by @command{uucp} or @command{uux}. With no options, @command{uustat} displays all jobs queued up for the invoking user, as if given the @option{--user} option with the appropriate argument. If any of the @option{-a}, @option{--all}, @option{-e}, @option{--executions}, @option{-s}, @option{--system}, @option{-S}, @option{--not-system}, @option{-u}, @option{--user}, @option{-U}, @option{--not-user}, @option{-c}, @option{--command}, @option{-C}, @option{--not-command}, @option{-o}, @option{--older-than}, @option{-y}, or @option{--younger-than} options are given, then all jobs which match the combined specifications are displayed. The @option{-K} or @option{--kill-all} option may be used to kill off a selected group of jobs, such as all jobs more than 7 days old. @node uustat Options, uustat Examples, uustat Description, Invoking uustat @subsection uustat Options The following options may be given to @command{uustat}. @table @option @item -a @itemx --all List all queued file transfer requests. @item -e @itemx --executions List queued execution requests rather than queued file transfer requests. Queued execution requests are processed by @command{uuxqt} rather than @command{uucico}. Queued execution requests may be waiting for some file to be transferred from a remote system. They are created by an invocation of @command{uux}. @item -s system @itemx --system system List all jobs queued up for the named system. These options may be specified multiple times, in which case all jobs for all the named systems will be listed. If used with @option{--list}, only the systems named will be listed. @item -S system @itemx --not-system system List all jobs queued for systems other than the one named. These options may be specified multiple times, in which case no jobs from any of the specified systems will be listed. If used with @option{--list}, only the systems not named will be listed. These options may not be used with @option{-s} or @option{--system}. @item -u user @itemx --user user List all jobs queued up for the named user. These options may be specified multiple times, in which case all jobs for all the named users will be listed. @item -U user @itemx --not-user user List all jobs queued up for users other than the one named. These options may be specified multiple times, in which case no jobs from any of the specified users will be listed. These options may not be used with @option{-u} or @option{--user}. @item -c command @itemx --command command List all jobs requesting the execution of the named command. If @samp{command} is @samp{ALL} this will list all jobs requesting the execution of some command (as opposed to simply requesting a file transfer). These options may be specified multiple times, in which case all jobs requesting any of the commands will be listed. @item -C command @itemx --not-command command List all jobs requesting execution of some command other than the named command, or, if @samp{command} is @samp{ALL}, list all jobs that simply request a file transfer (as opposed to requesting the execution of some command). These options may be specified multiple times, in which case no job requesting one of the specified commands will be listed. These options may not be used with @option{-c} or @option{--command}. @item -o hours @itemx --older-than hours List all queued jobs older than the given number of hours. If used with @option{--list}, only systems whose oldest job is older than the given number of hours will be listed. @item -y hours @itemx --younger-than hours List all queued jobs younger than the given number of hours. If used with @option{--list}, only systems whose oldest job is younger than the given number of hours will be listed. @item -k jobid @itemx --kill jobid Kill the named job. The job id is shown by the default output format, as well as by the @option{-j} or @option{--jobid} options to @command{uucp} or @command{uux}. A job may only be killed by the user who created the job, or by the UUCP administrator, or the superuser. The @option{-k} or @option{--kill} options may be used multiple times on the command line to kill several jobs. @item -r jobid @itemx --rejuvenate jobid Rejuvenate the named job. This will mark it as having been invoked at the current time, affecting the output of the @option{-o}, @option{--older-than}, @option{-y}, or @option{--younger-than} options, possibly preserving it from any automated cleanup daemon. The job id is shown by the default output format, as well as by the @option{-j} or @option{--jobid} options to @command{uucp} or @command{uux}. A job may only be rejuvenated by the user who created the job, or by the UUCP administrator, or the superuser. The @option{-r} or @option{--rejuvenate} options may be used multiple times on the command line to rejuvenate several jobs. @item -q @itemx --list Display the status of commands, executions and conversations for all remote systems for which commands or executions are queued. The @option{-s}, @option{--system}, @option{-S}, @option{--not-system}, @option{-o}, @option{--older-than}, @option{-y}, and @option{--younger-than} options may be used to restrict the systems which are listed. Systems for which no commands or executions are queued will never be listed. @item -m @itemx --status Display the status of conversations for all remote systems. @item -p @itemx --ps Display the status of all processes holding UUCP locks on systems or ports. @need 500 @item -i @itemx --prompt For each listed job, prompt whether to kill the job or not. If the first character of the input line is @kbd{y} or @kbd{Y}, the job will be killed. @item -K @itemx --kill-all Automatically kill each listed job. This can be useful for automatic cleanup scripts, in conjunction with the @option{--mail} and @option{--notify} options. @item -R @itemx --rejuvenate-all Automatically rejuvenate each listed job. This may not be used with @option{--kill-all}. @item -M @itemx --mail For each listed job, send mail to the UUCP administrator. If the job is killed (due to @option{--kill-all}, or @option{--prompt} with an affirmative response) the mail will indicate that. A comment specified by the @option{--comment} option may be included. If the job is an execution, the initial portion of its standard input will be included in the mail message; the number of lines to include may be set with the @option{--mail-lines} option (the default is 100). If the standard input contains null characters, it is assumed to be a binary file and is not included. @item -N @itemx --notify For each listed job, send mail to the user who requested the job. The mail is identical to that sent by the @option{-M} or @option{--mail} options. @item -W comment @itemx --comment comment Specify a comment to be included in mail sent with the @option{-M}, @option{--mail}, @option{-N}, or @option{--notify} options. @item -B lines @itemx --mail-lines lines When the @option{-M}, @option{--mail}, @option{-N}, or @option{--notify} options are used to send mail about an execution with standard input, this option controls the number of lines of standard input to include in the message. The default is 100. @item -Q @itemx --no-list Do not actually list the job, but only take any actions indicated by the @option{-i}, @option{--prompt}, @option{-K}, @option{--kill-all}, @option{-M}, @option{--mail}, @option{-N} or @option{--notify} options. @item -x type @itemx --debug type @itemx -I file @itemx --config file @itemx -v @itemx --version @itemx --help @xref{Standard Options}. @end table @node uustat Examples, , uustat Options, Invoking uustat @subsection uustat Examples @example uustat --all @end example Display status of all jobs. A sample output line is as follows: @smallexample bugsA027h bugs ian 04-01 13:50 Executing rmail ian@@airs.com (sending 12 bytes) @end smallexample The format is @example jobid system user queue-date command (size) @end example The jobid may be passed to the @option{--kill} or @option{--rejuvenate} options. The size indicates how much data is to be transferred to the remote system, and is absent for a file receive request. The @option{--system}, @option{--not-system}, @option{--user}, @option{--not-user}, @option{--command}, @option{--not-command}, @option{--older-than}, and @option{--younger-than} options may be used to control which jobs are listed. @example uustat --executions @end example Display status of queued up execution requests. A sample output line is as follows: @smallexample bugs bugs!ian 05-20 12:51 rmail ian @end smallexample The format is @example system requestor queue-date command @end example The @option{--system}, @option{--not-system}, @option{--user}, @option{--not-user}, @option{--command}, @option{--not-command}, @option{--older-than}, and @option{--younger-than} options may be used to control which requests are listed. @example uustat --list @end example Display status for all systems with queued up commands. A sample output line is as follows: @smallexample bugs 4C (1 hour) 0X (0 secs) 04-01 14:45 Dial failed @end smallexample This indicates the system, the number of queued commands, the age of the oldest queued command, the number of queued local executions, the age of the oldest queued execution, the date of the last conversation, and the status of that conversation. @example uustat --status @end example Display conversation status for all remote systems. A sample output line is as follows: @smallexample bugs 04-01 15:51 Conversation complete @end smallexample This indicates the system, the date of the last conversation, and the status of that conversation. If the last conversation failed, @command{uustat} will indicate how many attempts have been made to call the system. If the retry period is currently preventing calls to that system, @command{uustat} also displays the time when the next call will be permitted. @example uustat --ps @end example Display the status of all processes holding UUCP locks. The output format is system dependent, as @command{uustat} simply invokes @command{ps} on each process holding a lock. @example uustat -c rmail -o 168 -K -Q -M -N -W "Queued for over 1 week" @end example This will kill all @samp{rmail} commands that have been queued up waiting for delivery for over 1 week (168 hours). For each such command, mail will be sent both to the UUCP administrator and to the user who requested the rmail execution. The mail message sent will include the string given by the @option{-W} option. The @option{-Q} option prevents any of the jobs from being listed on the terminal, so any output from the program will be error messages. @node Invoking uuname, Invoking uulog, Invoking uustat, Invoking the UUCP Programs @section Invoking uuname @example uuname [-a] [--aliases] uuname -l uuname --local @end example By default, the @command{uuname} program simply lists the names of all the remote systems mentioned in the UUCP configuration files. The @command{uuname} program may also be used to print the UUCP name of the local system. The @command{uuname} program is mainly for use by shell scripts. The following options may be given to @command{uuname}. @table @option @item -a @itemx --aliases List all aliases for remote systems, as well as their canonical names. Aliases may be specified in the @file{sys} file (@pxref{Naming the System}). @item -l @itemx --local Print the UUCP name of the local system, rather than listing the names of all the remote systems. @item -x type @itemx --debug type @itemx -I file @itemx --config file @itemx -v @itemx --version @itemx --help @xref{Standard Options}. @end table @node Invoking uulog, Invoking uuto, Invoking uuname, Invoking the UUCP Programs @section Invoking uulog @example uulog [-#] [-n lines] [-sf system] [-u user] [-DSF] [--lines lines] [--system system] [--user user] [--debuglog] [--statslog] [--follow] [--follow=system] @end example The @command{uulog} program may be used to display the UUCP log file. Different options may be used to select which parts of the file to display. @table @option @item -# @itemx -n lines @itemx --lines lines Here @samp{#} is a number; e.g., @option{-10}. The specified number of lines is displayed from the end of the log file. The default is to display the entire log file, unless the @option{-f}, @option{-F}, or @option{--follow} options are used, in which case the default is to display 10 lines. @item -s system @itemx --system system Display only log entries pertaining to the specified system. @item -u user @itemx --user user Display only log entries pertaining to the specified user. @item -D @itemx --debuglog Display the debugging log file. @item -S @itemx --statslog Display the statistics log file. @item -F @itemx --follow Keep displaying the log file forever, printing new lines as they are appended to the log file. @item -f system @itemx --follow=system Keep displaying the log file forever, displaying only log entries pertaining to the specified system. @item -X type @itemx --debug type @itemx -I file @itemx --config file @itemx -v @itemx --version @itemx --help @xref{Standard Options}. Note that @command{uulog} specifies the debugging type using @option{-X} rather than the usual @option{-x}. @end table The operation of @command{uulog} depends to some degree upon the type of log files generated by the UUCP programs. This is a compile time option. If the UUCP programs have been compiled to use HDB style log files, @command{uulog} changes in the following ways: @itemize @bullet @item The new options @option{-x} and @option{--uuxqtlog} may be used to list the @command{uuxqt} log file. @item It is no longer possible to omit all arguments: one of @option{-s}, @option{--system}, @option{-f}, @option{--follow=system}, @option{-D}, @option{--debuglog}, @option{-S}, @option{--statslog}, @option{-x}, or @option{--uuxqtlog} must be used. @item The option @option{--system ANY} may be used to list log file entries which do not pertain to any particular system. @end itemize @node Invoking uuto, Invoking uupick, Invoking uulog, Invoking the UUCP Programs @section Invoking uuto @example uuto files... system!user @end example The @command{uuto} program may be used to conveniently send files to a particular user on a remote system. It will arrange for mail to be sent to the remote user when the files arrive on the remote system, and he or she may easily retrieve the files using the @command{uupick} program (@pxref{Invoking uupick}). Note that @command{uuto} does not provide any security---any user on the remote system can examine the files. The last argument specifies the system and user name to which to send the files. The other arguments are the files or directories to be sent. The @command{uuto} program is actually just a trivial shell script which invokes the @command{uucp} program with the appropriate arguments. Any option which may be given to @command{uucp} may also be given to @command{uuto}. @xref{Invoking uucp}. @need 2000 @node Invoking uupick, Invoking cu, Invoking uuto, Invoking the UUCP Programs @section Invoking uupick @example uupick [-s system] [--system system] @end example The @command{uupick} program is used to conveniently retrieve files transferred by the @command{uuto} program. For each file transferred by @command{uuto}, @command{uupick} will display the source system, the file name, and whether the name refers to a regular file or a directory. It will then wait for the user to specify an action to take. One of the following commands must be entered: @table @samp @item q Quit out of @command{uupick}. @item RETURN Skip the file. @item m [directory] Move the file or directory to the specified directory. If no directory is specified, the file is moved to the current directory. @item a [directory] Move all files from this system to the specified directory. If no directory is specified, the files are moved to the current directory. @item p List the file on standard output. @item d Delete the file. @item ! [command] Execute @samp{command} as a shell escape. @end table The @option{-s} or @option{--system} option may be used to restrict @command{uupick} to only present files transferred from a particular system. The @command{uupick} program also supports the standard UUCP program options; see @ref{Standard Options}. @need 2000 @node Invoking cu, Invoking uucico, Invoking uupick, Invoking the UUCP Programs @section Invoking cu @menu * cu Description:: Description of cu * cu Commands:: Commands Supported by cu * cu Variables:: Variables Supported by cu * cu Options:: Options Supported by cu @end menu @node cu Description, cu Commands, Invoking cu, Invoking cu @subsection cu Description @example cu [options] [system | phone | "dir"] @end example The @command{cu} program is used to call up another system and act as a dial in terminal. It can also do simple file transfers with no error checking. The @command{cu} program takes a single non-option argument. If the argument is the string @samp{dir} cu will make a direct connection to the port. This may only be used by users with write access to the port, as it permits reprogramming the modem. Otherwise, if the argument begins with a digit, it is taken to be a phone number to call. Otherwise, it is taken to be the name of a system to call. The @option{-z} or @option{--system} options may be used to name a system beginning with a digit, and the @option{-c} or @option{--phone} options may be used to name a phone number that does not begin with a digit. The @command{cu} program locates a port to use in the UUCP configuration files. If a simple system name is given, it will select a port appropriate for that system. The @option{-p}, @option{--port}, @option{-l}, @option{--line}, @option{-s}, and @option{--speed} options may be used to control the port selection. When a connection is made to the remote system, @command{cu} forks into two processes. One reads from the port and writes to the terminal, while the other reads from the terminal and writes to the port. @node cu Commands, cu Variables, cu Description, Invoking cu @subsection cu Commands The @command{cu} program provides several commands that may be used during the conversation. The commands all begin with an escape character, which by default is @kbd{~} (tilde). The escape character is only recognized at the beginning of a line. To send an escape character to the remote system at the start of a line, it must be entered twice. All commands are either a single character or a word beginning with @kbd{%} (percent sign). The @command{cu} program recognizes the following commands. @table @samp @item ~. Terminate the conversation. @item ~! command Run command in a shell. If command is empty, starts up a shell. @item ~$ command Run command, sending the standard output to the remote system. @item ~| command Run command, taking the standard input from the remote system. @item ~+ command Run command, taking the standard input from the remote system and sending the standard output to the remote system. @item ~#, ~%break Send a break signal, if possible. @item ~c directory, ~%cd directory Change the local directory. @item ~> file Send a file to the remote system. This just dumps the file over the communication line. It is assumed that the remote system is expecting it. @item ~< Receive a file from the remote system. This prompts for the local file name and for the remote command to execute to begin the file transfer. It continues accepting data until the contents of the @samp{eofread} variable are seen. @item ~p from to @itemx ~%put from to Send a file to a remote Unix system. This runs the appropriate commands on the remote system. @item ~t from to @itemx ~%take from to Retrieve a file from a remote Unix system. This runs the appropriate commands on the remote system. @item ~s variable value Set a @command{cu} variable to the given value. If value is not given, the variable is set to @samp{true}. @item ~! variable Set a @command{cu} variable to @samp{false}. @item ~z Suspend the cu session. This is only supported on some systems. On systems for which @kbd{^Z} may be used to suspend a job, @samp{~^Z} will also suspend the session. @item ~%nostop Turn off XON/XOFF handling. @item ~%stop Turn on XON/XOFF handling. @item ~v List all the variables and their values. @item ~? List all commands. @end table @node cu Variables, cu Options, cu Commands, Invoking cu @subsection cu Variables The @command{cu} program also supports several variables. They may be listed with the @samp{~v} command, and set with the @samp{~s} or @samp{~!} commands. @table @samp @item escape The escape character. The default is @kbd{~} (tilde). @item delay If this variable is true, @command{cu} will delay for a second, after recognizing the escape character, before printing the name of the local system. The default is true. @item eol The list of characters which are considered to finish a line. The escape character is only recognized after one of these is seen. The default is @kbd{carriage return}, @kbd{^U}, @kbd{^C}, @kbd{^O}, @kbd{^D}, @kbd{^S}, @kbd{^Q}, @kbd{^R}. @item binary Whether to transfer binary data when sending a file. If this is false, then newlines in the file being sent are converted to carriage returns. The default is false. @item binary-prefix A string used before sending a binary character in a file transfer, if the @samp{binary} variable is true. The default is @samp{^V}. @item echo-check Whether to check file transfers by examining what the remote system echoes back. This probably doesn't work very well. The default is false. @item echonl The character to look for after sending each line in a file. The default is carriage return. @item timeout The timeout to use, in seconds, when looking for a character, either when doing echo checking or when looking for the @samp{echonl} character. The default is 30. @item kill The character to use delete a line if the echo check fails. The default is @kbd{^U}. @item resend The number of times to resend a line if the echo check continues to fail. The default is 10. @item eofwrite The string to write after sending a file with the @samp{~>} command. The default is @samp{^D}. @item eofread The string to look for when receiving a file with the @samp{ ~<} command. The default is @samp{$}, which is intended to be a typical shell prompt. @item verbose Whether to print accumulated information during a file transfer. The default is true. @end table @node cu Options, , cu Variables, Invoking cu @subsection cu Options The following options may be given to @command{cu}. @table @option @item -e @itemx --parity=even Use even parity. @item -o @itemx --parity=odd Use odd parity. @item --parity=none Use no parity. No parity is also used if both @option{-e} and @option{-o} are given. @item -h @itemx --halfduplex Echo characters locally (half-duplex mode). @item --nostop Turn off XON/XOFF handling (it is on by default). @item -E char @itemx --escape char Set the escape character. Initially @kbd{~} (tilde). To eliminate the escape character, use @samp{-E ''}. @item -z system @itemx --system system The system to call. @item -c phone-number @itemx --phone phone-number The phone number to call. @item -p port @itemx -a port @itemx --port port Name the port to use. @item -l line @itemx --line line Name the line to use by giving a device name. This may be used to dial out on ports that are not listed in the UUCP configuration files. Write access to the device is required. @item -s speed @itemx -# @itemx --speed speed The speed (baud rate) to use. Here, @option{-#} means an actual number; e.g., @option{-9600}. @item -n @itemx --prompt Prompt for the phone number to use. @item -d Enter debugging mode. Equivalent to @option{--debug all}. @item -x type @itemx --debug type @itemx -I file @itemx --config file @itemx -v @itemx --version @itemx --help @xref{Standard Options}. @end table @node Invoking uucico, Invoking uuxqt, Invoking cu, Invoking the UUCP Programs @section Invoking uucico @menu * uucico Description:: Description of uucico * uucico Options:: Options Supported by uucico @end menu @node uucico Description, uucico Options, Invoking uucico, Invoking uucico @subsection uucico Description @example uucico [options] @end example The @command{uucico} daemon processes file transfer requests queued by @command{uucp} and @command{uux}. It is started when @command{uucp} or @command{uux} is run (unless they are given the @option{-r} or @option{--nouucico} options). It is also typically started periodically using entries in the @file{crontab} table(s). When @command{uucico} is invoked with @option{-r1}, @option{--master}, @option{-s}, @option{--system}, or @option{-S}, the daemon will place a call to a remote system, running in master mode. Otherwise the daemon will start in slave mode, accepting a call from a remote system. Typically a special login name will be set up for UUCP which automatically invokes @command{uucico} when a remote system calls in and logs in under that name. When @command{uucico} terminates, it invokes the @command{uuxqt} daemon, unless the @option{-q} or @option{--nouuxqt} options were given; @command{uuxqt} executes any work orders created by @command{uux} on a remote system, and any work orders created locally which have received remote files for which they were waiting. If a call fails, @command{uucico} will normally refuse to retry the call until a certain (configurable) amount of time has passed. This may be overriden by the @option{-f}, @option{--force}, or @option{-S} options. The @option{-l}, @option{--prompt}, @option{-e}, or @option{--loop} options may be used to force @command{uucico} to produce its own prompts of @samp{login: } and @samp{Password:}. When another @command{uucico} daemon calls in, it will see these prompts and log in as usual. The login name and password will normally be checked against a separate list kept specially for @command{uucico}, rather than the @file{/etc/passwd} file (@pxref{Configuration File Names}). It is possible, on some systems, to configure @command{uucico} to use @file{/etc/passwd}. The @option{-l} or @option{--prompt} options will prompt once and then exit; in this mode the UUCP administrator, or the superuser, may use the @option{-u} or @option{--login} option to force a login name, in which case @command{uucico} will not prompt for one. The @option{-e} or @option{--loop} options will prompt again after the first session is over; in this mode @command{uucico} will permanently control a port. If @command{uucico} receives a @code{SIGQUIT}, @code{SIGTERM} or @code{SIGPIPE} signal, it will cleanly abort any current conversation with a remote system and exit. If it receives a @code{SIGHUP} signal it will abort any current conversation, but will continue to place calls to (if invoked with @option{-r1} or @option{--master}) and accept calls from (if invoked with @option{-e} or @option{--loop}) other systems. If it receives a @code{SIGINT} signal it will finish the current conversation, but will not place or accept any more calls. @node uucico Options, , uucico Description, Invoking uucico @subsection uucico Options The following options may be given to @command{uucico}. @table @option @item -r1 @itemx --master Start in master mode: call out to a remote system. Implied by @option{-s}, @option{--system}, or @option{-S}. If no system is specified, sequentially call every system for which work is waiting to be done. @item -r0 @itemx --slave Start in slave mode. This is the default. @item -s system @itemx --system system Call the specified system. @item -S system Call the specified system, ignoring any required wait. This is equivalent to @samp{-s system -f}. @item -f @itemx --force Ignore any required wait for any systems to be called. @item -l @itemx --prompt Prompt for login name and password using @samp{login: } and @samp{Password:}. This allows @command{uucico} to be easily run from @command{inetd}. The login name and password are checked against the UUCP password file, which need not be @file{/etc/passwd}. The @option{--login} option may be used to force a login name, in which cause @command{uucico} will only prompt for a password. @item -p port @itemx --port port Specify a port to call out on or to listen to. @item -e @itemx --loop Enter an endless loop of login/password prompts and slave mode daemon execution. The program will not stop by itself; you must use @command{kill} to shut it down. @item -w @itemx --wait After calling out (to a particular system when @option{-s}, @option{--system}, or @option{-S} is specifed, or to all systems which have work when just @option{-r1} or @option{--master} is specifed), begin an endless loop as with @option{--loop}. @item -q @itemx --nouuxqt Do not start the @command{uuxqt} daemon when finished. @item -c @itemx --quiet If no calls are permitted at this time, then don't make the call, but also do not put an error message in the log file and do not update the system status (as reported by @command{uustat}). This can be convenient for automated polling scripts, which may want to simply attempt to call every system rather than worry about which particular systems may be called at the moment. This option also suppresses the log message indicating that there is no work to be done. @item -C @itemx --ifwork Only call the system named by @option{-s}, @option{--system}, or @option{-S} if there is work for that system. @item -D @itemx --nodetach Do not detach from the controlling terminal. Normally @command{uucico} detaches from the terminal before each call out to another system and before invoking @command{uuxqt}. This option prevents this. @item -u name @itemx --login name Set the login name to use instead of that of the invoking user. This option may only be used by the UUCP administrator or the superuser. If used with @option{--prompt}, this will cause @command{uucico} to prompt only for the password, not the login name. @item -z @itemx --try-next If a call fails after the remote system is reached, try the next alternate rather than simply exiting. @item -i type @itemx --stdin type Set the type of port to use when using standard input. The only supported port type is TLI, and this is only available on machines which support the TLI networking interface. Specifying @samp{-i TLI} causes @command{uucico} to use TLI calls to perform I/O. @item -X type Same as the standard option @option{-x type}. Provided for historical compatibility. @item -x type @itemx --debug type @itemx -I file @itemx --config file @itemx -v @itemx --version @itemx --help @xref{Standard Options}. @end table @node Invoking uuxqt, Invoking uuchk, Invoking uucico, Invoking the UUCP Programs @section Invoking uuxqt @example uuxqt [-c command] [-s system] [--command command] [--system system] @end example The @command{uuxqt} daemon executes commands requested by @command{uux} from either the local system or from remote systems. It is started automatically by the @command{uucico} daemon (unless @command{uucico} is given the @option{-q} or @option{--nouuxqt} options). There is normally no need to run @command{uuxqt}, since it will be invoked by @command{uucico}. However, @command{uuxqt} can be invoked directly to provide greater control over the processing of the work queue. Multiple invocations of @command{uuxqt} may be run at once, as controlled by the @code{max-uuxqts} configuration command; see @ref{Miscellaneous (config)}. The following options may be given to @command{uuxqt}. @table @option @item -c command @itemx --command command Only execute requests for the specified command. For example, @samp{uuxqt --command rmail}. @item -s system @itemx --system system Only execute requests originating from the specified system. @item -x type @itemx --debug type @itemx -I file @itemx --config @itemx -v @itemx --version @itemx --help @xref{Standard Options}. @end table @node Invoking uuchk, Invoking uuconv, Invoking uuxqt, Invoking the UUCP Programs @section Invoking uuchk @example uuchk [-s system] [--system system] @end example The @command{uuchk} program displays information read from the UUCP configuration files. It should be used to ensure that UUCP has been configured correctly. The @option{-s} or @option{--system} options may be used to display the configuration for just the specified system, rather than for all systems. The @command{uuchk} program also supports the standard UUCP program options; see @ref{Standard Options}. @need 2000 @node Invoking uuconv, Invoking uusched, Invoking uuchk, Invoking the UUCP Programs @section Invoking uuconv @example uuconv -i type -o type [-p program] [--program program] uuconv --input type --output type [-p program] [--program program] @end example The @command{uuconv} program converts UUCP configuration files from one format to another. The type of configuration file to read is specified using the @option{-i} or @option{--input} options. The type of configuration file to write is specified using the @option{-o} or @option{--output} options. The supported configuration file types are @samp{taylor}, @samp{v2}, and @samp{hdb}. For a description of the @samp{taylor} configuration files, see @ref{Configuration Files}. The other types of configuration files are used by traditional UUCP packages, and are not described in this manual. An input configuration of type @samp{v2} or @samp{hdb} is read from a compiled in directory (specified by @samp{oldconfigdir} in @file{Makefile}). An input configuration of type @samp{taylor} is read from a compiled in directory by default, but may be overridden with the standard @option{-I} or @option{--config} options (@pxref{Standard Options}). The output configuration is written to files in the directory in which @command{uuconv} is run. Some information in the input files may not be representable in the desired output format, in which case @command{uuconv} will silently discard it. The output of @command{uuconv} should be carefully checked before it is used. The @command{uuchk} program may be used for this purpose; see @ref{Invoking uuchk}. The @option{-p} or @option{--program} option may be used to convert specific @command{cu} configuration information, rather than the default of only converting the @command{uucp} configuration information; see @ref{config File}. The @command{uuchk} program also supports the standard UUCP program options; see @ref{Standard Options}. @node Invoking uusched, , Invoking uuconv, Invoking the UUCP Programs @section Invoking uusched The @command{uusched} program is actually just a shell script which invokes the @command{uucico} daemon. It is provided for backward compatibility. It causes @command{uucico} to call all systems for which there is work. Any option which may be given to @command{uucico} may also be given to @command{uusched}. @xref{Invoking uucico}. @node Installing Taylor UUCP, Using Taylor UUCP, Invoking the UUCP Programs, Top @chapter Installing Taylor UUCP These are the installation instructions for the Taylor UUCP package. @menu * Compilation:: Compiling Taylor UUCP * Testing the Compilation:: Testing the Compilation * Installing the Binaries:: Installing the Binaries * Configuration:: Configuring Taylor UUCP * Testing the Installation:: Testing the Installation @end menu @node Compilation, Testing the Compilation, Installing Taylor UUCP, Installing Taylor UUCP @section Compiling Taylor UUCP If you have a source code distribution, you must first compile it for your system. Free versions of Unix, such as Linux, NetBSD, or FreeBSD, often come with pre-compiled binary distributions of UUCP. If you are using a binary distribution, you may skip to the configuration section (@pxref{Configuration}). Follow these steps to compile the source code. @enumerate @item Take a look at the top of @file{Makefile.in} and set the appropriate values for your system. These control where the programs are installed and which user on the system owns them (normally they will be owned by a special user @command{uucp} rather than a real person; they should probably not be owned by @code{root}). @item Run the shell script @code{configure}. This script was generated using the @command{autoconf} program written by David MacKenzie of the Free Software Foundation. It takes a while to run. It will generate the file @file{config.h} based on @file{config.h.in}, and, for each source code directory, will generate @file{Makefile} based on @file{Makefile.in}. You can pass certain arguments to @code{configure} in the environment. Because @code{configure} will compile little test programs to see what is available on your system, you must tell it how to run your compiler. It recognizes the following environment variables: @table @samp @item CC The C compiler. If this is not set, then if @code{configure} can find @samp{gcc} it will use it, otherwise it will use @samp{cc}. @item CFLAGS Flags to pass to the C compiler when compiling the actual code. If this is not set, @code{configure} will use @option{-g}. @item LDFLAGS Flags to pass to the C compiler when only linking, not compiling. If this is not set, @code{configure} will use the empty string. @item LIBS Libraries to pass to the C compiler. If this is not set, @code{configure} will use the empty string. @item INSTALL The program to run to install UUCP in the binary directory. If this is not set, then if @code{configure} finds the BSD @command{install} program, it will set this to @samp{install -c}; otherwise, it will use @samp{cp}. @end table Suppose, for example, you want to set the environment variable @samp{CC} to @samp{rcc}. If you are using @command{sh}, @command{bash}, or @command{ksh}, invoke @code{configure} as @samp{CC=rcc configure}. If you are using @command{csh}, do @samp{setenv CC rcc; sh configure}. On some systems you will want to use @samp{LIBS=-lmalloc}. On Xenix derived versions of Unix do not use @samp{LIBS=-lx} because this will bring in the wrong versions of certain routines; if you want to use @option{-lx} you must specify @samp{LIBS=-lc -lx}. You can also pass other arguments to @code{configure} on the command line. Use @samp{configure --help} for a complete list. Of particular interest: @table @option @item --prefix=@var{dirname} The directory under which all files are installed. Default @file{/usr/local}. @item --with-newconfigdir=@var{dirname} The directory in which to find new style configuration files. Default @file{@var{prefix}/conf/uucp}. @item --with-oldconfigdir=@var{dirname} The directory in which to find old style configuration files. Default @file{/usr/lib/uucp}. @end table If @code{configure} fails for some reason, or if you have a very weird system, you may have to configure the package by hand. To do this, copy the file @file{config.h.in} to @file{config.h} and edit it for your system. Then for each source directory (the top directory, and the subdirectories @file{lib}, @file{unix}, and @file{uuconf}) copy @file{Makefile.in} to @file{Makefile}, find the words within @kbd{@@} characters, and set them correctly for your system. @item Igor V. Semenyuk provided this (lightly edited) note about ISC Unix 3.0. The @code{configure} script will default to passing @option{-posix} to @command{gcc}. However, using @option{-posix} changes the environment to POSIX, and on ISC 3.0, at least, the default for @code{POSIX_NO_TRUNC} is 1. This can lead to a problem when @command{uuxqt} executes @command{rmail}. @code{IDA sendmail} has dbm configuration files named @file{mailertable.@{dir,pag@}}. Notice these names are 15 characters long. When @command{uuxqt} compiled with the @option{-posix} executes @command{rmail}, which in turn executes @command{sendmail}, the later is run under the POSIX environment too. This leads to @command{sendmail} bombing out with @samp{'error opening 'M' database: name too long' (mailertable.dir)}. It's rather obscure behaviour, and it took me a day to find out the cause. I don't use the @option{-posix} switch; instead, I run @command{gcc} with @option{-D_POSIX_SOURCE}, and add @option{-lcposix} to @samp{LIBS}. @item On some versions of BSDI there is a bug in the shell which causes the default value for @samp{CFLAGS} to be set incorrectly. If @samp{echo $@{CFLAGS--g@}} echoes @samp{g} rather than @option{-g}, then you must set @samp{CFLAGS} in the environment before running configure. There is a patch available from BSDI for this bug. (Reported by David Vrona). @item On AIX 3.2.5, and possibly other versions, @samp{cc -E} does not work, reporting @samp{Option NOROCONST is not valid}. Test this before running configure by doing something like @samp{touch /tmp/foo.c; cc -E /tmp/foo.c}. This may give a warning about the file being empty, but it should not give the @samp{Option NOROCONST} warning. The workaround is to remove the @samp{,noroconst} entry from the @samp{options} clause in the @samp{cc} stanza in @file{/etc/xlc.cfg}. (Reported by Chris Lewis). @item You should verify that @code{configure} worked correctly by checking @file{config.h} and the instances of @file{Makefile}. @item Edit @file{policy.h} for your local system. The comments explain the various choices. The default values are intended to be reasonable, so you may not have to make any changes. You must decide what type of configuration files to use; for more information on the choices, see @ref{Configuration}. You must also decide what sort of spool directory you want to use. If this is a new installation, I recommend @samp{SPOOLDIR_TAYLOR}; otherwise, select the spool directory corresponding to your existing UUCP package. @item Type @samp{make} to compile everything. The @file{tstuu.c} file is not particularly portable; if you can't figure out how to compile it you can safely ignore it, as it is only used for testing. To use STREAMS pseudo-terminals, tstuu.c must be compiled with @option{-DHAVE_STREAMS_PTYS}; this is not determined by the configure script. If you have any other problems there is probably a bug in the @code{configure} script. @item Please report any problems you have. That is the only way they will get fixed for other people. Supply a patch if you can (@pxref{Patches}), or just ask for help. @end enumerate @node Testing the Compilation, Installing the Binaries, Compilation, Installing Taylor UUCP @section Testing the Compilation If your system supports pseudo-terminals, and you compiled the code to support the new style of configuration files (@code{HAVE_TAYLOR_CONFIG} was set to 1 in @file{policy.h}), you should be able to use the @command{tstuu} program to test the @command{uucico} daemon. If your system supports STREAMS based pseudo-terminals, you must compile tstuu.c with @option{-DHAVE_STREAMS_PTYS}. (The STREAMS based code was contributed by Marc Boucher). To run @command{tstuu}, just type @samp{tstuu} with no arguments. You must run it in the compilation directory, since it runs @file{./uucp}, @file{./uux} and @file{./uucico}. The @command{tstuu} program will run a lengthy series of tests (it takes over ten minutes on a slow VAX). You will need a fair amount of space available in @file{/usr/tmp}. You will probably want to put it in the background. Do not use @kbd{^Z}, because the program traps on @code{SIGCHLD} and winds up dying. The @command{tstuu} program will create a directory @file{/usr/tmp/tstuu} and fill it with configuration files, and create spool directories @file{/usr/tmp/tstuu/spool1} and @file{/usr/tmp/tstuu/spool2}. If your system does not support the @code{FIONREAD} call, the @samp{tstuu} program will run very slowly. This may or may not get fixed in a later version. The @command{tstuu} program will finish with an execute file named @file{X.@var{something}} and a data file named @file{D.@var{something}} in the directory @file{/usr/tmp/tstuu/spool1} (or, more likely, in subdirectories, depending on the choice of @code{SPOOLDIR} in @file{policy.h}). Two log files will be created in the directory @file{/usr/tmp/tstuu}. They will be named @file{Log1} and @file{Log2}, or, if you have selected @code{HAVE_HDB_LOGGING} in @file{policy.h}, @file{Log1/uucico/test2} and @file{Log2/uucico/test1}. There should be no errors in the log files. You can test @command{uuxqt} with @samp{./uuxqt -I /usr/tmp/tstuu/Config1}. This should leave a command file @file{C.@var{something}} and a data file @file{D.@var{something}} in @file{/usr/tmp/tstuu/spool1} or in subdirectories. Again, there should be no errors in the log file. Assuming you compiled the code with debugging enabled, the @option{-x} switch can be used to set debugging modes; see the @code{debug} command for details (@pxref{Debugging Levels}). Use @option{-x all} to turn on all debugging and generate far more output than you will ever want to see. The @command{uucico} daemons will put debugging output in the files @file{Debug1} and @file{Debug2} in the directory @file{/usr/tmp/tstuu}. After that, you're pretty much on your own. On some systems you can also use @command{tstuu} to test @command{uucico} against the system @command{uucico}, by using the @option{-u} switch. For this to work, change the definitions of @code{ZUUCICO_CMD} and @code{UUCICO_EXECL} at the top of @file{tstuu.c} to something appropriate for your system. The definitions in @file{tstuu.c} are what I used for Ultrix 4.0, on which @file{/usr/lib/uucp/uucico} is particularly obstinate about being run as a child; I was only able to run it by creating a login name with no password whose shell was @file{/usr/lib/uucp/uucico}. Calling login in this way will leave fake entries in @file{wtmp} and @file{utmp}; if you compile @file{tstout.c} (in the @file{contrib} directory) as a setuid @code{root} program, @command{tstuu} will run it to clear those entries out. On most systems, such hackery should not be necessary, although on SCO I had to su to @code{root} (@code{uucp} might also have worked) before I could run @file{/usr/lib/uucp/uucico}. You can test @command{uucp} and @command{uux} (give them the @option{-r} switch to keep them from starting @command{uucico}) to make sure they create the right sorts of files. Unfortunately, if you don't know what the right sorts of files are, I'm not going to tell you here. If you can not run @command{tstuu}, or if it fails inexplicably, don't worry about it too much. On some systems @command{tstuu} will fail because of problems using pseudo terminals, which will not matter in normal use. The real test of the package is talking to another system. @node Installing the Binaries, Configuration, Testing the Compilation, Installing Taylor UUCP @section Installing the Binaries You can install the executable files by becoming @code{root} and typing @samp{make install}. Or you can look at what @samp{make install} does and do it by hand. It tries to preserve your old programs, if any, but it only does this the first time Taylor UUCP is installed (so that if you install several versions of Taylor UUCP, you can still go back to your original UUCP programs). You can retrieve the original programs by typing @samp{make uninstall}. Note that by default the programs are compiled with debugging information, and they are not stripped when they are installed. You may want to strip the installed programs to save disk space. For more information, see your system documentation for the @command{strip} program. Of course, simply installing the executable files is not enough. You must also arrange for them to be used correctly. @node Configuration, Testing the Installation, Installing the Binaries, Installing Taylor UUCP @section Configuring Taylor UUCP You will have to decide what types of configuration files you want to use. This package supports a new sort of configuration file; see @ref{Configuration Files}. It also supports V2 configuration files (@file{L.sys}, @file{L-devices}, etc.) and HDB configuration files (@file{Systems}, @file{Devices}, etc.). No documentation is provided for V2 or HDB configuration files. All types of configuration files can be used at once, if you are so inclined. Currently using just V2 configuration files is not really possible, because there is no way to specify a dialer (there are no built in dialers, and the program does not know how to read @file{acucap} or @file{modemcap}); however, V2 configuration files can be used with a new style dial file (@pxref{dial File}), or with a HDB @file{Dialers} file. Use of HDB configuration files has two known bugs. A blank line in the middle of an entry in the @file{Permissions} file will not be ignored as it should be. Dialer programs, as found in some versions of HDB, are not recognized directly. If you must use a dialer program, rather than an entry in @file{Devices}, you must use the @code{chat-program} command in a new style dial file; see @ref{dial File}. You will have to invoke the dialer program via a shell script or another program, since an exit code of 0 is required to recognize success; the @code{dialHDB} program in the @file{contrib} directory may be used for this purpose. The @command{uuconv} (@pxref{Invoking uuconv}) program can be used to convert from V2 or HDB configuration files to the new style (it can also do the reverse translation, if you are so inclined). It will not do all of the work, and the results should be carefully checked, but it can be quite useful. If you are installing a new system, you will, of course, have to write the configuration files; see @ref{Configuration Files} for details on how to do this. After writing the configuration files, use the @command{uuchk} program to verify that they are what you expect; see @ref{Invoking uuchk}. @node Testing the Installation, , Configuration, Installing Taylor UUCP @section Testing the Installation After you have written the configuration files, and verified them with the @command{uuchk} program (@pxref{Invoking uuchk}), you must check that UUCP can correctly contact another system. Tell @command{uucico} to dial out to the system by using the @option{-s} system switch (e.g., @samp{uucico -s uunet}). The log file should tell you what happens. The exact location of the log file depends upon the settings in @file{policy.h} when you compiled the program, and on the use of the @code{logfile} command in the @file{config} file. Typical locations are @file{/usr/spool/uucp/Log} or a subdirectory under @file{/usr/spool/uucp/.Log}. If you compiled the code with debugging enabled, you can use debugging mode to get a great deal of information about what sort of data is flowing back and forth; the various possibilities are described with the @code{debug} command (@pxref{Debugging Levels}). When initially setting up a connection @samp{-x chat} is probably the most useful (e.g., @samp{uucico -s uunet -x chat}); you may also want to use @samp{-x handshake,incoming,outgoing}. You can use @option{-x} multiple times on one command line, or you can give it comma separated arguments as in the last example. Use @samp{-x all} to turn on all possible debugging information. The debugging information is written to a file, normally @file{/usr/spool/uucp/Debug}, although the default can be changed in @file{policy.h}, and the @file{config} file can override the default with the @code{debugfile} command. The debugging file may contain passwords and some file contents as they are transmitted over the line, so the debugging file is only readable by the @code{uucp} user. You can use the @option{-f} switch to force @command{uucico} to call out even if the last call failed recently; using @option{-S} when naming a system has the same effect. Otherwise the status file (in the @file{.Status} subdirectory of the main spool directory, normally @file{/usr/spool/uucp}) (@pxref{Status Directory}) will prevent too many attempts from occurring in rapid succession. On older System V based systems which do not have the @code{setreuid} system call, problems may arise if ordinary users can start an execution of @command{uuxqt}, perhaps indirectly via @command{uucp} or @command{uux}. UUCP jobs may wind up executing with a real user ID of the user who invoked @command{uuxqt}, which can cause problems if the UUCP job checks the real user ID for security purposes. On such systems, it is safest to put @samp{run-uuxqt never} (@pxref{Miscellaneous (config)}) in the @file{config} file, so that @command{uucico} never starts @command{uuxqt}, and invoke @command{uuxqt} directly from a @file{crontab} file. Please let me know about any problems you have and how you got around them. If you do report a problem, please include the version number of the package you are using, the operating system you are running it on, and a sample of the debugging file showing the problem (debugging information is usually what is needed, not just the log file). General questions such as ``why doesn't @command{uucico} dial out'' are impossible to answer without much more information. @node Using Taylor UUCP, Configuration Files, Installing Taylor UUCP, Top @chapter Using Taylor UUCP @menu * Calling Other Systems:: Calling Other Systems * Accepting Calls:: Accepting Calls * Mail and News:: Using UUCP for Mail and News * The Spool Directory Layout:: The Spool Directory Layout * Spool Directory Cleaning:: Cleaning the UUCP Spool Directory @end menu @node Calling Other Systems, Accepting Calls, Using Taylor UUCP, Using Taylor UUCP @section Calling Other Systems @cindex calling out By default @command{uucp} and @command{uux} will automatically start up @command{uucico} to call another system whenever work is queued up. However, the call may fail, or you may have put in time restrictions which prevent the call at that time (perhaps because telephone rates are high) (@pxref{When to Call}). Also, a remote system may have work queued up for your system, but may not be calling you for some reason (perhaps you have agreed that your system should always place the call). To make sure that work gets transferred between the systems withing a reasonable time period, you should arrange to periodically invoke @command{uucico}. These periodic invocations are normally triggered by entries in the @file{crontab} file. The exact format of @file{crontab} files, and how new entries are added, varies from system to system; check your local documentation (try @samp{man cron}). To attempt to call all systems with outstanding work, use the command @samp{uucico -r1}. To attempt to call a particular system, use the command @samp{uucico -s @var{system}}. To attempt to call a particular system, but only if there is work for it, use the command @samp{uucico -C -s @var{system}}. (@pxref{Invoking uucico}). A common case is to want to try to call a system at a certain time, with periodic retries if the call fails. A simple way to do this is to create an empty UUCP command file, known as a @dfn{poll file}. If a poll file exists for a system, then @samp{uucico -r1} will place a call to it. If the call succeeds, the poll file will be deleted. A poll file can be easily created using the @samp{uux} command, by requesting the execution of an empty command. To create a poll file for @var{system}, just do something like this: @example uux -r @var{system}! @end example The @option{-r} tells @samp{uux} to not start up @samp{uucico} immediately. Of course, if you do want @samp{uucico} to start up right away, omit the @option{-r}; if the call fails, the poll file will be left around to cause a later call. For example, I use the following crontab entries locally: @example 45 * * * * /bin/echo /usr/lib/uucp/uucico -r1 | /bin/su uucpa 40 4,10,15 * * * /usr/bin/uux -r uunet! @end example Every hour, at 45 minutes past, this will check if there is any work to be done, and, if there is, will call the appropriate system. Also, at 4:40am, 10:40am, and 3:40pm, this will create a poll file file for @samp{uunet}, forcing the next run of @command{uucico} to call @samp{uunet}. @node Accepting Calls, Mail and News, Calling Other Systems, Using Taylor UUCP @section Accepting Calls @cindex calling in @cindex accepting calls To accept calls from another system, you must arrange matters such that when that system calls in, it automatically invokes @command{uucico} on your system. The most common arrangement is to create a special user name and password for incoming UUCP calls. This user name typically uses the same user ID as the regular @code{uucp} user (Unix permits several user names to share the same user ID). The shell for this user name should be set to @command{uucico}. Here is a sample @file{/etc/passwd} line to accept calls from a remote system named airs: @example Uairs:@var{password}:4:8:airs UUCP:/usr/spool/uucp:/usr/lib/uucp/uucico @end example The details may vary on your system. You must use reasonable user and group ID's. You must use the correct file name for @command{uucico}. The @var{password} must appear in the UUCP configuration files on the remote system, but will otherwise never be seen or typed by a human. Note that @command{uucico} appears as the login shell, and that it will be run with no arguments. This means that it will start in slave mode and accept an incoming connection. @xref{Invoking uucico}. On some systems, creating an empty file named @file{.hushlogin} in the home directory will skip the printing of various bits of information when the remote @command{uucico} logs in, speeding up the UUCP connection process. For the greatest security, each system which calls in should use a different user name, each with a different password, and the @code{called-login} command should be used in the @file{sys} file to ensure that the correct login name is used. @xref{Accepting a Call}, and see @ref{Security}. If you never need to dial out from your system, but only accept incoming calls, you can arrange for @command{uucico} to handle logins itself, completely controlling the port, by using the @option{--endless} option. @xref{Invoking uucico}. @node Mail and News, The Spool Directory Layout, Accepting Calls, Using Taylor UUCP @section Using UUCP for Mail and News. @cindex mail @cindex news Taylor UUCP does not include a mail package. All Unix systems come with some sort of mail delivery agent, typically @command{sendmail} or @code{MMDF}. Source code is available for some alternative mail delivery agents, such as @code{IDA sendmail} and @command{smail}. Taylor UUCP also does not include a news package. The two major Unix news packages are @code{C-news} and @code{INN}. Both are available in source code form. Configuring and using mail delivery agents is a notoriously complex topic, and I will not be discussing it here. Configuring news systems is usually simpler, but I will not be discussing that either. I will merely describe the interactions between the mail and news systems and UUCP. A mail or news system interacts with UUCP in two ways: sending and receiving. @menu * Sending mail or news:: Sending mail or news via UUCP * Receiving mail or news:: Receiving mail or news via UUCP @end menu @node Sending mail or news, Receiving mail or news, Mail and News, Mail and News @subsection Sending mail or news via UUCP When mail is to be sent from your machine to another machine via UUCP, the mail delivery agent will invoke @command{uux}. It will generally run a command such as @samp{uux - @var{system}!rmail @var{address}}, where @var{system} is the remote system to which the mail is being sent. It may pass other options to @command{uux}, such as @option{-r} or @option{-g} (@pxref{Invoking uux}). The news system also invokes @command{uux} in order to transfer articles to another system. The only difference is that news will use @command{uux} to invoke @command{rnews} on the remote system, rather than @command{rmail}. You should arrange for your mail and news systems to invoke the Taylor UUCP version of @command{uux}. If you only have Taylor UUCP, or if you simply replace any existing version of @command{uux} with the Taylor UUCP version, this will probably happen automatically. However, if you have two UUCP packages installed on your system, you will probably have to modify the mail and news configuration files in some way. Actually, if both the system UUCP and Taylor UUCP are using the same spool directory format, the system @command{uux} will probably work fine with the Taylor @command{uucico} (the reverse is not the case: the Taylor @command{uux} requires the Taylor @command{uucico}). However, data transfer will be somewhat more efficient if the Taylor @command{uux} is used. @node Receiving mail or news, , Sending mail or news, Mail and News @subsection Receiving mail or news via UUCP To receive mail, all that is necessary is for UUCP to invoke @command{rmail}. Any mail delivery agent will provide an appropriate version of @command{rmail}; you must simply make sure that it is in the command path used by UUCP (it almost certainly already is). The default command path is set in @file{policy.h}, and it may be overridden for a particular system by the @code{command-path} command (@pxref{Miscellaneous (sys)}). Similarly, for news UUCP must be able to invoke @command{rnews}. Any news system will provide a version of @command{rnews}, and you must ensure that is in a directory on the path that UUCP will search. @node The Spool Directory Layout, Spool Directory Cleaning, Mail and News, Using Taylor UUCP @section The Spool Directory Layout @cindex spool directory In general, the layout of the spool directory may be safely ignored. However, it is documented here for the curious. This description only covers the @code{SPOOLDIR_TAYLOR} layout. The ways in which the other spool directory layouts differ are described in the source file @file{unix/spool.c}. Directories and files are only created when they are needed, so a typical system will not have all of the entries described here. @menu * System Spool Directories:: System Spool Directories * Status Directory:: Status Spool Directory * Execution Subdirectories:: Execution Spool Subdirectories * Other Spool Subdirectories:: Other Spool Subdirectories * Spool Lock Files:: Spool Directory Lock Files @end menu @node System Spool Directories, Status Directory, The Spool Directory Layout, The Spool Directory Layout @subsection System Spool Directories @cindex system spool directories @table @file @item @var{system} There is a subdirectory of the main spool directory for each remote system. @item @var{system}/C. This directory stores files describing file transfer commands to be sent to the @var{system}. Each file name starts with @file{C.@var{g}}, where @var{g} is the job grade. Each file contains one or more commands. For details of the commands, see @ref{UUCP Protocol Commands}. @item @var{system}/D. This directory stores data files. Files with names like @file{D.@var{g}@var{ssss}}, where @var{g} is the grade and @var{ssss} is a sequence number, are waiting to be transferred to the @var{system}, as directed by the files in the @file{@var{system}/C.} directory. Files with other names, typically @file{D.@var{system}@var{g}@var{ssss}}, have been received from @var{system} and are waiting to be processed by an execution file in the @file{@var{system}/X.} directory. @item @var{system}/D.X This directory stores data files which will become execution files on the remote system. In current practice, this directory rarely exists, because most simple executions, including typical uses of @command{rmail} and @command{rnews}, send an @samp{E} command rather than an execution file (@pxref{The E Command}). @item @var{system}/X. This directory stores execution files which have been received from @var{system}. This directory normally exists, even though the corresponding @file{D.X} directory does not, because @command{uucico} will create an execution file on the fly when it receives an @samp{E} command. @item @var{system}/SEQF This file holds the sequence number of the last job sent to @var{system}. The sequence number is used to ensure that file names are unique in the remote system spool directory. The file is four bytes long. Sequence numbers are composed of digits and the upper case letters. @end table @node Status Directory, Execution Subdirectories, System Spool Directories, The Spool Directory Layout @subsection Status Directory @table @file @item .Status @cindex .Status @cindex status files This directory holds status files for each remote system. The name of the status file is the name of the system which it describes. Each status file describes the last conversation with the system. Running @samp{uustat --status} basically just formats and prints the contents of the status files (@pxref{uustat Examples}). Each status file has a single text line with six fields. @table @asis @item code A code indicating the status of the last conversation. The following values are defined, though not all are actually used. @table @samp @item 0 Conversation completed normally. @item 1 @command{uucico} was unable to open the port. @item 2 The last call to the system failed while dailing. @item 3 The last call to the system failed while logging in. @item 4 The last call to the system failed during the initial UUCP protocol handshake (@pxref{The Initial Handshake}). @item 5 The last call to the system failed after the initial handshake. @item 6 @command{uucico} is currently talking to the system. @item 7 The last call to the system failed because it was the wrong time to call (this is not used if calling the system is never permitted). @end table @item retries The number of retries since the last successful call. @item time of last call The time of the last call, in seconds since the epoch (as returned by the @code{time} system call). @item wait If the last call failed, this is the number of seconds since the last call before @command{uucico} may attempt another call. This is set based on the retry time; see @ref{When to Call}. The @option{-f} or @option{-S} options to @command{uucico} direct it to ignore this wait time; see @ref{Invoking uucico}. @item description A text description of the status, corresponding to the code in the first field. This may contain spaces. @item system name The name of the remote system. @end table @end table @node Execution Subdirectories, Other Spool Subdirectories, Status Directory, The Spool Directory Layout @subsection Execution Subdirectories @table @file @item .Xqtdir @cindex .Xqtdir When @command{uuxqt} executes a job requested by @command{uux}, it first changes the working directory to the @file{.Xqtdir} subdirectory. This permits the job to create any sort of temporary file without worrying about overwriting other files in the spool directory. Any files left in the @file{.Xqtdir} subdirectory are removed after each execution is complete. @item .Xqtdir@var{nnnn} When several instances of @command{uuxqt} are executing simultaneously, each one executes jobs in a separate directory. The first uses @file{.Xqtdir}, the second uses @file{.Xqtdir0001}, the third uses @file{.Xqtdir0002}, and so forth. @item .Corrupt @cindex .Corrupt If @command{uuxqt} encounters an execution file which it is unable to parse, it saves it in the @file{.Corrupt} directory, and sends mail about it to the UUCP administrator. @item .Failed @cindex .Failed If @command{uuxqt} executes a job, and the job fails, and there is enough disk space to hold the command file and all the data files, then @command{uuxqt} saves the files in the @file{.Failed} directory, and sends mail about it to the UUCP administrator. @end table @node Other Spool Subdirectories, Spool Lock Files, Execution Subdirectories, The Spool Directory Layout @subsection Other Spool Subdirectories @table @file @item .Sequence @cindex .Sequence This directory holds conversation sequence number files. These are used if the @code{sequence} command is used for a system (@pxref{Miscellaneous (sys)}). The sequence number for the system @var{system} is stored in the file @file{.Sequence/@var{system}}. It is simply stored as a printable number. @item .Temp @cindex .Temp This directory holds data files as they are being received from a remote system, before they are moved to their final destination. For file send requests which use a valid temporary file name in the @var{temp} field of the @samp{S} or @samp{E} command (@pxref{The S Command}), @command{uucico} receives the file into @file{.Temp/@var{system}/@var{temp}}, where @var{system} is the name of the remote system, and @var{temp} is the temporary file name. If a conversation fails during a file transfer, these files are used to automatically restart the file transfer from the point of failure. If the @samp{S} or @samp{E} command does not include a temporary file name, automatic restart is not possible. In this case, the files are received into a randomly named file in the @file{.Temp} directory itself. @item .Preserve @cindex .Preserve This directory holds data files which could not be transferred to a remote system for some reason (for example, the data file might be large, and exceed size restrictions imposed by the remote system). When a locally requested file transfer fails, @command{uucico} will store the data file in the @file{.Preserve} directory, and send mail to the requestor describing the failure and naming the saved file. @item .Received @cindex .Received This directory records which files have been received. If a conversation fails just after @command{uucico} acknowledges receipt of a file, it is possible for the acknowledgement to be lost. If this happens, the remote system will resend the file. If the file were an execution request, and @command{uucico} did not keep track of which files it had already received, this could lead to the execution being performed twice. To avoid this problem, when a conversation fails, @command{uucico} records each file that has been received, but for which the remote system may not have received the acknowledgement. It records this information by creating an empty file with the name @file{.Received/@var{system}/@var{temp}}, where @var{system} is the name of the remote system, and @var{temp} is the @var{temp} field of the @samp{S} or @samp{E} command from the remote system (@pxref{The S Command}). Then, if the remote system offers the file again in the next conversation, @command{uucico} refuses the send request and deletes the record in the @file{.Received} directory. This approach only works for file sends which use a temporary file name, but this is true of all execution requests. @end table @node Spool Lock Files, , Other Spool Subdirectories, The Spool Directory Layout @subsection Lock Files in the Spool Directory @cindex lock files in spool directory Lock files for devices and systems are stored in the lock directory, which may or may not be the same as the spool directory. The lock directory is set at compilation time by @code{LOCKDIR} in @file{policy.h}, which may be overridden by the @code{lockdir} command in the @file{config} file (@pxref{Miscellaneous (config)}). For a description of the names used for device lock files, and the format of the contents of a lock file, see @ref{UUCP Lock Files}. @table @file @item LCK..@var{sys} @cindex LCK..@var{sys} @cindex system lock files A lock file for a system, where @var{sys} is the system name. As noted above, these lock files are kept in the lock directory, which may not be the spool directory. These lock files are created by @command{uucico} while talking to a remote system, and are used to prevent multiple simultaneous conversations with a system. On systems which limit file names to 14 characters, only the first eight characters of the system name are used in the lock file name. This requires that the names of each directly connected remote system be unique in the first eight characters. @item LCK.XQT.@var{NN} @cindex LCK.XQT.@var{NN} When @command{uuxqt} starts up, it uses lock files to determine how many other @command{uuxqt} daemons are currently running. It first tries to lock @file{LCK.XQT.0}, then @file{LCK.XQT.1}, and so forth. This is used to implement the @code{max-uuxqts} command (@pxref{Miscellaneous (config)}). It is also used to parcel out the @file{.Xqtdir} subdirectories (@pxref{Execution Subdirectories}). @item LXQ.@var{cmd} @cindex LXQ.@var{cmd} When @command{uuxqt} is invoked with the @option{-c} or @option{--command} option (@pxref{Invoking uuxqt}), it creates a lock file named after the command it is executing. For example, @samp{uuxqt -c rmail} will create the lock file @file{LXQ.rmail}. This prevents other @command{uuxqt} daemons from executing jobs of the specified type. @item @var{system}/X./L.@var{xxx} @cindex L.@var{xxx} While @command{uuxqt} is executing a particular job, it creates a lock file with the same name as the @file{X.} file describing the job, but replacing the initial @samp{X} with @samp{L}. This ensures that if multiple @command{uuxqt} daemons are running, they do not simultaneously execute the same job. @item LCK..SEQ This lock file is used to control access to the sequence files for each system (@pxref{System Spool Directories}). It is only used on systems which do not support POSIX file locking using the @code{fcntl} system call. @end table @node Spool Directory Cleaning, , The Spool Directory Layout, Using Taylor UUCP @section Cleaning the Spool Directory @cindex spool directory, cleaning @cindex cleaning the spool directory The spool directory may need to be cleaned up periodically. Under some circumstances, files may accumulate in various subdirectories, such as @file{.Preserve} (@pxref{Other Spool Subdirectories}) or @file{.Corrupt} (@pxref{Execution Subdirectories}). Also, if a remote system stops calling in, you may want to arrange for any queued up mail to be returned to the sender. This can be done using the @command{uustat} command (@pxref{Invoking uustat}). The @file{contrib} directory includes a simple @file{uuclean} script which may be used as an example of a clean up script. It can be run daily out of @file{crontab}. You should periodically trim the UUCP log files, as they will otherwise grow without limit. The names of the log files are set in @file{policy.h}, and may be overridden in the configuration file (@pxref{config File}). By default they are are @file{/usr/spool/uucp/Log} and @file{/usr/spool/uucp/Stats}. You may find the @code{savelog} program in the @file{contrib} directory to be of use. There is a manual page for it in @file{contrib} as well. @node Configuration Files, Protocols, Using Taylor UUCP, Top @chapter Taylor UUCP Configuration Files This chapter describes the configuration files accepted by the Taylor UUCP package if compiled with @code{HAVE_TAYLOR_CONFIG} set to 1 in @file{policy.h}. The configuration files are normally found in the directory @var{newconfigdir}, which is defined by the @code{configure} option @option{--with-newconfigdir}; by default @var{newconfigdir} is @file{/usr/local/conf/uucp}. However, the main configuration file, @file{config}, is the only one which must be in that directory, since it may specify a different location for any or all of the other files. You may run any of the UUCP programs with a different main configuration file by using the @option{-I} or @option{--config} option; this can be useful when testing a new configuration. When you use the @option{-I} option the programs will revoke any setuid privileges. @menu * Configuration Overview:: Configuration File Overview * Configuration File Format:: Configuration File Format * Configuration Examples:: Examples of Configuration Files * Time Strings:: How to Write Time Strings * Chat Scripts:: How to Write Chat Scripts * config File:: The Main Configuration File * sys File:: The System Configuration File * port File:: The Port Configuration Files * dial File:: The Dialer Configuration Files * UUCP Over TCP:: UUCP Over TCP * Security:: Security Issues @end menu @node Configuration Overview, Configuration File Format, Configuration Files, Configuration Files @section Configuration File Overview UUCP uses several different types of configuration files, each describing a different kind of information. The commands permitted in each file are described in detail below. This section is a brief description of some of the different types of files. The @file{config} file is the main configuration file. It describes general information not associated with a particular remote system, such as the location of various log files. There are reasonable defaults for everything that may be specified in the @file{config} file, so you may not actually need one on your system. There may be only one @file{config} file, but there may be one or more of each other type of file. The default is one file for each type, but more may be listed in the @file{config} file. The @file{sys} files are used to describe remote systems. Each remote system to which you connect must be listed in a @file{sys} file. A @file{sys} file will include information for a system, such as the speed (baud rate) to use, or when to place calls. For each system you wish to call, you must describe one or more ports; these ports may be defined directly in the @file{sys} file, or they may be defined in a @file{port} file. The @file{port} files are used to describe ports. A port is a particular hardware connection on your computer. You would normally define as many ports as there are modems attached to your computer. A TCP connection is also described using a port. The @file{dial} files are used to describe dialers. Dialer is essentially another word for modem. The @file{dial} file describes the commands UUCP should use to dial out on a particular type of modem. You would normally define as many dialers as there are types of modems attached to your computer. For example, if you have three Telebit modems used for UUCP, you would probably define three ports and one dialer. There are other types of configuration files, but these are the important ones. The other types are described below. @node Configuration File Format, Configuration Examples, Configuration Overview, Configuration Files @section Configuration File Format All the configuration files follow a simple line-oriented @samp{@var{keyword} @var{value}} format. Empty lines are ignored, as are leading spaces; unlike HDB, lines with leading spaces are read. The first word on each line is a keyword. The rest of the line is interpreted according to the keyword. Most keywords are followed by numbers, boolean values or simple strings with no embedded spaces. The @kbd{#} character is used for comments. Everything from a @kbd{#} to the end of the line is ignored unless the @kbd{#} is preceded by a @kbd{\} (backslash); if the @kbd{#} is preceeded by a @kbd{\}, the @kbd{\} is removed but the @kbd{#} remains in the line. This can be useful for a phone number containing a @kbd{#}. To enter the sequence @samp{\#}, use @samp{\\#}. The backslash character may be used to continue lines. If the last character in a line is a backslash, the backslash is removed and the line is continued by the next line. The second line is attached to the first with no intervening characters; if you want any whitespace between the end of the first line and the start of the second line, you must insert it yourself. However, the backslash is not a general quoting character. For example, you cannot use it to get an embedded space in a string argument. Everything after the keyword must be on the same line. A @var{boolean} may be specified as @kbd{y}, @kbd{Y}, @kbd{t}, or @kbd{T} for true and @kbd{n}, @kbd{N}, @kbd{f}, or @kbd{F} for false; any trailing characters are ignored, so @code{true}, @code{false}, etc., are also acceptable. @node Configuration Examples, Time Strings, Configuration File Format, Configuration Files @section Examples of Configuration Files This section provides few typical examples of configuration files. There are also sample configuration files in the @file{sample} subdirectory of the distribution. @menu * config File Examples:: Examples of the Main Configuration File * Leaf Example:: Call a Single Remote Site * Gateway Example:: The Gateway for Several Local Systems @end menu @node config File Examples, Leaf Example, Configuration Examples, Configuration Examples @subsection config File Examples @cindex config file examples To start with, here are some examples of uses of the main configuration file, @file{config}. For a complete description of the commands that are permitted in @file{config}, see @ref{config File}. In many cases you will not need to create a @file{config} file at all. The most common reason to create one is to give your machine a special UUCP name. Other reasons might be to change the UUCP spool directory, or to permit any remote system to call in. If you have an internal network of machines, then it is likely that the internal name of your UUCP machine is not the name you want to use when calling other systems. For example, here at @file{airs.com} our mail/news gateway machine is named @file{elmer.airs.com} (it is one of several machines all named @file{@var{localname}.airs.com}). If we did not provide a @file{config} file, then our UUCP name would be @file{elmer}; however, we actually want it to be @file{airs}. Therefore, we use the following line in @file{config}: @example nodename airs @end example @cindex changing spool directory @cindex spool directory, changing The UUCP spool directory name is set in @file{policy.h} when the code is compiled. You might at some point decide that it is appropriate to move the spool directory, perhaps to put it on a different disk partition. You would use the following commands in @file{config} to change to directories on the partition @file{/uucp}: @example spool /uucp/spool pubdir /uucp/uucppublic logfile /uucp/spool/Log debugfile /uucp/spool/Debug @end example You would then move the contents of the current spool directory to @file{/uucp/spool}. If you do this, make sure that no UUCP processes are running while you change @file{config} and move the spool directory. @cindex anonymous UUCP Suppose you wanted to permit any system to call in to your system and request files. This is generally known as @dfn{anonymous UUCP}, since the systems which call in are effectively anonymous. By default, unknown systems are not permitted to call in. To permit this you must use the @code{unknown} command in @file{config}. The @code{unknown} command is followed by any command that may appear in the system file; for full details, see @ref{sys File}. I will show two possible anonymous UUCP configurations. The first will let any system call in and download files, but will not permit them to upload files to your system. @example # No files may be transferred to this system unknown receive-request no # The public directory is /usr/spool/anonymous unknown pubdir /usr/spool/anonymous # Only files in the public directory may be sent (the default anyhow) unknown remote-send ~ @end example @noindent Setting the public directory is convenient for the systems which call in. It permits to request a file by prefixing it with @file{~/}. For example, assuming your system is known as @samp{server}, then to retrieve the file @file{/usr/spool/anonymous/INDEX} a user on a remote site could just enter @samp{uucp server!~/INDEX ~}; this would transfer @file{INDEX} from @samp{server}'s public directory to the user's local public directory. Note that when using @samp{csh} or @samp{bash} the @kbd{!} and the second @kbd{~} must be quoted. The next example will permit remote systems to upload files to a special directory named @file{/usr/spool/anonymous/upload}. Permitting a remote system to upload files permits it to send work requests as well; this example is careful to prohibit commands from unknown systems. @example # No commands may be executed (the list of permitted commands is empty) unknown commands # The public directory is /usr/spool/anonymous unknown pubdir /usr/spool/anonymous # Only files in the public directory may be sent; users may not download # files from the upload directory unknown remote-send ~ !~/upload # May only upload files into /usr/spool/anonymous/upload unknown remote-receive ~/upload @end example @node Leaf Example, Gateway Example, config File Examples, Configuration Examples @subsection Leaf Example @cindex leaf site @cindex sys file example (leaf) A relatively common simple case is a @dfn{leaf site}, a system which only calls or is called by a single remote site. Here is a typical @file{sys} file that might be used in such a case. For full details on what commands can appear in the @file{sys} file, see @ref{sys File}. This is the @file{sys} file that is used at @file{airs.com}. We use a single modem to dial out to @file{uunet}. This example shows how you can specify the port and dialer information directly in the @file{sys} file for simple cases. It also shows the use of the following: @table @code @item call-login Using @code{call-login} and @code{call-password} allows the default login chat script to be used. In this case, the login name is specified in the call-out login file (@pxref{Configuration File Names}). @item call-timegrade @file{uunet} is requested to not send us news during the daytime. @item chat-fail If the modem returns @samp{BUSY} or @samp{NO CARRIER} the call is immediately aborted. @item protocol-parameter Since @file{uunet} tends to be slow, the default timeout has been increased. @end table This @file{sys} file relies on certain defaults. It will allow @file{uunet} to queue up @samp{rmail} and @samp{rnews} commands. It will allow users to request files from @file{uunet} into the UUCP public directory. It will also allow @file{uunet} to request files from the UUCP public directory; in fact @file{uunet} never requests files, but for additional security we could add the line @samp{request false}. @example # The following information is for uunet system uunet # The login name and password are kept in the callout password file call-login * call-password * # We can send anything at any time. time any # During the day we only accept grade `Z' or above; at other times # (not mentioned here) we accept all grades. uunet queues up news # at grade `d', which is lower than `Z'. call-timegrade Z Wk0755-2305,Su1655-2305 # The phone number. phone 7389449 # uunet tends to be slow, so we increase the timeout chat-timeout 120 # We are using a preconfigured Telebit 2500. port type modem port device /dev/ttyd0 port speed 19200 port carrier true port dialer chat "" ATZ\r\d\c OK ATDT\D CONNECT port dialer chat-fail BUSY port dialer chat-fail NO\sCARRIER port dialer complete \d\d+++\d\dATH\r\c port dialer abort \d\d+++\d\dATH\r\c # Increase the timeout and the number of retries. protocol-parameter g timeout 20 protocol-parameter g retries 10 @end example @node Gateway Example, , Leaf Example, Configuration Examples @subsection Gateway Example @cindex gateway @cindex sys file example (gateway) Many organizations have several local machines which are connected by UUCP, and a single machine which connects to the outside world. This single machine is often referred to as a @dfn{gateway} machine. For this example I will assume a fairly simple case. It should still provide a good general example. There are three machines, @file{elmer}, @file{comton} and @file{bugs}. @file{elmer} is the gateway machine for which I will show the configuration file. @file{elmer} calls out to @file{uupsi}. As an additional complication, @file{uupsi} knows @file{elmer} as @file{airs}; this will show how a machine can have one name on an internal network but a different name to the external world. @file{elmer} has two modems. It also has an TCP connection to @file{uupsi}, but since that is supposed to be reserved for interactive work (it is, perhaps, only a 9600 baud SLIP line) it will only use it if the modems are not available. A network this small would normally use a single @file{sys} file. However, for pedagogical purposes I will show two separate @file{sys} files, one for the local systems and one for @file{uupsi}. This is done with the @code{sysfile} command in the @file{config} file. Here is the @file{config} file. @example # This is config # The local sys file sysfile /usr/local/lib/uucp/sys.local # The remote sys file sysfile /usr/local/lib/uucp/sys.remote @end example Using the defaults feature of the @file{sys} file can greatly simplify the listing of local systems. Here is @file{sys.local}. Note that this assumes that the local systems are trusted; they are permited to request any world readable file and to write files into any world writable directory. @example # This is sys.local # Get the login name and password to use from the call-out file call-login * call-password * # The systems must use a particular login called-login Ulocal # Permit sending any world readable file local-send / remote-send / # Permit receiving into any world writable directory local-receive / remote-receive / # Call at any time time any # Use port1, then port2 port port1 alternate port port2 # Now define the systems themselves. Because of all the defaults we # used, there is very little to specify for the systems themselves. system comton phone 5551212 system bugs phone 5552424 @end example The @file{sys.remote} file describes the @file{uupsi} connection. The @code{myname} command is used to change the UUCP name to @file{airs} when talking to @file{uupsi}. @example # This is sys.remote # Define uupsi system uupsi # The login name and password are in the call-out file call-login * call-password * # We can call out at any time time any # uupsi uses a special login name called-login Uuupsi # uuspi thinks of us as `airs' myname airs # The phone number phone 5554848 # We use port2 first, then port1, then TCP port port2 alternate port port1 alternate # We don't bother to make a special entry in the port file for TCP, we # just describe the entire port right here. We use a special chat # script over TCP because the usual one confuses some TCP servers. port type TCP address uu.psi.com chat ogin: \L word: \P @end example The ports are defined in the file @file{port} (@pxref{port File}). For this example they are both connected to the same type of 2400 baud Hayes-compatible modem. @example # This is port port port1 type modem device /dev/ttyd0 dialer hayes speed 2400 port port2 type modem device /dev/ttyd1 dialer hayes speed 2400 @end example Dialers are described in the @file{dial} file (@pxref{dial File}). @example # This is dial dialer hayes # The chat script used to dial the phone. \D is the phone number. chat "" ATZ\r\d\c OK ATDT\D CONNECT # If we get BUSY or NO CARRIER we abort the dial immediately chat-fail BUSY chat-fail NO\sCARRIER # When the call is over we make sure we hangup the modem. complete \d\d+++\d\dATH\r\c abort \d\d+++\d\dATH\r\c @end example @node Time Strings, Chat Scripts, Configuration Examples, Configuration Files @section Time Strings @cindex time strings Several commands use time strings to specify a range of times. This section describes how to write time strings. A time string may be a list of simple time strings separated with a vertical bar @samp{|} or a comma @samp{,}. Each simple time string must begin with @samp{Su}, @samp{Mo}, @samp{Tu}, @samp{We}, @samp{Th}, @samp{Fr}, or @samp{Sa}, or @samp{Wk} for any weekday, or @samp{Any} for any day. Following the day may be a range of hours separated with a hyphen using 24 hour time. The range of hours may cross 0; for example @samp{2300-0700} means any time except 7 AM to 11 PM. If no time is given, calls may be made at any time on the specified day(s). The time string may also be the single word @samp{Never}, which does not match any time. The time string may also be a single word with a name defined in a previous @code{timetable} command (@pxref{Miscellaneous (config)}). Here are a few sample time strings with an explanation of what they mean. @table @samp @item Wk2305-0855,Sa,Su2305-1655 This means weekdays before 8:55 AM or after 11:05 PM, any time Saturday, or Sunday before 4:55 PM or after 11:05 PM. These are approximately the times during which night rates apply to phone calls in the U.S.A. Note that this time string uses, for example, @samp{2305} rather than @samp{2300}; this will ensure a cheap rate phone call even if the computer clock is running up to five minutes ahead of the real time. @item Wk0905-2255,Su1705-2255 This means weekdays from 9:05 AM to 10:55 PM, or Sunday from 5:05 PM to 10:55 PM. This is approximately the opposite of the previous example. @item Any This means any day. Since no time is specified, it means any time on any day. @end table @node Chat Scripts, config File, Time Strings, Configuration Files @section Chat Scripts @cindex chat scripts Chat scripts are used in several different places, such as dialing out on modems or logging in to remote systems. Chat scripts are made up of pairs of strings. The program waits until it sees the first string, known as the @dfn{expect} string, and then sends out the second string, the @dfn{send} string. Each chat script is defined using a set of commands. These commands always end in a string beginning with @code{chat}, but may start with different strings. For example, in the @file{sys} file there is one set of commands beginning with @code{chat} and another set beginning with @code{called-chat}. The prefixes are only used to disambiguate different types of chat scripts, and this section ignores the prefixes when describing the commands. @table @code @item chat @var{strings} @findex chat Specify a chat script. The arguments to the @code{chat} command are pairs of strings separated by whitespace. The first string of each pair is an expect string, the second is a send string. The program will wait for the expect string to appear; when it does, the program will send the send string. If the expect string does not appear within a certain number of seconds (as set by the @code{chat-timeout} command), the chat script fails and, typically, the call is aborted. If the final expect string is seen (and the optional final send string has been sent), the chat script is successful. An expect string may contain additional subsend and subexpect strings, separated by hyphens. If the expect string is not seen, the subsend string is sent and the chat script continues by waiting for the subexpect string. This means that a hyphen may not appear in an expect string; on an ASCII system, use @samp{\055} instead. An expect string may simply be @samp{""}, meaning to skip the expect phase. Otherwise, the following escape characters may appear in expect strings: @table @samp @item \b a backspace character @item \n a newline or line feed character @item \N a null character (for HDB compatibility) @item \r a carriage return character @item \s a space character @item \t a tab character @item \\ a backslash character @item \@var{ddd} character @var{ddd}, where @var{ddd} are up to three octal digits @item \x@var{ddd} character @var{ddd}, where @var{ddd} are hexadecimal digits. @end table As in C, there may be up to three octal digits following a backslash, but the hexadecimal escape sequence continues as far as possible. To follow a hexadecimal escape sequence with a hex digit, interpose a send string of @samp{""}. A chat script expect string may also specify a timeout. This is done by using the escape sequence @samp{\W@var{seconds}}. This escape sequence may only appear at the very end of the expect string. It temporarily overrides the timeout set by @code{chat-timeout} (described below) only for the expect string to which it is attached. A send string may simply be @samp{""} to skip the send phase. Otherwise, all of the escape characters legal for expect strings may be used, and the following escape characters are also permitted: @table @samp @item EOT send an end of transmission character (@kbd{^D}) @item BREAK send a break character (may not work on all systems) @item \c suppress trailing carriage return at end of send string @item \d delay sending for 1 or 2 seconds @item \e disable echo checking @item \E enable echo checking @item \K same as @samp{BREAK} @item \p pause sending for a fraction of a second @item \M do not require carrier signal @item \m require carrier signal (fail if not present) @end table Some specific types of chat scripts also define additional escape sequences that may appear in the send string. For example, the login chat script defines @samp{\L} and @samp{\P} to send the login name and password, respectively. A carriage return will be sent at the end of each send string, unless the @kbd{\c} escape sequence appears in the string. Note that some UUCP packages use @kbd{\b} for break, but here it means backspace. Echo checking means that after writing each character the program will wait until the character is echoed. Echo checking must be turned on separately for each send string for which it is desired; it will be turned on for characters following @kbd{\E} and turned off for characters following @kbd{\e}. When used with a port which does not support the carrier signal, as set by the @code{carrier} command in the port file, @kbd{\M} and @kbd{\m} are ignored. Similarly, when used in a dialer chat script with a dialer which does not support the carrier signal, as set by the @code{carrier} command in the dial file, @kbd{\M} and @kbd{\m} are ignored. @item chat-timeout @var{number} @findex chat-timeout The number of seconds to wait for an expect string in the chat script, before timing out and sending the next subsend, or failing the chat script entirely. The default value is 10 for a login chat or 60 for any other type of chat. @item chat-fail @var{string} @findex chat-fail If the @var{string} is seen at any time during a chat script, the chat script is aborted. The string may not contain any whitespace characters: escape sequences must be used for them. Multiple @code{chat-fail} commands may appear in a single chat script. The default is to have none. This permits a chat script to be quickly aborted if an error string is seen. For example, a script used to dial out on a modem might use the command @samp{chat-fail BUSY} to stop the chat script immediately if the string @samp{BUSY} was seen. The @code{chat-fail} strings are considered in the order they are listed, so if one string is a suffix of another the longer one should be listed first. This affects the error message which will be logged. Of course, if one string is contained within another, but is not a suffix, the smaller string will always be found before the larger string could match. @item chat-seven-bit @var{boolean} @findex chat-seven-bit If the argument is true, all incoming characters are stripped to seven bits when being compared to the expect string. Otherwise all eight bits are used in the comparison. The default is true, because some Unix systems generate parity bits during the login prompt which must be ignored while running a chat script. This has no effect on any @code{chat-program}, which must ignore parity by itself if necessary. @item chat-program @var{strings} @findex chat-program Specify a program to run before executing the chat script. This program could run its own version of a chat script, or it could do whatever it wants. If both @code{chat-program} and @code{chat} are specified, the program is executed first followed by the chat script. The first argument to the @code{chat-program} command is the program name to run. The remaining arguments are passed to the program. The following escape sequences are recognized in the arguments: @table @kbd @item \Y port device name @item \S port speed @item \\ backslash @end table Some specific uses of @code{chat-program} define additional escape sequences. Arguments other than escape sequences are passed exactly as they appear in the configuration file, except that sequences of whitespace are compressed to a single space character (this exception may be removed in the future). If the @code{chat-program} command is not used, no program is run. On Unix, the standard input and standard output of the program will be attached to the port in use. Anything the program writes to standard error will be written to the UUCP log file. No other file descriptors will be open. If the program does not exit with a status of 0, it will be assumed to have failed. This means that the dialing programs used by some versions of HDB may not be used directly, but you may be able to run them via the @code{dialHDB} program in the @file{contrib} directory. The program will be run as the @code{uucp} user, and the environment will be that of the process that started @command{uucico}, so care must be taken to maintain security. No search path is used to find the program; a full file name must be given. If the program is an executable shell script, it will be passed to @file{/bin/sh} even on systems which are unable to execute shell scripts. @end table Here is a simple example of a chat script that might be used to reset a Hayes compatible modem. @example chat "" ATZ OK-ATZ-OK @end example The first expect string is @samp{""}, so it is ignored. The chat script then sends @samp{ATZ}. If the modem responds with @samp{OK}, the chat script finishes. If 60 seconds (the default timeout) pass before seeing @samp{OK}, the chat script sends another @samp{ATZ}. If it then sees @samp{OK}, the chat script succeeds. Otherwise, the chat script fails. For a more complex chat script example, see @ref{Logging In}. @node config File, sys File, Chat Scripts, Configuration Files @section The Main Configuration File @cindex config file @cindex main configuration file @cindex configuration file (config) The main configuration file is named @file{config}. Since all the values that may be specified in the main configuration file also have defaults, there need not be a main configuration file at all. Each command in @file{config} may have a program prefix, which is a separate word appearing at the beginning of the line. The currently supported prefixes are @samp{uucp} and @samp{cu}. Any command prefixed by @samp{uucp} will not be read by the @command{cu} program. Any command prefixed by @samp{cu} will only be read by the @command{cu} program. For example, to use a list of systems known only to @command{cu}, list them in a separate file @file{@var{file}} and put @samp{cu sysfile @file{@var{file}}} in @file{config}. @menu * Miscellaneous (config):: Miscellaneous config File Commands * Configuration File Names:: Using Different Configuration Files * Log File Names:: Using Different Log Files * Debugging Levels:: Debugging Levels @end menu @node Miscellaneous (config), Configuration File Names, config File, config File @subsection Miscellaneous config File Commands @table @code @item nodename @var{string} @findex nodename @itemx hostname @var{string} @findex hostname @itemx uuname @var{string} @findex uuname @cindex UUCP system name @cindex system name These keywords are equivalent. They specify the UUCP name of the local host. If there is no configuration file, an appropriate system function will be used to get the host name, if possible. @item spool @var{string} @findex spool @cindex spool directory, setting @cindex /usr/spool/uucp Specify the spool directory. The default is from @file{policy.h}. This is where UUCP files are queued. Status files and various sorts of temporary files are also stored in this directory and subdirectories of it. @item pubdir @var{string} @findex pubdir in config file @cindex public directory @cindex uucppublic @cindex /usr/spool/uucppublic Specify the public directory. The default is from @file{policy.h}. When a file is named using a leading @kbd{~/}, it is taken from or to the public directory. Each system may use a separate public directory by using the @code{pubdir} command in the system configuration file; see @ref{Miscellaneous (sys)}. @item lockdir @var{string} @findex lockdir @cindex lock directory Specify the directory to place lock files in. The default is from @file{policy.h}; see the information in that file. Normally the lock directory should be set correctly in @file{policy.h}, and not changed here. However, changing the lock directory is sometimes useful for testing purposes. This only affects lock files for devices and systems; it does not affect certain internal lock files which are stored in the spool directory (@pxref{Spool Lock Files}). @item unknown @var{string} @dots{} @findex unknown @cindex unknown systems The @var{string} and subsequent arguments are treated as though they appeared in the system file (@pxref{sys File}). They are used to apply to any unknown systems that may call in, probably to set file transfer permissions and the like. If the @code{unknown} command is not used, unknown systems are not permitted to call in. @item strip-login @var{boolean} @findex strip-login @cindex parity in login names If the argument is true, then, when @command{uucico} is doing its own login prompting with the @option{-e}, @option{-l}, or @option{-w} switches, it will strip the parity bit when it reads the login name and password. Otherwise all eight bits will be used when checking the strings against the UUCP password file. The default is true, since some other UUCP packages send parity bits with the login name and password, and few systems use eight bit characters in the password file. @item strip-proto @var{boolean} @findex strip-proto If the argument is true, then @command{uucico} will strip the parity bit from incoming UUCP protocol commands. Otherwise all eight bits will be used. This only applies to commands which are not encapsulated in a link layer protocol. The default is true, which should always be correct unless your UUCP system names use eight bit characters. @item max-uuxqts @var{number} @findex max-uuxqts Specify the maximum number of @command{uuxqt} processes which may run at the same time. Having several @command{uuxqt} processes running at once can significantly slow down a system, but, since @command{uuxqt} is automatically started by @command{uucico}, it can happen quite easily. The default for @code{max-uuxqts} is 0, which means that there is no limit. If HDB configuration files are being read and the code was compiled without @code{HAVE_TAYLOR_CONFIG}, then, if the file @file{Maxuuxqts} in the configuration directory contains a readable number, it will be used as the value for @code{max-uuxqts}. @item run-uuxqt @var{string} or @var{number} @findex run-uuxqt Specify when @command{uuxqt} should be run by @command{uucico}. This may be a positive number, in which case @command{uucico} will start a @command{uuxqt} process whenever it receives the given number of execution files from the remote system, and, if necessary, at the end of the call. The argument may also be one of the strings @samp{once}, @samp{percall}, or @samp{never}. The string @samp{once} means that @command{uucico} will start @command{uuxqt} once at the end of execution. The string @samp{percall} means that @command{uucico} will start @command{uuxqt} once per call that it makes (this is only different from @code{once} when @command{uucico} is invoked in a way that causes it to make multiple calls, such as when the @option{-r1} option is used without the @option{-s} option). The string @samp{never} means that @command{uucico} will never start @command{uuxqt}, in which case @command{uuxqt} should be periodically run via some other mechanism. The default depends upon which type of configuration files are being used; if @code{HAVE_TAYLOR_CONFIG} is used the default is @samp{once}, otherwise if @code{HAVE_HDB_CONFIG} is used the default is @samp{percall}, and otherwise, for @code{HAVE_V2_CONFIG}, the default is @samp{10}. @item timetable @var{string} @var{string} @findex timetable The @code{timetable} defines a timetable that may be used in subsequently appearing time strings; see @ref{Time Strings}. The first string names the timetable entry; the second is a time string. The following @code{timetable} commands are predefined. The NonPeak timetable is included for compatibility. It originally described the offpeak hours of Tymnet and Telenet, but both have since changed their schedules. @example timetable Evening Wk1705-0755,Sa,Su timetable Night Wk2305-0755,Sa,Su2305-1655 timetable NonPeak Wk1805-0655,Sa,Su @end example If this command does not appear, then, obviously, no additional timetables will be defined. @item v2-files @var{boolean} @findex v2-files If the code was compiled to be able to read V2 configuration files, a false argument to this command will prevent them from being read. This can be useful while testing. The default is true. @item hdb-files @var{boolean} @findex hdb-files If the code was compiled to be able to read HDB configuration files, a false argument to this command will prevent them from being read. This can be useful while testing. The default is true. @end table @node Configuration File Names, Log File Names, Miscellaneous (config), config File @subsection Configuration File Names @table @code @item sysfile @var{strings} @findex sysfile Specify the system file(s). The default is the file @file{sys} in the directory @var{newconfigdir}. These files hold information about other systems with which this system communicates; see @ref{sys File}. Multiple system files may be given on the line, and the @code{sysfile} command may be repeated; each system file has its own set of defaults. @item portfile @var{strings} @findex portfile Specify the port file(s). The default is the file @file{port} in the directory @var{newconfigdir}. These files describe ports which are used to call other systems and accept calls from other systems; see @ref{port File}. No port files need be named at all. Multiple port files may be given on the line, and the @code{portfile} command may be repeated. @item dialfile @var{strings} @findex dialfile Specify the dial file(s). The default is the file @file{dial} in the directory @var{newconfigdir}. These files describe dialing devices (modems); see @ref{dial File}. No dial files need be named at all. Multiple dial files may be given on the line, and the @code{dialfile} command may be repeated. @item dialcodefile @var{strings} @findex dialcodefile @cindex configuration file (dialcode) @cindex dialcode file @cindex dialcode configuration file Specify the dialcode file(s). The default is the file @file{dialcode} in the directory @var{newconfigdir}. These files specify dialcodes that may be used when sending phone numbers to a modem. This permits using the same set of phone numbers in different area-codes or with different phone systems, by using dialcodes to specify the calling sequence. When a phone number goes through dialcode translation, the leading alphabetic characters are stripped off. The dialcode files are read line by line, just like any other configuration file, and when a line is found whose first word is the same as the leading characters from the phone number, the second word on the line (which would normally consist of numbers) replaces the dialcode in the phone number. No dialcode file need be used. Multiple dialcode files may be specified on the line, and the @code{dialcodefile} command may be repeated; all the dialcode files will be read in turn until a dialcode is located. @item callfile @var{strings} @findex callfile @cindex call out file @cindex call configuration file @cindex call out login name @cindex call out password @cindex configuration file (call) Specify the call out login name and password file(s). The default is the file @file{call} in the directory @var{newconfigdir}. If the call out login name or password for a system are given as @kbd{*} (@pxref{Logging In}), these files are read to get the real login name or password. Each line in the file(s) has three words: the system name, the login name, and the password. The login name and password may contain escape sequences like those in a chat script expect string (@pxref{Chat Scripts}). This file is only used when placing calls to remote systems; the password file described under @code{passwdfile} below is used for incoming calls. The intention of the call out file is to permit the system file to be publically readable; the call out files must obviously be kept secure. These files need not be used. Multiple call out files may be specified on the line, and the @code{callfile} command may be repeated; all the files will be read in turn until the system is found. @item passwdfile @var{strings} @findex passwdfile @cindex passwd file @cindex passwd configuration file @cindex configuration file (passwd) @cindex call in login name @cindex call in password Specify the password file(s) to use for login names when @command{uucico} is doing its own login prompting, which it does when given the @option{-e}, @option{-l} or @option{-w} switches. The default is the file @file{passwd} in the directory @var{newconfigdir}. Each line in the file(s) has two words: the login name and the password (e.g., @samp{Ufoo foopas}). They may contain escape sequences like those in a chat script expect string (@pxref{Chat Scripts}). The login name is accepted before the system name is known, so these are independent of which system is calling in; a particular login may be required for a system by using the @code{called-login} command in the system file (@pxref{Accepting a Call}). These password files are optional, although one must exist if @command{uucico} is to present its own login prompts. As a special exception, a colon may be used to separate the login name from the password, and a colon may be used to terminate the password. This means that the login name and password may not contain a colon. This feature, in conjunction with the @code{HAVE_ENCRYPTED_PASSWORDS} macro in @file{policy.h}, permits using a standard Unix @file{/etc/passwd} as a UUCP password file, providing the same set of login names and passwords for both @command{getty} and @command{uucico}. Multiple password files may be specified on the line, and the @code{passwdfile} command may be repeated; all the files will be read in turn until the login name is found. @end table @node Log File Names, Debugging Levels, Configuration File Names, config File @subsection Log File Names @table @code @item logfile @var{string} @findex logfile @cindex log file Name the log file. The default is from @file{policy.h}. Logging information is written to this file. If @code{HAVE_HDB_LOGGING} is defined in @file{policy.h}, then by default a separate log file is used for each system; using this command to name a log file will cause all the systems to use it. @item statfile @var{string} @findex statfile @cindex statistics file Name the statistics file. The default is from @file{policy.h}. Statistical information about file transfers is written to this file. @item debugfile @var{string} @findex debugfile @cindex debugging file Name the file to which all debugging information is written. The default is from @file{policy.h}. This command is only effective if the code has been compiled to include debugging (this is controlled by the @code{DEBUG} macro in @file{policy.h}). If debugging is on, messages written to the log file are also written to the debugging file to make it easier to keep the order of actions straight. The debugging file is different from the log file because information such as passwords can appear in it, so it must be not be publically readable. @end table @node Debugging Levels, , Log File Names, config File @subsection Debugging Levels @table @code @item debug @var{string} @dots{} @findex debug in config file Set the debugging level. This command is only effective if the code has been compiled to include debugging. The default is to have no debugging. The arguments are strings which name the types of debugging to be turned on. The following types of debugging are defined: @table @samp @item abnormal Output debugging messages for abnormal situations, such as recoverable errors. @item chat Output debugging messages for chat scripts. @item handshake Output debugging messages for the initial handshake. @item uucp-proto Output debugging messages for the UUCP session protocol. @item proto Output debugging messages for the individual link protocols. @item port Output debugging messages for actions on the communication port. @item config Output debugging messages while reading the configuration files. @item spooldir Output debugging messages for actions in the spool directory. @item execute Output debugging messages whenever another program is executed. @item incoming List all incoming data in the debugging file. @item outgoing List all outgoing data in the debugging file. @item all All of the above. @end table The debugging level may also be specified as a number. A 1 will set @samp{chat} debugging, a 2 will set both @samp{chat} and @samp{handshake} debugging, and so on down the possibilities. Currently an 11 will turn on all possible debugging, since there are 11 types of debugging messages listed above; more debugging types may be added in the future. The @code{debug} command may be used several times in the configuration file; every debugging type named will be turned on. When running any of the programs, the @option{-x} switch (actually, for @command{uulog} it's the @option{-X} switch) may be used to turn on debugging. The argument to the @option{-x} switch is one of the strings listed above, or a number as described above, or a comma separated list of strings (e.g., @option{-x chat,handshake}). The @option{-x} switch may also appear several times on the command line, in which case all named debugging types will be turned on. The @option{-x} debugging is in addition to any debugging specified by the @code{debug} command; there is no way to cancel debugging information. The debugging level may also be set specifically for calls to or from a specific system with the @code{debug} command in the system file (@pxref{Miscellaneous (sys)}). The debugging messages are somewhat idiosyncratic; it may be necessary to refer to the source code for additional information in some cases. @end table @node sys File, port File, config File, Configuration Files @section The System Configuration File @cindex sys file @cindex system configuration file @cindex configuration file (sys) By default there is a single system configuration, named @file{sys} in the directory @var{newconfigdir}. This may be overridden by the @code{sysfile} command in the main configuration file; see @ref{Configuration File Names}. These files describe all remote systems known to the UUCP package. @menu * Defaults and Alternates:: Using Defaults and Alternates * Naming the System:: Naming the System * Calling Out:: Calling Out * Accepting a Call:: Accepting a Call * Protocol Selection:: Protocol Selection * File Transfer Control:: File Transfer Control * Miscellaneous (sys):: Miscellaneous sys File Commands * Default sys File Values:: Default Values @end menu @node Defaults and Alternates, Naming the System, sys File, sys File @subsection Defaults and Alternates The first set of commands in the file, up to the first @code{system} command, specify defaults to be used for all systems in that file. Each @file{sys} file uses a different set of defaults. Subsequently, each set of commands from @code{system} up to the next @code{system} command describe a particular system. Default values may be overridden for specific systems. Each system may then have a series of alternate choices to use when calling out or calling in. The first set of commands for a particular system, up to the first @code{alternate} command, provide the first choice. Subsequently, each set of commands from @code{alternate} up to the next @code{alternate} command describe an alternate choice for calling out or calling in. When a system is called, the commands before the first @code{alternate} are used to select a phone number, port, and so forth; if the call fails for some reason, the commands between the first @code{alternate} and the second are used, and so forth. Well, not quite. Actually, each succeeding alternate will only be used if it is different in some relevant way (different phone number, different chat script, etc.). If you want to force the same alternate to be used again (to retry a phone call more than once, for example), enter the phone number (or any other relevant field) again to make it appear different. The alternates can also be used to give different permissions to an incoming call based on the login name. This will only be done if the first set of commands, before the first @code{alternate} command, uses the @code{called-login} command. The list of alternates will be searched, and the first alternate with a matching @code{called-login} command will be used. If no alternates match, the call will be rejected. The @code{alternate} command may also be used in the file-wide defaults (the set of commands before the first @code{system} command). This might be used to specify a list of ports which are available for all systems (for an example of this, see @ref{Gateway Example}) or to specify permissions based on the login name used by the remote system when it calls in. The first alternate for each system will default to the first alternate for the file-wide defaults (as modified by the commands used before the first @code{alternate} command for this system), the second alternate for each system to the second alternate for the file-wide defaults (as modified the same way), and so forth. If a system specifies more alternates than the file-wide defaults, the trailing ones will default to the last file-wide default alternate. If a system specifies fewer alternates than the file-wide defaults, the trailing file-wide default alternates will be used unmodified. The @code{default-alternates} command may be used to modify this behaviour. This can all get rather confusing, although it's easier to use than to describe concisely; the @command{uuchk} program may be used to ensure that you are getting what you want. @need 2000 @node Naming the System, Calling Out, Defaults and Alternates, sys File @subsection Naming the System @table @code @item system @var{string} @findex system Specify the remote system name. Subsequent commands up to the next @code{system} command refer to this system. @item alternate [@var{string}] @findex alternate Start an alternate set of commands (@pxref{Defaults and Alternates}). An optional argument may be used to name the alternate. This name will be recorded in the log file if the alternate is used to call the system. There is no way to name the first alternate (the commands before the first @code{alternate} command). @item default-alternates @var{boolean} @findex default-alternates If the argument is false, any remaining default alternates (from the defaults specified at the top of the current system file) will not be used. The default is true. @item alias @var{string} @findex alias Specify an alias for the current system. The alias may be used by local @command{uucp} and @command{uux} commands, as well as by the remote system (which can be convenient if a remote system changes its name). The default is to have no aliases. @item myname @var{string} @findex myname Specifies a different system name to use when calling the remote system. Also, if @code{called-login} is used and is not @samp{ANY}, then, when a system logs in with that login name, @var{string} is used as the local system name. Because the local system name must be determined before the remote system has identified itself, using @code{myname} and @code{called-login} together for any system will set the local name for that login; this means that each locally used system name must have a unique login name associated with it. This allows a system to have different names for an external and an internal network. The default is to not use a special local name. @end table @node Calling Out, Accepting a Call, Naming the System, sys File @subsection Calling Out This section describes commands used when placing a call to another system. @menu * When to Call:: When to Call * Placing the Call:: Placing the Call * Logging In:: Logging In @end menu @need 2000 @node When to Call, Placing the Call, Calling Out, Calling Out @subsubsection When to Call @table @code @item time @var{string} [@var{number}] @findex time Specify when the system may be called. The first argument is a time string; see @ref{Time Strings}. The optional second argument specifies a retry time in minutes. If a call made during a time that matches the time string fails, no more calls are permitted until the retry time has passed. By default an exponentially increasing retry time is used: after each failure the next retry period is longer. A retry time specified in the @code{time} command is always a fixed amount of time. The @code{time} command may appear multiple times in a single alternate, in which case if any time string matches the system may be called. When the @code{time} command is used for a particular system, any @code{time} or @code{timegrade} commands that appeared in the system defaults are ignored. The default time string is @samp{Never}. @item timegrade @var{character} @var{string} [@var{number}] @findex timegrade @cindex grades The @var{character} specifies a grade. It must be a single letter or digit. The @var{string} is a time string (@pxref{Time Strings}). All jobs of grade @var{character} or higher (where @kbd{0} > @kbd{9} > @kbd{A} > @kbd{Z} > @kbd{a} > @kbd{z}) may be run at the specified time. An ordinary @code{time} command is equivalent to using @code{timegrade} with a grade of @kbd{z}, permitting all jobs. If there are no jobs of a sufficiently high grade according to the time string, the system will not be called. Giving the @option{-s} switch to @command{uucico} to force it to call a system causes it to assume there is a job of grade @kbd{0} waiting to be run. The optional third argument specifies a retry time in minutes. See the @code{time} command, above, for more details. Note that the @code{timegrade} command serves two purposes: 1) if there is no job of sufficiently high grade the system will not be called, and 2) if the system is called anyway (because the @option{-s} switch was given to @command{uucico}) only jobs of sufficiently high grade will be transferred. However, if the other system calls in, the @code{timegrade} commands are ignored, and jobs of any grade may be transferred (but see @code{call-timegrade} and @code{called-timegrade}, below). Also, the @code{timegrade} command will not prevent the other system from transferring any job it chooses, regardless of who placed the call. The @code{timegrade} command may appear multiple times without using @code{alternate}. When the @code{timegrade} command is used for a particular system, any @code{time} or @code{timegrade} commands that appeared in the system defaults are ignored. If this command does not appear, there are no restrictions on what grade of work may be done at what time. @item max-retries @var{number} @findex max-retries Gives the maximum number of times this system may be retried. If this many calls to the system fail, it will be called at most once a day whatever the retry time is. The default is 26. @item success-wait @var{number} A retry time, in seconds, which applies after a successful call. This can be used to put a limit on how frequently the system is called. For example, an argument of 1800 means that the system will not be called more than once every half hour. The default is 0, which means that there is no limit. @item call-timegrade @var{character} @var{string} @findex call-timegrade The @var{character} is a single character @kbd{A} to @kbd{Z}, @kbd{a} to @kbd{z}, or @kbd{0} to @kbd{9} and specifies a grade. The @var{string} is a time string (@pxref{Time Strings}). If a call is placed to the other system during a time which matches the time string, the remote system will be requested to only run jobs of grade @var{character} or higher. Unfortunately, there is no way to guarantee that the other system will obey the request (this UUCP package will, but there are others which will not); moreover, job grades are historically somewhat arbitrary, so specifying a grade will only be meaningful if the other system cooperates in assigning grades. This grade restriction only applies when the other system is called, not when the other system calls in. The @code{call-timegrade} command may appear multiple times without using @code{alternate}. If this command does not appear, or if none of the time strings match, the remote system will be allowed to send whatever grades of work it chooses. @item called-timegrade @var{character} @var{string} @findex called-timegrade The @var{character} is a single character @kbd{A} to @kbd{Z}, @kbd{a} to @kbd{z}, or @kbd{0} to @kbd{9} and specifies a grade. The @var{string} is a time string (@pxref{Time Strings}). If a call is received from the other system during a time which matches the time string, only jobs of grade @var{character} or higher will be sent to the remote system. This allows the job grade to be set for incoming calls, overriding any request made by the remote uucico. As noted above, job grades are historically somewhat arbitrary, so specifying a grade will only be meaningful if the other system cooperates in assigning grades. This grade restriction only applies to jobs on the local system; it does not affect the jobs transferred by the remote system. This grade restriction only applies when the other system calls in, not when the other system is called. The @code{called-timegrade} command may appear multiple times. If this command does not appear, or if none of the time strings match, any grade may be sent to the remote system upon receiving a call. @end table @need 2000 @node Placing the Call, Logging In, When to Call, Calling Out @subsubsection Placing the Call @table @code @itemx speed @var{number} @findex speed in sys file @item baud @var{number} @findex baud in sys file Specify the speed (the term @dfn{baud} is technically incorrect, but widely understood) at which to call the system. This will try all available ports with that speed until an unlocked port is found. The ports are defined in the port file. If both @code{speed} and @code{port} commands appear, both are used when selecting a port. To allow calls at more than one speed, the @code{alternate} command must be used (@pxref{Defaults and Alternates}). If this command does not appear, there is no default; the speed may be specified in the port file, but if it is not then the natural speed of the port will be used (whatever that means on the system). Specifying an explicit speed of 0 will request the natural speed of the port (whatever the system sets it to), overriding any default speed from the defaults at the top of the file. @item port @var{string} @findex port in sys file Name a particular port or type of port to use when calling the system. The information for this port is obtained from the port file. If this command does not appear, there is no default; a port must somehow be specified in order to call out (it may be specified implicitly using the @code{speed} command or explicitly using the next version of @code{port}). There may be many ports with the same name; each will be tried in turn until an unlocked one is found which matches the desired speed. @item port @var{string} @dots{} If more than one string follows the @code{port} command, the strings are treated as a command that might appear in the port file (@pxref{port File}). If a port is named (by using a single string following @code{port}) these commands are ignored; their purpose is to permit defining the port completely in the system file rather than always requiring entries in two different files. In order to call out, a port must be specified using some version of the @code{port} command, or by using the @code{speed} command to select ports from the port file. @item phone @var{string} @findex phone @itemx address @var{string} @findex address Give a phone number to call (when using a modem port) or a remote host to contact (when using a TCP or TLI port). The commands @code{phone} and @code{address} are equivalent; the duplication is intended to provide a mnemonic choice depending on the type of port in use. When used with a modem port, an @kbd{=} character in the phone number means to wait for a secondary dial tone (although only some modems support this); a @kbd{-} character means to pause while dialing for 1 second (again, only some modems support this). If the system has more than one phone number, each one must appear in a different alternate. The @code{phone} command must appear in order to call out on a modem; there is no default. When used with a TCP port, the string names the host to contact. It may be a domain name or a numeric Internet address. If no address is specified, the system name is used. When used with a TLI port, the string is treated as though it were an expect string in a chat script, allowing the use of escape characters (@pxref{Chat Scripts}). The @code{dialer-sequence} command in the port file may override this address (@pxref{port File}). When used with a port that not a modem or TCP or TLI, this command is ignored. @end table @node Logging In, , Placing the Call, Calling Out @subsubsection Logging In @table @code @item chat @var{strings} @findex chat in sys file @item chat-timeout @var{number} @findex chat-timeout in sys file @item chat-fail @var{string} @findex chat-fail in sys file @item chat-seven-bit @var{boolean} @findex chat-seven-bit in sys file @item chat-program @var{strings} @findex chat-program in sys file These commands describe a chat script to use when logging on to a remote system. This login chat script is run after any chat script defined in the @file{dial} file (@pxref{dial File}). Chat scripts are explained in @ref{Chat Scripts}. Two additional escape sequences may be used in send strings. @table @samp @item \L Send the login name, as set by the @code{call-login} command. @item \P Send the password, as set by the @code{call-password} command. @end table Three additional escape sequences may be used with the @code{chat-program} command. These are @samp{\L} and @samp{\P}, which become the login name and password, respectively, and @samp{\Z}, which becomes the name of the system of being called. The default chat script is: @example chat "" \r\c ogin:-BREAK-ogin:-BREAK-ogin: \L word: \P @end example This will send a carriage return (the @kbd{\c} suppresses the additional trailing carriage return that would otherwise be sent) and waits for the string @samp{ogin:} (which would be the last part of the @samp{login:} prompt supplied by a Unix system). If it doesn't see @samp{ogin:}, it sends a break and waits for @samp{ogin:} again. If it still doesn't see @samp{ogin:}, it sends another break and waits for @samp{ogin:} again. If it still doesn't see @samp{ogin:}, the chat script aborts and hangs up the phone. If it does see @samp{ogin:} at some point, it sends the login name (as specified by the @code{call-login} command) followed by a carriage return (since all send strings are followed by a carriage return unless @kbd{\c} is used) and waits for the string @samp{word:} (which would be the last part of the @samp{Password:} prompt supplied by a Unix system). If it sees @samp{word:}, it sends the password and a carriage return, completing the chat script. The program will then enter the handshake phase of the UUCP protocol. This chat script will work for most systems, so you will only be required to use the @code{call-login} and @code{call-password} commands. In fact, in the file-wide defaults you could set defaults of @samp{call-login *} and @samp{call-password *}; you would then just have to make an entry for each system in the call-out login file. Some systems seem to flush input after the @samp{login:} prompt, so they may need a version of this chat script with a @kbd{\d} before the @kbd{\L}. When using UUCP over TCP, some servers will not be handle the initial carriage return sent by this chat script; in this case you may have to specify the simple chat script @samp{ogin: \L word: \P}. @item call-login @var{string} @findex call-login Specify the login name to send with @kbd{\L} in the chat script. If the string is @samp{*} (e.g., @samp{call-login *}) the login name will be fetched from the call out login name and password file (@pxref{Configuration File Names}). The string may contain escape sequences as though it were an expect string in a chat script (@pxref{Chat Scripts}). There is no default. @item call-password @var{string} @findex call-password Specify the password to send with @kbd{\P} in the chat script. If the string is @samp{*} (e.g., @samp{call-password *}) the password will be fetched from the call-out login name and password file (@pxref{Configuration File Names}). The string may contain escape sequences as though it were an expect string in a chat script (@pxref{Chat Scripts}). There is no default. @end table @node Accepting a Call, Protocol Selection, Calling Out, sys File @subsection Accepting a Call @table @code @item called-login @var{strings} @findex called-login The first @var{string} specifies the login name that the system must use when calling in. If it is @samp{ANY} (e.g., @samp{called-login ANY}) any login name may be used; this is useful to override a file-wide default and to indicate that future alternates may have different login names. Case is significant. The default value is @samp{ANY}. Different alternates (@pxref{Defaults and Alternates}) may use different @code{called-login} commands, in which case the login name will be used to select which alternate is in effect; this will only work if the first alternate (before the first @code{alternate} command) uses the @code{called-login} command. Additional strings may be specified after the login name; they are a list of which systems are permitted to use this login name. If this feature is used, then normally the login name will only be given in a single @code{called-login} command. Only systems which appear on the list, or which use an explicit @code{called-login} command, will be permitted to use that login name. If the same login name is used more than once with a list of systems, all the lists are concatenated together. This feature permits you to restrict a login name to a particular set of systems without requiring you to use the @code{called-login} command for every single system; you can achieve a similar effect by using a different system file for each permitted login name with an appropriate @code{called-login} command in the file-wide defaults. @item callback @var{boolean} @findex callback If @var{boolean} is true, then when the remote system calls @command{uucico} will hang up the connection and prepare to call it back. The default is false. @item called-chat @var{strings} @findex called-chat @item called-chat-timeout @var{number} @findex called-chat-timeout @item called-chat-fail @var{string} @findex called-chat-fail @item called-chat-seven-bit @var{boolean} @findex called-chat-seven-bit @item called-chat-program @var{strings} @findex called-chat-program These commands may be used to define a chat script (@pxref{Chat Scripts}) that is run whenever the local system is called by the system being defined. The chat script defined by the @code{chat} command (@pxref{Logging In}), on the other hand, is used when the remote system is called. This called chat script might be used to set special modem parameters that are appropriate to a particular system. It is run after protocol negotiation is complete, but before the protocol has been started. For additional escape sequence which may be used besides those defined for all chat scripts, see @ref{Logging In}. There is no default called chat script. If the called chat script fails, the incoming call will be aborted. @end table @node Protocol Selection, File Transfer Control, Accepting a Call, sys File @subsection Protocol Selection @table @code @item protocol @var{string} @findex protocol in sys file Specifies which protocols to use for the other system, and in which order to use them. This would not normally be used. For example, @samp{protocol tfg}. The default depends on the characteristics of the port and the dialer, as specified by the @code{seven-bit} and @code{reliable} commands. If neither the port nor the dialer use either of these commands, the default is to assume an eight-bit reliable connection. The commands @samp{seven-bit true} or @samp{reliable false} might be used in either the port or the dialer to change this. Each protocol has particular requirements that must be met before it will be considered during negotiation with the remote side. The @samp{t} and @samp{e} protocols are intended for use over TCP or some other communication path with end to end reliability, as they do no checking of the data at all. They will only be considered on a TCP port which is both reliable and eight bit. For technical details, see @ref{t Protocol}, and @ref{e Protocol}. The @samp{i} protocol is a bidirectional protocol. It requires an eight-bit connection. It will run over a half-duplex link, such as Telebit modems in PEP mode, but for efficient use of such a connection you must use the @code{half-duplex} command (@pxref{port File}). @xref{i Protocol}. The @samp{g} protocol is robust, but requires an eight-bit connection. @xref{g Protocol}. The @samp{G} protocol is the System V Release 4 version of the @samp{g} protocol. @xref{Big G Protocol}. The @samp{a} protocol is a Zmodem like protocol, contributed by Doug Evans. It requires an eight-bit connection, but unlike the @samp{g} or @samp{i} protocol it will work if certain control characters may not be transmitted. The @samp{j} protocol is a variant of the @samp{i} protocol which can avoid certain control characters. The set of characters it avoids can be set by a parameter. While it technically does not require an eight bit connection (it could be configured to avoid all characters with the high bit set) it would be very inefficient to use it over one. It is useful over a eight-bit connection that will not transmit certain control characters. @xref{j Protocol}. The @samp{f} protocol is intended for use with X.25 connections; it checksums each file as a whole, so any error causes the entire file to be retransmitted. It requires a reliable connection, but only uses seven-bit transmissions. It is a streaming protocol, so, while it can be used on a serial port, the port must be completely reliable and flow controlled; many aren't. @xref{f Protocol}. The @samp{v} protocol is the @samp{g} protocol as used by the DOS program UUPC/Extended. It is provided only so that UUPC/Extended users can use it; there is no particular reason to select it. @xref{v Protocol}. The @samp{y} protocol is an efficient streaming protocol. It does error checking, but when it detects an error it immediately aborts the connection. This requires a reliable, flow controlled, eight-bit connection. In practice, it is only useful on a connection that is nearly always error-free. Unlike the @samp{t} and @samp{e} protocols, the connection need not be entirely error-free, so the @samp{y} protocol can be used on a serial port. @xref{y Protocol}. The protocols will be considered in the order shown above. This means that if neither the @code{seven-bit} nor the @code{reliable} command are used, the @samp{t} protocol will be used over a TCP connection and the @samp{i} protocol will be used over any other type of connection (subject, of course, to what is supported by the remote system; it may be assumed that all systems support the @samp{g} protocol). Note that currently specifying both @samp{seven-bit true} and @samp{reliable false} will not match any protocol. If this occurs through a combination of port and dialer specifications, you will have to use the @code{protocol} command for the system or no protocol will be selected at all (the only reasonable choice would be @samp{protocol f}). A protocol list may also be specified for a port (@pxref{port File}), but, if there is a list for the system, the list for the port is ignored. @item protocol-parameter @var{character} @var{string} @dots{} @findex protocol-parameter in sys file @var{character} is a single character specifying a protocol. The remaining strings are a command specific to that protocol which will be executed if that protocol is used. A typical command is something like @samp{window 7}. The particular commands are protocol specific. The @samp{i} protocol supports the following commands, all of which take numeric arguments: @table @code @item window The window size to request the remote system to use. This must be between 1 and 16 inclusive. The default is 16. @item packet-size The packet size to request the remote system to use. This must be between 1 and 4095 inclusive. The default is 1024. @item remote-packet-size If this is between 1 and 4095 inclusive, the packet size requested by the remote system is ignored, and this is used instead. The default is 0, which means that the remote system's request is honored. @item sync-timeout The length of time, in seconds, to wait for a SYNC packet from the remote system. SYNC packets are exchanged when the protocol is started. The default is 10. @item sync-retries The number of times to retry sending a SYNC packet before giving up. The default is 6. @item timeout The length of time, in seconds, to wait for an incoming packet before sending a negative acknowledgement. The default is 10. @item retries The number of times to retry sending a packet or a negative acknowledgement before giving up and closing the connection. The default is 6. @item errors The maximum number of errors to permit before closing the connection. The default is 100. @item error-decay The rate at which to ignore errors. Each time this many packets are received, the error count is decreased by one, so that a long connection with an occasional error will not exceed the limit set by @code{errors}. The default is 10. @item ack-frequency The number of packets to receive before sending an acknowledgement. The default is half the requested window size, which should provide good performance in most cases. @end table The @samp{g}, @samp{G} and @samp{v} protocols support the following commands, all of which take numeric arguments, except @code{short-packets} which takes a boolean argument: @table @code @item window The window size to request the remote system to use. This must be between 1 and 7 inclusive. The default is 7. @item packet-size The packet size to request the remote system to use. This must be a power of 2 between 32 and 4096 inclusive. The default is 64 for the @samp{g} and @samp{G} protocols and 1024 for the @samp{v} protocol. Many older UUCP packages do not support packet sizes larger than 64, and many others do not support packet sizes larger than 128. Some UUCP packages will even dump core if a larger packet size is requested. The packet size is not a negotiation, and it may be different in each direction. If you request a packet size larger than the remote system supports, you will not be able to send any files. @item startup-retries The number of times to retry the initialization sequence. The default is 8. @item init-retries The number of times to retry one phase of the initialization sequence (there are three phases). The default is 4. @item init-timeout The timeout in seconds for one phase of the initialization sequence. The default is 10. @item retries The number of times to retry sending either a data packet or a request for the next packet. The default is 6. @item timeout The timeout in seconds when waiting for either a data packet or an acknowledgement. The default is 10. @item garbage The number of unrecognized bytes to permit before dropping the connection. This must be larger than the packet size. The default is 10000. @item errors The number of errors (malformed packets, out of order packets, bad checksums, or packets rejected by the remote system) to permit before dropping the connection. The default is 100. @item error-decay The rate at which to ignore errors. Each time this many packets are received, the error count is decreased by one, so that a long connection with an occasional error will not exceed the limit set by @code{errors}. The default is 10. @item remote-window If this is between 1 and 7 inclusive, the window size requested by the remote system is ignored and this is used instead. This can be useful when dealing with some poor UUCP packages. The default is 0, which means that the remote system's request is honored. @item remote-packet-size If this is between 32 and 4096 inclusive the packet size requested by the remote system is ignored and this is used instead. There is probably no good reason to use this. The default is 0, which means that the remote system's request is honored. @item short-packets If this is true, then the code will optimize by sending shorter packets when there is less data to send. This confuses some UUCP packages, such as System V Release 4 (when using the @samp{G} protocol) and Waffle; when connecting to such a package, this parameter must be set to false. The default is true for the @samp{g} and @samp{v} protocols and false for the @samp{G} protocol. @end table The @samp{a} protocol is a Zmodem like protocol contributed by Doug Evans. It supports the following commands, all of which take numeric arguments except for @code{escape-control}, which takes a boolean argument: @table @code @item timeout Number of seconds to wait for a packet to arrive. The default is 10. @item retries The number of times to retry sending a packet. The default is 10. @item startup-retries The number of times to retry sending the initialization packet. The default is 4. @item garbage The number of garbage characters to accept before closing the connection. The default is 2400. @item send-window The number of characters that may be sent before waiting for an acknowledgement. The default is 1024. @item escape-control Whether to escape control characters. If this is true, the protocol may be used over a connection which does not transmit certain control characters, such as @code{XON} or @code{XOFF}. The connection must still transmit eight bit characters other than control characters. The default is false. @end table The @samp{j} protocol can be used over an eight bit connection that will not transmit certain control characters. It accepts the same protocol parameters that the @samp{i} protocol accepts, as well as one more: @table @code @item avoid A list of characters to avoid. This is a string which is interpreted as an escape sequence (@pxref{Chat Scripts}). The protocol does not have a way to avoid printable ASCII characters (byte values from 32 to 126, inclusive); only ASCII control characters and eight-bit characters may be avoided. The default value is @samp{\021\023}; these are the characters @code{XON} and @code{XOFF}, which many connections use for flow control. If the package is configured to use @code{HAVE_BSD_TTY}, then on some versions of Unix you may have to avoid @samp{\377} as well, due to the way some implementations of the BSD terminal driver handle signals. @end table The @samp{f} protocol is intended for use with error-correcting modems only; it checksums each file as a whole, so any error causes the entire file to be retransmitted. It supports the following commands, both of which take numeric arguments: @table @code @item timeout The timeout in seconds before giving up. The default is 120. @item retries How many times to retry sending a file. The default is 2. @end table The @samp{t} and @samp{e} protocols are intended for use over TCP or some other communication path with end to end reliability, as they do no checking of the data at all. They both support a single command, which takes a numeric argument: @table @code @item timeout The timeout in seconds before giving up. The default is 120. @end table The @samp{y} protocol is a streaming protocol contributed by Jorge Cwik. It supports the following commands, both of which take numeric arguments: @table @code @item timeout The timeout in seconds when waiting for a packet. The default is 60. @item packet-size The packet size to use. The default is 1024. @end table The protocol parameters are reset to their default values after each call. @end table @node File Transfer Control, Miscellaneous (sys), Protocol Selection, sys File @subsection File Transfer Control @table @code @item send-request @var{boolean} @findex send-request The @var{boolean} determines whether the remote system is permitted to request files from the local system. The default is yes. @item receive-request @var{boolean} @findex receive-request The @var{boolean} determines whether the remote system is permitted to send files to the local system. The default is yes. @item request @var{boolean} @findex request A shorthand command, equivalent to specifying both @samp{send-request @var{boolean}} and @samp{receive-request @var{boolean}}. @item call-transfer @var{boolean} @findex call-transfer The @var{boolean} is checked when the local system places the call. It determines whether the local system may do file transfers queued up for the remote system. The default is yes. @item called-transfer @var{boolean} @findex called-transfer The @var{boolean} is checked when the remote system calls in. It determines whether the local system may do file transfers queued up for the remote system. The default is yes. @item transfer @var{boolean} @findex transfer A shorthand command, equivalent to specifying both @samp{call-transfer @var{boolean}} and @samp{called-transfer @var{boolean}}. @item call-local-size @var{number} @var{string} @findex call-local-size The @var{string} is a time string (@pxref{Time Strings}). The @var{number} is the size in bytes of the largest file that should be transferred at a time matching the time string, if the local system placed the call and the request was made by the local system. This command may appear multiple times in a single alternate. If this command does not appear, or if none of the time strings match, there are no size restrictions. With all the size control commands, the size of a file from the remote system (as opposed to a file from the local system) will only be checked if the other system is running this package: other UUCP packages will not understand a maximum size request, nor will they provide the size of remote files. @item call-remote-size @var{number} @var{string} @findex call-remote-size Specify the size in bytes of the largest file that should be transferred at a given time by remote request, when the local system placed the call. This command may appear multiple times in a single alternate. If this command does not appear, there are no size restrictions. @item called-local-size @var{number} @var{string} @findex called-local-size Specify the size in bytes of the largest file that should be transferred at a given time by local request, when the remote system placed the call. This command may appear multiple times in a single alternate. If this command does not appear, there are no size restrictions. @item called-remote-size @var{number} @var{string} @findex called-remote-size Specify the size in bytes of the largest file that should be transferred at a given time by remote request, when the remote system placed the call. This command may appear multiple times in a single alternate. If this command does not appear, there are no size restrictions. @item local-send @var{strings} @findex local-send Specifies that files in the directories named by the @var{strings} may be sent to the remote system when requested locally (using @command{uucp} or @command{uux}). The directories in the list should be separated by whitespace. A @samp{~} may be used for the public directory. On a Unix system, this is typically @file{/usr/spool/uucppublic}; the public directory may be set with the @code{pubdir} command. Here is an example of @code{local-send}: @example local-send ~ /usr/spool/ftp/pub @end example Listing a directory allows all files within the directory and all subdirectories to be sent. Directories may be excluded by preceding them with an exclamation point. For example: @example local-send /usr/ftp !/usr/ftp/private ~ @end example @noindent means that all files in @file{/usr/ftp} or the public directory may be sent, except those files in @file{/usr/ftp/private}. The list of directories is read from left to right, and the last directory to apply takes effect; this means that directories should be listed from top down. The default is the root directory (i.e., any file at all may be sent by local request). @item remote-send @var{strings} @findex remote-send Specifies that files in the named directories may be sent to the remote system when requested by the remote system. The default is @samp{~}. @item local-receive @var{strings} @findex local-receive Specifies that files may be received into the named directories when requested by a local user. The default is @samp{~}. @item remote-receive @var{strings} @findex remote-receive Specifies that files may be received into the named directories when requested by the remote system. The default is @samp{~}. On Unix, the remote system may only request that files be received into directories that are writeable by the world, regardless of how this is set. @item forward-to @var{strings} @findex forward-to Specifies a list of systems to which files may be forwarded. The remote system may forward files through the local system on to any of the systems in this list. The string @samp{ANY} may be used to permit forwarding to any system. The default is to not permit forwarding to other systems. Note that if the remote system is permitted to execute the @command{uucp} command, it effectively has the ability to forward to any system. @item forward-from @var{strings} @findex forward-from Specifies a list of systems from which files may be forwarded. The remote system may request files via the local system from any of the systems in this list. The string @samp{ANY} may be used to permit forwarding to any system. The default is to not permit forwarding from other systems. Note that if a remote system is permitted to execute the @command{uucp} command, it effectively has the ability to request files from any system. @item forward @var{strings} @findex forward Equivalent to specifying both @samp{forward-to @var{strings}} and @samp{forward-from @var{strings}}. This would normally be used rather than either of the more specific commands. @item max-file-time @var{number} @findex max-file-time The maximum amount of time which will be sent sending any one file if there are other files to send. This will only be effective when using a protocol which permits interrupting one file send to send another file. This is true of the @samp{i} and @samp{j} protocols. The default is to have no maximum. @end table @node Miscellaneous (sys), Default sys File Values, File Transfer Control, sys File @subsection Miscellaneous sys File Commands @table @code @item sequence @var{boolean} @findex sequence If @var{boolean} is true, then conversation sequencing is automatically used for the remote system, so that if somebody manages to spoof as the remote system, it will be detected the next time the remote system actually calls. This is false by default. @item command-path @var{strings} @findex command-path Specifies the path (a list of whitespace separated directories) to be searched to locate commands to execute. This is only used for commands requested by @command{uux}, not for chat programs. The default is from @file{policy.h}. @item commands @var{strings} @findex commands The list of commands which the remote system is permitted to execute locally. For example: @samp{commands rnews rmail}. If the value is @samp{ALL} (case significant), all commands may be executed. The default is @samp{rnews rmail}. @item free-space @var{number} @findex free-space Specify the minimum amount of file system space (in bytes) to leave free after receiving a file. If the incoming file will not fit, it will be rejected. This initial rejection will only work when talking to another instance of this package, since older UUCP packages do not provide the file size of incoming files. Also, while a file is being received, @command{uucico} will periodically check the amount of free space. If it drops below the amount given by the @code{free-space} command, the file transfer will be aborted. The default amount of space to leave free is from @file{policy.h}. This file space checking may not work on all systems. @item pubdir @var{string} @findex pubdir in sys file Specifies the public directory that is used when @samp{~} is specifed in a file transfer or a list of directories. This essentially overrides the public directory specified in the main configuration file for this system only. The default is the public directory specified in the main configuration file (which defaults to a value from @file{policy.h}). @item debug @var{string} @dots{} @findex debug in sys file Set additional debugging for calls to or from the system. This may be used to debug a connection with a specific system. It is particularly useful when debugging incoming calls, since debugging information will be generated whenever the call comes in. See the @code{debug} command in the main configuration file (@pxref{Debugging Levels}) for more details. The debugging information specified here is in addition to that specified in the main configuration file or on the command line. @item max-remote-debug @var{string} @dots{} @findex max-remote-debug When the system calls in, it may request that the debugging level be set to a certain value. The @code{max-remote-debug} command may be used to put a limit on the debugging level which the system may request, to avoid filling up the disk with debugging information. Only the debugging types named in the @code{max-remote-debug} command may be turned on by the remote system. To prohibit any debugging, use @samp{max-remote-debug none}. @end table @node Default sys File Values, , Miscellaneous (sys), sys File @subsection Default sys File Values The following are used as default values for all systems; they can be considered as appearing before the start of the file. @example time Never chat "" \r\c ogin:-BREAK-ogin:-BREAK-ogin: \L word: \P chat-timeout 10 callback n sequence n request y transfer y local-send / remote-send ~ local-receive ~ remove-receive ~ command-path [ from @file{policy.h} ] commands rnews rmail max-remote-debug abnormal,chat,handshake @end example @node port File, dial File, sys File, Configuration Files @section The Port Configuration File @cindex port file @cindex port configuration file @cindex configuration file (port) The port files may be used to name and describe ports. By default there is a single port file, named @file{port} in the directory @var{newconfigdir}. This may be overridden by the @code{portfile} command in the main configuration file; see @ref{Configuration File Names}. Any commands in a port file before the first @code{port} command specify defaults for all ports in the file; however, since the @code{type} command must appear before all other commands for a port, the defaults are only useful if all ports in the file are of the same type (this restriction may be lifted in a later version). All commands after a @code{port} command up to the next @code{port} command then describe that port. There are different types of ports; each type supports its own set of commands. Each command indicates which types of ports support it. There may be many ports with the same name; if a system requests a port by name then each port with that name will be tried until an unlocked one is found. @table @code @item port @var{string} @findex port in port file Introduces and names a port. @item type @var{string} @findex type Define the type of port. The default is @samp{modem}. If this command appears, it must immediately follow the @code{port} command. The type defines what commands are subsequently allowed. Currently the types are: @table @samp @item modem For a modem hookup. @item stdin For a connection through standard input and standard output, as when @command{uucico} is run as a login shell. @item direct For a direct connection to another system. @item tcp For a connection using TCP. @item tli For a connection using TLI. @item pipe For a connection through a pipe running another program. @end table @item protocol @var{string} @findex protocol in port file Specify a list of protocols to use for this port. This is just like the corresponding command for a system (@pxref{Protocol Selection}). A protocol list for a system takes precedence over a list for a port. @item protocol-parameter @var{character} @var{strings} [ any type ] @findex protocol-parameter in port file The same command as the @code{protocol-parameter} command used for systems (@pxref{Protocol Selection}). This one takes precedence. @item seven-bit @var{boolean} [ any type ] @findex seven-bit in port file This is only used during protocol negotiation; if the argument is true, it forces the selection of a protocol which works across a seven-bit link. It does not prevent eight bit characters from being transmitted. The default is false. @item reliable @var{boolean} [ any type ] @findex reliable in port file This is only used during protocol negotiation; if the argument is false, it forces the selection of a protocol which works across an unreliable communication link. The default is true. It would be more common to specify this for a dialer rather than a port. @item half-duplex @var{boolean} [ any type ] @findex half-duplex in port file If the argument is true, it means that the port only supports half-duplex connections. This only affects bidirectional protocols, and causes them to not do bidirectional transfers. @item device @var{string} [ modem, direct and tli only ] @findex device Names the device associated with this port. If the device is not named, the port name is taken as the device. Device names are system dependent. On Unix, a modem or direct connection might be something like @file{/dev/ttyd0}; a TLI port might be @file{/dev/inet/tcp}. @itemx speed @var{number} [modem and direct only ] @findex speed in port file @item baud @var{number} [ modem and direct only ] @findex baud in port file The speed this port runs at. If a system specifies a speed but no port name, then all ports which match the speed will be tried in order. If the speed is not specified here and is not specified by the system, the natural speed of the port will be used by default. @itemx speed-range @var{number} @var{number} [ modem only ] @findex speed-range @item baud-range @var{number} @var{number} [ modem only ] @findex baud-range Specify a range of speeds this port can run at. The first number is the minimum speed, the second number is the maximum speed. These numbers will be used when matching a system which specifies a desired speed. The simple @code{speed} (or @code{baud}) command is still used to determine the speed to run at if the system does not specify a speed. For example, the command @samp{speed-range 300 19200} means that the port will match any system which uses a speed from 300 to 19200 baud (and will use the speed specified by the system); this could be combined with @samp{speed 2400}, which means that when this port is used with a system that does not specify a speed, the port will be used at 2400 baud. @item carrier @var{boolean} [ modem and direct only ] @findex carrier in port file The argument indicates whether the port supports carrier. If a modem port does not support carrier, the carrier detect signal will never be required on this port, regardless of what the modem chat script indicates. The default for a modem port is true. If a direct port supports carrier, the port will be set to expect carrier whenever it is used. The default for a direct port is false. @item hardflow @var{boolean} [ modem and direct only ] @findex hardflow The argument indicates whether the port supports hardware flow control. If it does not, hardware flow control will not be turned on for this port. The default is true. Hardware flow control is only supported on some systems. @item dial-device @var{string} [ modem only ] @findex dial-device Dialing instructions should be output to the named device, rather than to the normal port device. The default is to output to the normal port device. @item dialer @var{string} [ modem only ] @findex dialer in port file Name a dialer to use. The information is looked up in the dial file. There is no default. Some sort of dialer information must be specified to call out on a modem. @item dialer @var{string} @dots{} [ modem only ] If more than one string follows the @code{dialer} command, the strings are treated as a command that might appear in the dial file (@pxref{dial File}). If a dialer is named (by using the first form of this command, described just above), these commands are ignored. They may be used to specify dialer information directly in simple situations without needing to go to a separate file. There is no default. Some sort of dialer information must be specified to call out on a modem. @item dialer-sequence @var{strings} [ modem or tcp or tli only ] @findex dialer-sequence Name a sequence of dialers and tokens (phone numbers) to use. The first argument names a dialer, and the second argument names a token. The third argument names another dialer, and so on. If there are an odd number of arguments, the phone number specified with a @code{phone} command in the system file is used as the final token. The token is what is used for @kbd{\D} or @kbd{\T} in the dialer chat script. If the token in this string is @kbd{\D}, the system phone number will be used; if it is @kbd{\T}, the system phone number will be used after undergoing dialcodes translation. A missing final token is taken as @kbd{\D}. This command currently does not work if @code{dial-device} is specified; to handle this correctly will require a more systematic notion of chat scripts. Moreover, the @code{complete} and @code{abort} chat scripts, the protocol parameters, and the @code{carrier} and @code{dtr-toggle} commands are ignored for all but the first dialer. This command basically lets you specify a sequence of chat scripts to use. For example, the first dialer might get you to a local network and the second dialer might describe how to select a machine from the local network. This lets you break your dialing sequence into simple modules, and may make it easier to share dialer entries between machines. This command is to only way to use a chat script with a TCP port. This can be useful when using a modem which is accessed via TCP. When this command is used with a TLI port, then if the first dialer is @samp{TLI} or @samp{TLIS} the first token is used as the address to connect to. If the first dialer is something else, or if there is no token, the address given by the @code{address} command is used (@pxref{Placing the Call}). Escape sequences in the address are expanded as they are for chat script expect strings (@pxref{Chat Scripts}). The different between @samp{TLI} and @samp{TLIS} is that the latter implies the command @samp{stream true}. These contortions are all for HDB compatibility. Any subsequent dialers are treated as they are for a TCP port. @item lockname @var{string} [ modem and direct only ] @findex lockname Give the name to use when locking this port. On Unix, this is the name of the file that will be created in the lock directory. It is used as is, so on Unix it should generally start with @samp{LCK..}. For example, if a single port were named both @file{/dev/ttycu0} and @file{/dev/tty0} (perhaps with different characteristics keyed on the minor device number), then the command @code{lockname LCK..ttycu0} could be used to force the latter to use the same lock file name as the former. @item service @var{string} [ tcp only ] @findex service Name the TCP port number to use. This may be a number. If not, it will be looked up in @file{/etc/services}. If this is not specified, the string @samp{uucp} is looked up in @file{/etc/services}. If it is not found, port number 540 (the standard UUCP-over-TCP port number) will be used. @item version @var{string} [ tcp only ] @findex version Specify the IP version number to use. The default is @samp{0}, which permits any version. The other possible choices are @samp{4}, which requires @samp{IPv4}, or @samp{6}, which requires @samp{IPv6}. Normally it is not necessary to use this command, but in some cases, as @samp{IPv6} is rolled out across the Internet, it may be necessary to require UUCP to use a particular type of connection. @item push @var{strings} [ tli only ] @findex push Give a list of modules to push on to the TLI stream. @item stream @var{boolean} [ tli only ] @findex stream If this is true, and the @code{push} command was not used, the @samp{tirdwr} module is pushed on to the TLI stream. @item server-address @var{string} [ tli only ] @findex server-address Give the address to use when running as a TLI server. Escape sequences in the address are expanded as they are for chat script expect strings (@pxref{Chat Scripts}). The string is passed directly to the TLI @code{t_bind} function. The value needed may depend upon your particular TLI implementation. Check the manual pages, and, if necessary, try writing some sample programs. For AT&T 3B2 System V Release 3 using the Wollongong TCP/IP stack, which is probably typical, the format of TLI string is @samp{SSPPIIII}, where @samp{SS} is the service number (for TCP, this is 2), @samp{PP} is the TCP port number, and @samp{IIII} is the Internet address. For example, to accept a connection from on port 540 from any interface, use @samp{server-address \x00\x02\x02\x1c\x00\x00\x00\x00}. To only accept connections from a particular interface, replace the last four digits with the network address of the interface. (Thanks to Paul Pryor for the information in this paragraph). @item command @var{strings} [ pipe only ] @findex command Give the command, with arguments, to run when using a pipe port type. When a port of this type is used, the command is executed and @command{uucico} communicates with it over a pipe. This permits @command{uucico} or @command{cu} to communicate with another system which can only be reached through some unusual means. A sample use might be @samp{command /bin/rlogin -E -8 -l @var{login} @var{system}}. The command is run with the full privileges of UUCP; it is responsible for maintaining security. @end table @node dial File, UUCP Over TCP, port File, Configuration Files @section The Dialer Configuration File @cindex dial file @cindex dialer configuration file @cindex configuration file (dial) The dialer configuration files define dialers. By default there is a single dialer file, named @file{dial} in the directory @var{newconfigdir}. This may be overridden by the @code{dialfile} command in the main configuration file; see @ref{Configuration File Names}. Any commands in the file before the first @code{dialer} command specify defaults for all the dialers in the file. All commands after a @code{dialer} command up to the next @code{dialer} command are associated with the named dialer. @table @code @item dialer @var{string} @findex dialer in dial file Introduces and names a dialer. @item chat @var{strings} @findex chat in dial file @item chat-timeout @var{number} @findex chat-timeout in dial file @item chat-fail @var{string} @findex chat-fail in dial file @item chat-seven-bit @var{boolean} @findex chat-seven-bit in dial file @item chat-program @var{strings} @findex chat-program in dial file Specify a chat script to be used to dial the phone. This chat script is used before the login chat script in the @file{sys} file, if any (@pxref{Logging In}). For full details on chat scripts, see @ref{Chat Scripts}. The @command{uucico} daemon will sleep for one second between attempts to dial out on a modem. If your modem requires a longer wait period, you must start your chat script with delays (@samp{\d} in a send string). The chat script will be read from and sent to the port specified by the @code{dial-device} command for the port, if there is one. The following escape addition escape sequences may appear in send strings: @table @kbd @item \D send phone number without dialcode translation @item \T send phone number with dialcode translation @end table See the description of the dialcodes file (@pxref{Configuration File Names}) for a description of dialcode translation. If both the port and the dialer support carrier, as set by the @code{carrier} command in the port file and the @code{carrier} command in the dialer file, then every chat script implicitly begins with @kbd{\M} and ends with @kbd{\m}. There is no default chat script for dialers. The following additional escape sequences may be used in @code{chat-program}: @table @kbd @item \D phone number without dialcode translation @item \T phone number with dialcode translation @end table If the program changes the port in any way (e.g., sets parity) the changes will be preserved during protocol negotiation, but once the protocol is selected it will change the port settings. @item dialtone @var{string} @findex dialtone A string to output when dialing the phone number which causes the modem to wait for a secondary dial tone. This is used to translate the @kbd{=} character in a phone number. The default is a comma. @item pause @var{string} @findex pause A string to output when dialing the phone number which causes the modem to wait for 1 second. This is used to translate the @kbd{-} character in a phone number. The default is a comma. @item carrier @var{boolean} @findex carrier in dial file An argument of true means that the dialer supports the modem carrier signal. After the phone number is dialed, @command{uucico} will require that carrier be on. One some systems, it will be able to wait for it. If the argument is false, carrier will not be required. The default is true. @item carrier-wait @var{number} @findex carrier-wait If the port is supposed to wait for carrier, this may be used to indicate how many seconds to wait. The default is 60 seconds. Only some systems support waiting for carrier. @item dtr-toggle @var{boolean} @var{boolean} @findex dtr-toggle If the first argument is true, then DTR is toggled before using the modem. This is only supported on some systems and some ports. The second @var{boolean} need not be present; if it is, and it is true, the program will sleep for 1 second after toggling DTR. The default is to not toggle DTR. @need 500 @item complete-chat @var{strings} @findex complete-chat @item complete-chat-timeout @var{number} @findex complete-chat-timeout @item complete-chat-fail @var{string} @findex complete-chat-fail @item complete-chat-seven-bit @var{boolean} @findex complete-chat-seven-bit @item complete-chat-program @var{strings} @findex complete-chat-program These commands define a chat script (@pxref{Chat Scripts}) which is run when a call is finished normally. This allows the modem to be reset. There is no default. No additional escape sequences may be used. @item complete @var{string} @findex complete This is a simple use of @code{complete-chat}. It is equivalent to @code{complete-chat "" @var{string}}; this has the effect of sending @var{string} to the modem when a call finishes normally. @item abort-chat @var{strings} @findex abort-chat @item abort-chat-timeout @var{number} @findex abort-chat-timeout @item abort-chat-fail @var{string} @findex abort-chat-fail @item abort-chat-seven-bit @var{boolean} @findex abort-chat-seven-bit @item abort-chat-program @var{strings} @findex abort-chat-program These commands define a chat script (@pxref{Chat Scripts}) to be run when a call is aborted. They may be used to interrupt and reset the modem. There is no default. No additional escape sequences may be used. @item abort @var{string} @findex abort This is a simple use of @code{abort-chat}. It is equivalent to @code{abort-chat "" @var{string}}; this has the effect of sending @var{string} to the modem when a call is aborted. @item protocol-parameter @var{character} @var{strings} @findex protocol-parameter in dial file Set protocol parameters, just like the @code{protocol-parameter} command in the system configuration file or the port configuration file; see @ref{Protocol Selection}. These parameters take precedence, then those for the port, then those for the system. @item seven-bit @var{boolean} @findex seven-bit in dial file This is only used during protocol negotiation; if it is true, it forces selection of a protocol which works across a seven-bit link. It does not prevent eight bit characters from being transmitted. The default is false. It would be more common to specify this for a port than for a dialer. @item reliable @var{boolean} @findex reliable in dial file This is only used during protocol negotiation; if it is false, it forces selection of a protocol which works across an unreliable communication link. The default is true. @item half-duplex @var{boolean} [ any type ] @findex half-duplex in dial file If the argument is true, it means that the dialer only supports half-duplex connections. This only affects bidirectional protocols, and causes them to not do bidirectional transfers. @end table @node UUCP Over TCP, Security, dial File, Configuration Files @section UUCP Over TCP If your system has a Berkeley style socket library, or a System V style TLI interface library, you can compile the code to permit making connections over TCP. Specifying that a system should be reached via TCP is easy, but nonobvious. @menu * TCP Client:: Connecting to Another System Over TCP * TCP Server:: Running a TCP Server @end menu @node TCP Client, TCP Server, UUCP Over TCP, UUCP Over TCP @subsection Connecting to Another System Over TCP If you are using the new style configuration files (@pxref{Configuration Files}), add the line @samp{port type tcp} to the entry in the @file{sys} file. By default UUCP will get the port number by looking up @samp{uucp} in @file{/etc/services}; if the @samp{uucp} service is not defined, port 540 will be used. You can set the port number to use with the command @samp{port service @var{xxx}}, where @var{xxx} can be either a number or a name to look up in @file{/etc/services}. You can specify the address of the remote host with @samp{address @var{a.b.c}}; if you don't give an address, the remote system name will be used. You should give an explicit chat script for the system when you use TCP; the default chat script begins with a carriage return, which will not work with some UUCP TCP servers. If you are using V2 configuration files, add a line like this to @file{L.sys}: @example @var{sys} Any TCP uucp @var{host}.@var{domain} chat-script @end example This will make an entry for system @var{sys}, to be called at any time, over TCP, using port number @samp{uucp} (as found in @file{/etc/services}; this may be specified as a number), using remote host @file{@var{host}.@var{domain}}, with some chat script. If you are using HDB configuration files, add a line like this to Systems: @example @var{sys} Any TCP - @var{host}.@var{domain} chat-script @end example and a line like this to @file{Devices}: @example TCP uucp - - @end example You only need one line in @file{Devices} regardless of how many systems you contact over TCP. This will make an entry for system @var{sys}, to be called at any time, over TCP, using port number @samp{uucp} (as found in @file{/etc/services}; this may be specified as a number), using remote host @file{@var{host}.@var{domain}}, with some chat script. @node TCP Server, , TCP Client, UUCP Over TCP @subsection Running a TCP Server The @command{uucico} daemon may be run as a TCP server. To use the default port number, which is a reserved port, @command{uucico} must be invoked by the superuser (or it must be set user ID to the superuser, but I don't recommend doing that). You must define a port, either using the port file (@pxref{port File}), if you are using the new configuration method, or with an entry in @file{Devices} if you are using HDB; there is no way to define a port using V2. If you are using HDB the port must be named @samp{TCP}; a line as shown above will suffice. You can then start @command{uucico} as @samp{uucico -p TCP} (after the @option{-p}, name the port; in HDB it must be @samp{TCP}). This will wait for incoming connections, and fork off a child for each one. Each connection will be prompted with @samp{login:} and @samp{Password:}; the results will be checked against the UUCP (not the system) password file (@pxref{Configuration File Names}). Another way to run a UUCP TCP server is to use the BSD @command{uucpd} program. Yet another way to run a UUCP TCP server is to use @command{inetd}. Arrange for @command{inetd} to start up @command{uucico} with the @option{-l} switch. This will cause @command{uucico} to prompt with @samp{login:} and @samp{Password:} and check the results against the UUCP (not the system) password file (you may want to also use the @option{-D} switch to avoid a fork, which in this case is unnecessary). @node Security, , UUCP Over TCP, Configuration Files @section Security This discussion of UUCP security applies only to Unix. It is a bit cursory; suggestions for improvement are solicited. UUCP is traditionally not very secure. Taylor UUCP addresses some security issues, but is still far from being a secure system. If security is very important to you, then you should not permit any external access to your computer, including UUCP. Any opening to the outside world is a potential security risk. When local users use UUCP to transfer files, Taylor UUCP can do little to secure them from each other. You can allow somewhat increased security by putting the owner of the UUCP programs (normally @code{uucp}) into a separate group; the use of this is explained in the following paragraphs, which refer to this separate group as @code{uucp-group}. When the @command{uucp} program is invoked to copy a file to a remote system, it will, by default, copy the file into the UUCP spool directory. When the @command{uux} program is used, the @option{-C} switch must be used to copy the file into the UUCP spool directory. In any case, once the file has been copied into the spool directory, other local users will not be able to access it. When a file is requested from a remote system, UUCP will only permit it to be placed in a directory which is writable by the requesting user. The directory must also be writable by UUCP. A local user can create a directory with a group of @code{uucp-group} and set the mode to permit group write access. This will allow the file be requested without permitting it to be viewed by any other user. There is no provision for security for @command{uucp} requests (as opposed to @command{uux} requests) made by a user on a remote system. A file sent over by a remote request may only be placed in a directory which is world writable, and the file will be world readable and writable. This will permit any local user to destroy or replace the contents of the file. A file requested by a remote system must be world readable, and the directory it is in must be world readable. Any local user will be able to examine, although not necessarily modify, the file before it is sent. There are some security holes and race conditions that apply to the above discussion which I will not elaborate on. They are not hidden from anybody who reads the source code, but they are somewhat technical and difficult (though scarcely impossible) to exploit. Suffice it to say that even under the best of conditions UUCP is not completely secure. For many sites, security from remote sites is a more important consideration. Fortunately, Taylor UUCP does provide some support in this area. The greatest security is provided by always dialing out to the other site. This prevents anybody from pretending to be the other site. Of course, only one side of the connection can do this. If remote dialins must be permitted, then it is best if the dialin line is used only for UUCP. If this is the case, then you should create a call-in password file (@pxref{Configuration File Names}) and let @command{uucico} do its own login prompting. For example, to let remote sites log in on a port named @samp{entry} in the port file (@pxref{port File}), you might invoke @samp{uucico -e -p entry}. This would cause @command{uucico} to enter an endless loop of login prompts and daemon executions. The advantage of this approach is that even if remote users break into the system by guessing or learning the password, they will only be able to do whatever @command{uucico} permits them to do. They will not be able to start a shell on your system. If remote users can dial in and log on to your system, then you have a security hazard more serious than that posed by UUCP. But then, you probably knew that already. Once your system has connected with the remote UUCP, there is a fair amount of control you can exercise. You can use the @code{remote-send} and @code{remote-receive} commands to control the directories the remote UUCP can access. You can use the @code{request} command to prevent the remote UUCP from making any requests of your system at all; however, if you do this it will not even be able to send you mail or news. If you do permit remote requests, you should be careful to restrict what commands may be executed at the remote system's request. The default is @command{rmail} and @command{rnews}, which will suffice for most systems. If different remote systems call in and they must be granted different privileges (perhaps some systems are within the same organization and some are not) then the @code{called-login} command should be used for each system to require that they use different login names. Otherwise, it would be simple for a remote system to use the @code{myname} command and pretend to be a different system. The @code{sequence} command can be used to detect when one system pretended to be another, but, since the sequence numbers must be reset manually after a failed handshake, this can sometimes be more trouble than it's worth. @c START-OF-FAQ @ignore This chapter is used to generate the comp.mail.uucp UUCP Internals FAQ, as well as being part of the Taylor UUCP manual. Text that should appear only in the manual is bracketed by ifclear faq. Text that should appear only in the FAQ is bracketed by ifset faq. @end ignore @ifset faq @paragraphindent asis @format Subject: UUCP Internals Frequently Asked Questions Newsgroups: comp.mail.uucp,comp.answers,news.answers Followup-To: comp.mail.uucp Reply-To: ian@@airs.com (Ian Lance Taylor) Keywords: UUCP, protocol, FAQ Approved: news-answers-request@@MIT.Edu Archive-name: uucp-internals Version: $Revision: 1.132 $ Last-modified: $Date: 2002/03/07 05:56:27 $ @end format @end ifset @node Protocols, Hacking, Configuration Files, Top @chapter UUCP Protocol Internals @ifclear faq This chapter describes how the various UUCP protocols work, and discusses some other internal UUCP issues. This chapter is quite technical. You do not need to understand it, or even read it, in order to use Taylor UUCP. It is intended for people who are interested in how the UUCP code works. The information in this chapter is posted monthly to the Usenet newsgroups @samp{comp.mail.uucp}, @samp{news.answers}, and @samp{comp.answers}. The posting is available from any @samp{news.answers} archive site, such as @samp{rtfm.mit.edu}. If you plan to use this information to write a UUCP program, please make sure you get the most recent version of the posting, in case there have been any corrections. @end ifclear @ifset faq Recent changes: @itemize @bullet @item Conversion to Texinfo format. @item Description of the @samp{E} command. @item Description of optional number following @samp{-N} and @samp{ROKN} in UUCP protocol startup. @item Detailed description of the @samp{y} protocol. @item Mention the name uuxqt uses for lock files. @end itemize This article was written by Ian Lance Taylor @samp{} and I may even update it periodically. Please send me mail about suggestions or inaccuracies. This article describes how the various UUCP protocols work, and discusses some other internal UUCP issues. It does not describe how to configure UUCP, nor how to solve UUCP connection problems, nor how to deal with UUCP mail. I do not know of any FAQ postings on these topics. There are some documents on the net describing UUCP configuration, but I can not keep an up to date list here; try using archie. If you haven't read the @samp{news.announce.newusers} articles, read them. This article is in digest format. Some newsreaders will be able to break it apart into separate articles. Please don't ask me how to do this, though. This article covers the following topics. If questions about one of these topics is posted to @samp{comp.mail.uucp}, please send mail to the poster referring her or him to this FAQ. There is no reason to post a followup, as most of us know the answer already. @end ifset @menu * UUCP Protocol Sources:: Sources for UUCP Protocol Information * UUCP Grades:: UUCP Grades * UUCP Lock Files:: UUCP Lock Files * Execution File Format:: Execution File Format * UUCP Protocol:: UUCP Protocol * g Protocol:: g protocol * f Protocol:: f protocol * t Protocol:: t protocol * e Protocol:: e protocol * Big G Protocol:: G protocol * i Protocol:: i protocol * j Protocol:: j protocol * x Protocol:: x protocol * y Protocol:: y protocol * d Protocol:: d protocol * h Protocol:: h protocol * v Protocol:: v protocol @end menu @ifset faq @format UUCP Protocol Sources Alarm in Debugging Output UUCP Grades UUCP Lock Files Execution File Format UUCP Protocol UUCP @samp{g} Protocol UUCP @samp{f} Protocol UUCP @samp{t} Protocol UUCP @samp{e} Protocol UUCP @samp{G} Protocol UUCP @samp{i} Protocol UUCP @samp{j} Protocol UUCP @samp{x} Protocol UUCP @samp{y} Protocol UUCP @samp{d} Protocol UUCP @samp{h} Protocol UUCP @samp{v} Protocol Thanks ---------------------------------------------------------------------- From: UUCP Protocol Sources Subject: UUCP Protocol Sources @end format @end ifset @node UUCP Protocol Sources, UUCP Grades, Protocols, Protocols @section UUCP Protocol Sources @quotation ``Unix-to-Unix Copy Program,'' said PDP-1. ``You will never find a more wretched hive of bugs and flamers. We must be cautious.'' @flushright ---DECWars @end flushright @end quotation I took a lot of the information from Jamie E. Hanrahan's paper in the Fall 1990 DECUS Symposium, and from @cite{Managing UUCP and Usenet} by Tim O'Reilly and Grace Todino (with contributions by several other people). The latter includes most of the former, and is published by @example O'Reilly & Associates, Inc. 103 Morris Street, Suite A Sebastopol, CA 95472 @end example It is currently in its tenth edition. The ISBN number is @samp{0-937175-93-5}. Some information is originally due to a Usenet article by Chuck Wegrzyn. The information on execution files comes partially from Peter Honeyman. The information on the @samp{g} protocol comes partially from a paper by G.L.@: Chesson of Bell Laboratories, partially from Jamie E. Hanrahan's paper, and partially from source code by John Gilmore. The information on the @samp{f} protocol comes from the source code by Piet Berteema. The information on the @samp{t} protocol comes from the source code by Rick Adams. The information on the @samp{e} protocol comes from a Usenet article by Matthias Urlichs. The information on the @samp{d} protocol comes from Jonathan Clark, who also supplied information about QFT. The UUPlus information comes straight from Christopher J. Ambler, of UUPlus Development; it applies to version 1.52 and up of the shareware version of UUPlus Utilities, called FSUUCP 1.52, but referred to in this article as UUPlus. Although there are few books about UUCP, there are many about networks and protocols in general. I recommend two non-technical books which describe the sorts of things that are available on the network: @cite{The Whole Internet}, by Ed Krol, and @cite{Zen and the Art of the Internet}, by Brendan P. Kehoe. Good technical discussions of networking issues can be found in @cite{Internetworking with TCP/IP}, by Douglas E. Comer and David L. Stevens and in @cite{Design and Validation of Computer Protocols} by Gerard J. Holzmann. @ifset faq @c Note that this section is only in the FAQ, since it does not fit in @c here in the manual. @format ------------------------------ From: Alarm in Debugging Output Subject: Alarm in Debugging Output Alarm in Debugging Output ========================= @end format The debugging output of many versions of UUCP will include messages like @samp{alarm 1} or @samp{pkcget: alarm 1}. Taylor UUCP does not use the word @samp{alarm}, but will instead log messages like @samp{Timed out waiting for packet}. These types of messages mean that the UUCP package has timed out while waiting for some sort of response from the remote system. If it happens consistently when trying to transfer a particular file, then the most likely problem is that one of the modems will not transmit the XON or XOFF characters. Several UUCP protocols require an eight bit clean connection, which means that the modems must treat XON or XOFF as normal data characters, not as flow control signals. This should always be checked first. Other possible problems are that the modems have simply dropped their connection, or perhaps on one side or the other the serial buffer is overflowing and dropping characters. Another possibility is that the UUCP packages disagree about some aspect of the UUCP protocol, which is uncommon but does happen occasionally. Using the information in the following sections, you should be able to figure out what type of data your UUCP was expecting to receive. This may give some indication as to exactly what the problem is. It is difficult to be more specific, since there are many possiblities. @format ------------------------------ From: UUCP Grades Subject: UUCP Grades @end format @end ifset @node UUCP Grades, UUCP Lock Files, UUCP Protocol Sources, Protocols @section UUCP Grades @cindex grades implementation Modern UUCP packages support a priority grade for each command. The grades generally range from @kbd{A} (the highest) to @kbd{Z} followed by @kbd{a} to @kbd{z}. Some UUCP packages (including Taylor UUCP) also support @kbd{0} to @kbd{9} before @kbd{A}. Some UUCP packages may permit any ASCII character as a grade. On Unix, these grades are encoded in the name of the command file created by @command{uucp} or @command{uux}. A command file name generally has the form @file{C.nnnngssss} where @samp{nnnn} is the remote system name for which the command is queued, @samp{g} is a single character grade, and @samp{ssss} is a four character sequence number. For example, a command file created for the system @samp{airs} at grade @samp{Z} might be named @file{C.airsZ2551}. The remote system name will be truncated to seven characters, to ensure that the command file name will fit in the 14 character file name limit of the traditional Unix file system. UUCP packages which have no other means of distinguishing which command files are intended for which systems thus require all systems they connect to to have names that are unique in the first seven characters. Some UUCP packages use a variant of this format which truncates the system name to six characters. HDB and Taylor UUCP use a different spool directory format, which allows up to fourteen characters to be used for each system name. The sequence number in the command file name may be a decimal integer, or it may be a hexadecimal integer, or it may contain any alphanumeric character. Different UUCP packages are different. @ifclear faq Taylor UUCP uses any alphanumeric character. @end ifclear UUPlus Utilities (as FSUUCP, a shareware DOS based UUCP and news package) uses up to 8 characters for file names in the spool (this is a DOS file system limitation; actually, with the extension, 11 characters are available, but FSUUCP reserves that for future use). FSUUCP defaults mail to grade @samp{D}, and news to grade @samp{N}, except that when the grade of incoming mail can be determined, that grade is preserved if the mail is forwarded to another system. The default grades may be changed by editing the @file{LIB/MAILRC} file for mail, or the @file{UUPLUS.CFG} file for news. UUPC/extended for DOS, OS/2 and Windows NT handles mail at grade @samp{C}, news at grade @samp{d}, and file transfers at grade @samp{n}. The UUPC/extended @command{UUCP} and @command{RMAIL} commands accept grades to override the default, the others do not. I do not know how command grades are handled in other non-Unix UUCP packages. Modern UUCP packages allow you to restrict file transfer by grade depending on the time of day. Typically this is done with a line in the @file{Systems} (or @file{L.sys}) file like this: @example airs Any/Z,Any2305-0855 ... @end example This allows grades @samp{Z} and above to be transferred at any time. Lower grades may only be transferred at night. I believe that this grade restriction applies to local commands as well as to remote commands, but I am not sure. It may only apply if the UUCP package places the call, not if it is called by the remote system. Taylor UUCP can use the @code{timegrade} and @code{call-timegrade} commands to achieve the same effect. @ifclear faq @xref{When to Call}. @end ifclear It supports the above format when reading @file{Systems} or @file{L.sys}. UUPC/extended provides the @code{symmetricgrades} option to announce the current grade in effect when calling the remote system. UUPlus allows specification of the highest grade accepted on a per-call basis with the @option{-g} option in @command{UUCICO}. This sort of grade restriction is most useful if you know what grades are being used at the remote site. The default grades used depend on the UUCP package. Generally @command{uucp} and @command{uux} have different defaults. A particular grade can be specified with the @option{-g} option to @command{uucp} or @command{uux}. For example, to request execution of @command{rnews} on @samp{airs} with grade @samp{d}, you might use something like @example uux -gd - airs!rnews < article @end example Uunet queues up mail at grade @samp{C}, but increases the grade based on the size. News is queued at grade @samp{d}, and file transfers at grade @samp{n}. The example above would allow mail (below some large size) to be received at any time, but would only permit news to be transferred at night. @ifset faq @format ------------------------------ From: UUCP Lock Files Subject: UUCP Lock Files @end format @end ifset @node UUCP Lock Files, Execution File Format, UUCP Grades, Protocols @section UUCP Lock Files @cindex lock files This discussion applies only to Unix. I have no idea how UUCP locks ports on other systems. UUCP creates files to lock serial ports and systems. On most, if not all, systems, these same lock files are also used by @command{cu} to coordinate access to serial ports. On some systems @command{getty} also uses these lock files, often under the name @command{uugetty}. The lock file normally contains the process ID of the locking process. This makes it easy to determine whether a lock is still valid. The algorithm is to create a temporary file and then link it to the name that must be locked. If the link fails because a file with that name already exists, the existing file is read to get the process ID. If the process still exists, the lock attempt fails. Otherwise the lock file is deleted and the locking algorithm is retried. Older UUCP packages put the lock files in the main UUCP spool directory, @file{/usr/spool/uucp}. HDB UUCP generally puts the lock files in a directory of their own, usually @file{/usr/spool/locks} or @file{/etc/locks}. The original UUCP lock file format encodes the process ID as a four byte binary number. The order of the bytes is host-dependent. HDB UUCP stores the process ID as a ten byte ASCII decimal number, with a trailing newline. For example, if process 1570 holds a lock file, it would contain the eleven characters space, space, space, space, space, space, one, five, seven, zero, newline. Some versions of UUCP add a second line indicating which program created the lock (@command{uucp}, @command{cu}, or @command{getty/uugetty}). I have also seen a third type of UUCP lock file which does not contain the process ID at all. The name of the lock file is traditionally @file{LCK..} followed by the base name of the device. For example, to lock @file{/dev/ttyd0} the file @file{LCK..ttyd0} would be created. On SCO Unix, the last letter of the lock file name is always forced to lower case even if the device name ends with an upper case letter. System V Release 4 UUCP names the lock file using the major and minor device numbers rather than the device name. The file is named @file{LK.@var{XXX}.@var{YYY}.@var{ZZZ}}, where @var{XXX}, @var{YYY} and @var{ZZZ} are all three digit decimal numbers. @var{XXX} is the major device number of the device holding the directory holding the device file (e.g., @file{/dev}). @var{YYY} is the major device number of the device file itself. @var{ZZZ} is the minor device number of the device file itself. If @code{s} holds the result of passing the device to the stat system call (e.g., @code{stat ("/dev/ttyd0", &s)}), the following line of C code will print out the corresponding lock file name: @example printf ("LK.%03d.%03d.%03d", major (s.st_dev), major (s.st_rdev), minor (s.st_rdev)); @end example The advantage of this system is that even if there are several links to the same device, they will all use the same lock file name. When two or more instances of @command{uuxqt} are executing, some sort of locking is needed to ensure that a single execution job is only started once. I don't know how most UUCP packages deal with this. Taylor UUCP uses a lock file for each execution job. The name of the lock file is the same as the name of the @file{X.*} file, except that the initial @samp{X} is changed to an @samp{L}. The lock file holds the process ID as described above. @ifset faq @format ------------------------------ From: Execution File Format Subject: Execution File Format @end format @end ifset @node Execution File Format, UUCP Protocol, UUCP Lock Files, Protocols @section Execution File Format @cindex execution file format @cindex @file{X.*} file format UUCP @file{X.*} files control program execution. They are created by @command{uux}. They are transferred between systems just like any other file. The @command{uuxqt} daemon reads them to figure out how to execute the job requested by @command{uux}. An @file{X.*} file is simply a text file. The first character of each line is a command, and the remainder of the line supplies arguments. The following commands are defined: @table @samp @item C command This gives the command to execute, including the program and all arguments. For example, @samp{rmail ian@@airs.com}. @item U user system This names the user who requested the command, and the system from which the request came. @item I standard-input This names the file from which standard input is taken. If no standard input file is given, the standard input will probably be attached to @file{/dev/null}. If the standard input file is not from the system on which the execution is to occur, it will also appear in an @samp{F} command. @item O standard-output [system] This names the standard output file. The optional second argument names the system to which the file should be sent. If there is no second argument, the file should be created on the executing system. @item F required-file [filename-to-use] The @samp{F} command can appear multiple times. Each @samp{F} command names a file which must exist before the execution can proceed. This will usually be a file which is transferred from the system on which @command{uux} was executed, but it can also be a file from the local system or some other system. If the file is not from the local system, then the command will usually name a file in the spool directory. If the optional second argument appears, then the file should be copied to the execution directory under that name. This is necessary for any file other than the standard input file. If the standard input file is not from the local system, it will appear in both an @samp{F} command and an @samp{I} command. @item R requestor-address This is the address to which mail about the job should be sent. It is relative to the system named in the @samp{U} command. If the @samp{R} command does not appear, then mail is sent to the user named in the @samp{U} command. @item Z This command takes no arguments. It means that a mail message should be sent if the command failed. This is the default behaviour for most modern UUCP packages, and for them the @samp{Z} command does not actually do anything. @item N This command takes no arguments. It means that no mail message should be sent, even if the command failed. @item n This command takes no arguments. It means that a mail message should be sent if the command succeeded. Normally a message is sent only if the command failed. @item B This command takes no arguments. It means that the standard input should be returned with any error message. This can be useful in cases where the input would otherwise be lost. @item e This command takes no arguments. It means that the command should be processed with @file{/bin/sh}. For some packages this is the default anyhow. Most packages will refuse to execute complex commands or commands containing wildcards, because of the security holes this opens. @item E This command takes no arguments. It means that the command should be processed with the @code{execve} system call. For some packages this is the default anyhow. @item M status-file This command means that instead of mailing a message, the message should be copied to the named file on the system named by the @samp{U} command. @item Q This command takes no arguments. It means that the string arguments to all the other commands are backslash quoted. Any backslash in one of the strings should be followed by either a backslash or three octal digits. The backslash quoting is interpreted as in a C string. If the @samp{Q} command does not appear, backslashes in the strings are not treated specially. The @samp{Q} command was introduced in Taylor UUCP version 1.07. @item # comment This command is ignored, as is any other unrecognized command. @end table Here is an example. Given the following command executed on system test1 @example uux - test2!cat - test2!~ian/bar !qux '>~/gorp' @end example (this is only an example, as most UUCP systems will not permit the cat command to be executed) Taylor UUCP will produce something like the following @file{X.} file: @example U ian test1 F D.test1N003r qux O /usr/spool/uucppublic/gorp test1 F D.test1N003s I D.test1N003s C cat - ~ian/bar qux @end example The standard input will be read into a file and then transferred to the file @file{D.test1N003s} on system @samp{test2}. The file @file{qux} will be transferred to @file{D.test1N003r} on system @samp{test2}. When the command is executed, the latter file will be copied to the execution directory under the name @samp{qux}. Note that since the file @file{~ian/bar} is already on the execution system, no action need be taken for it. The standard output will be collected in a file, then copied to the file @file{/usr/spool/uucppublic/gorp} on the system @samp{test1}. @ifset faq @format ------------------------------ From: UUCP Protocol Subject: UUCP Protocol @end format @end ifset @node UUCP Protocol, g Protocol, Execution File Format, Protocols @section UUCP Protocol @cindex UUCP protocol @cindex protocol, UUCP The UUCP protocol is a conversation between two UUCP packages. A UUCP conversation consists of three parts: an initial handshake, a series of file transfer requests, and a final handshake. @menu * The Initial Handshake:: The Initial Handshake * UUCP Protocol Commands:: UUCP Protocol Commands * The Final Handshake:: The Final Handshake @end menu @node The Initial Handshake, UUCP Protocol Commands, UUCP Protocol, UUCP Protocol @subsection The Initial Handshake @cindex initial handshake Before the initial handshake, the caller will usually have logged in the called machine and somehow started the UUCP package there. On Unix this is normally done by setting the shell of the login name used to @file{/usr/lib/uucp/uucico}. All messages in the initial handshake begin with a @kbd{^P} (a byte with the octal value @samp{\020}) and end with a null byte (@samp{\000}). A few systems end these messages with a line feed character (@samp{\012}) instead of a null byte; the examples below assume a null byte is being used. Some options below are supported by QFT, which stands for Queued File Transfer, and is (or was) an internal Bell Labs version of UUCP. Taylor UUCP size negotiation was introduced by Taylor UUCP, and is also supported by DOS based UUPlus and Amiga based wUUCP and UUCP-1.17. The initial handshake goes as follows. It is begun by the called machine. @table @asis @item called: @samp{\020Shere=hostname\000} The hostname is the UUCP name of the called machine. Older UUCP packages do not output it, and simply send @samp{\020Shere\000}. @item caller: @samp{\020Shostname options\000} The hostname is the UUCP name of the calling machine. The following options may appear (or there may be none): @table @samp @item -QSEQ Report sequence number for this conversation. The sequence number is stored at both sites, and incremented after each call. If there is a sequence number mismatch, something has gone wrong (somebody may have broken security by pretending to be one of the machines) and the call is denied. If the sequence number changes on one of the machines, perhaps because of an attempted breakin or because a disk backup was restored, the sequence numbers on the two machines must be reconciled manually. @item -xLEVEL Requests the called system to set its debugging level to the specified value. This is not supported by all systems. @item -pGRADE @itemx -vgrade=GRADE Requests the called system to only transfer files of the specified grade or higher. This is not supported by all systems. Some systems support @samp{-p}, some support @samp{-vgrade=}. UUPlus allows either @samp{-p} or @samp{-v} to be specified on a per-system basis in the @file{SYSTEMS} file (@samp{gradechar} option). @item -R Indicates that the calling UUCP understands how to restart failed file transmissions. Supported only by System V Release 4 UUCP, QFT, and Taylor UUCP. @item -ULIMIT Reports the ulimit value of the calling UUCP. The limit is specified as a base 16 number in C notation (e.g., @samp{-U0x1000000}). This number is the number of 512 byte blocks in the largest file which the calling UUCP can create. The called UUCP may not transfer a file larger than this. Supported only by System V Release 4 UUCP, QFT and UUPlus. UUPlus reports the lesser of the available disk space on the spool directory drive and the ulimit variable in @file{UUPLUS.CFG}. Taylor UUCP understands this option, but does not generate it. @item -N[NUMBER] Indicates that the calling UUCP understands the Taylor UUCP size negotiation extension. Not supported by traditional UUCP packages. Supported by UUPlus. The optional number is a bitmask of features supported by the calling UUCP, and is described below. @end table @item called: @samp{\020ROK\000} There are actually several possible responses. @table @samp @item ROK The calling UUCP is acceptable, and the handshake proceeds to the protocol negotiation. Some options may also appear; see below. @item ROKN[NUMBER] The calling UUCP is acceptable, it specified @samp{-N}, and the called UUCP also understands the Taylor UUCP size limiting extensions. The optional number is a bitmask of features supported by the called UUCP, and is described below. @item RLCK The called UUCP already has a lock for the calling UUCP, which normally indicates the two machines are already communicating. @item RCB The called UUCP will call back. This may be used to avoid impostors (but only one machine out of each pair should call back, or no conversation will ever begin). @item RBADSEQ The call sequence number is wrong (see the @samp{-Q} discussion above). @item RLOGIN The calling UUCP is using the wrong login name. @item RYou are unknown to me The calling UUCP is not known to the called UUCP, and the called UUCP does not permit connections from unknown systems. Some versions of UUCP just drop the line rather than sending this message. @end table If the response is @samp{ROK}, the following options are supported by System V Release 4 UUCP and QFT. @table @samp @item -R The called UUCP knows how to restart failed file transmissions. @item -ULIMIT Reports the ulimit value of the called UUCP. The limit is specified as a base 16 number in C notation. This number is the number of 512 byte blocks in the largest file which the called UUCP can create. The calling UUCP may not send a file larger than this. Also supported by UUPlus. Taylor UUCP understands this option, but does not generate it. @item -xLEVEL I'm not sure just what this means. It may request the calling UUCP to set its debugging level to the specified value. @end table If the response is not @samp{ROK} (or @samp{ROKN}) both sides hang up the phone, abandoning the call. @item called: @samp{\020Pprotocols\000} Note that the called UUCP outputs two strings in a row. The protocols string is a list of UUCP protocols supported by the caller. Each UUCP protocol has a single character name. These protocols are discussed in more detail later in this document. For example, the called UUCP might send @samp{\020Pgf\000}. @item caller: @samp{\020Uprotocol\000} The calling UUCP selects which protocol to use out of the protocols offered by the called UUCP. If there are no mutually supported protocols, the calling UUCP sends @samp{\020UN\000} and both sides hang up the phone. Otherwise the calling UUCP sends something like @samp{\020Ug\000}. @end table Most UUCP packages will consider each locally supported protocol in turn and select the first one supported by the called UUCP. With some versions of HDB UUCP, this can be modified by giving a list of protocols after the device name in the @file{Devices} file or the @file{Systems} file. For example, to select the @samp{e} protocol in @file{Systems}, @example airs Any ACU,e ... @end example or in Devices, @example ACU,e ttyXX ... @end example Taylor UUCP provides the @code{protocol} command which may be used either for a system @ifclear faq (@pxref{Protocol Selection}) @end ifclear or a @ifclear faq port (@pxref{port File}). @end ifclear @ifset faq port. @end ifset UUPlus allows specification of the protocol string on a per-system basis in the @file{SYSTEMS} file. The optional number following a @samp{-N} sent by the calling system, or an @samp{ROKN} sent by the called system, is a bitmask of features supported by the UUCP package. The optional number was introduced in Taylor UUCP version 1.04. The number is sent as an octal number with a leading zero. The following bits are currently defined. A missing number should be taken as @samp{011}. @table @samp @item 01 UUCP supports size negotiation. @item 02 UUCP supports file restart. @item 04 UUCP supports the @samp{E} command. @item 010 UUCP requires the file size in the @samp{S} and @samp{R} commands to be in base 10. This bit is used by default if no number appears, but should not be explicitly sent. @item 020 UUCP expects a dummy string between the notify field and the size field in an @samp{S} command. This is true of SVR4 UUCP. This bit should not be used. @item 040 UUCP supports the @samp{q} option in the @samp{S}, @samp{R}, @samp{X}, and @samp{E} commands. @end table After the protocol has been selected and the initial handshake has been completed, both sides turn on the selected protocol. For some protocols (notably @samp{g}) a further handshake is done at this point. @node UUCP Protocol Commands, The Final Handshake, The Initial Handshake, UUCP Protocol @subsection UUCP Protocol Commands Each protocol supports a method for sending a command to the remote system. This method is used to transmit a series of commands between the two UUCP packages. At all times, one package is the master and the other is the slave. Initially, the calling UUCP is the master. If a protocol error occurs during the exchange of commands, both sides move immediately to the final handshake. The master will send one of five commands: @samp{S}, @samp{R}, @samp{X}, @samp{E}, or @samp{H}. Any file name referred to below is either an absolute file name beginning with @file{/}, a public directory file name beginning with @file{~/}, a file name relative to a user's home directory beginning with @file{~@var{USER}/}, or a spool directory file name. File names in the spool directory are not absolute, but instead are converted to file names within the spool directory by UUCP. They always begin with @file{C.} (for a command file created by @command{uucp} or @command{uux}), @file{D.} (for a data file created by @command{uucp}, @command{uux} or by an execution, or received from another system for an execution), or @file{X.} (for an execution file created by @command{uux} or received from another system). All the commands other than the @samp{H} command support options. The @samp{q} option indicates that the command argument strings are backslash quoted. If the @samp{q} option appears, then any backslash in one of the arguments should be followed by either a backslash or three octal digits. The backslash quoting is interpreted as in a C string. If the @samp{q} option does not appear, backslashes in the strings are not treated specially. The @samp{q} option was introduced in Taylor UUCP version 1.07. @menu * The S Command:: The S Command * The R Command:: The R Command * The X Command:: The X Command * The E Command:: The E Command * The H Command:: The H Command @end menu @node The S Command, The R Command, UUCP Protocol Commands, UUCP Protocol Commands @subsubsection The S Command @cindex S UUCP protocol command @cindex UUCP protocol S command @table @asis @item master: @samp{S @var{from} @var{to} @var{user} -@var{options} @var{temp} @var{mode} @var{notify} @var{size}} The @samp{S} and the @samp{-} are literal characters. This is a request by the master to send a file to the slave. @table @var @item from The name of the file to send. If the @samp{C} option does not appear in @var{options}, the master will actually open and send this file. Otherwise the file has been copied to the spool directory, where it is named @var{temp}. The slave ignores this field unless @var{to} is a directory, in which case the basename of @var{from} will be used as the file name. If @var{from} is a spool directory filename, it must be a data file created for or by an execution, and must begin with @file{D.}. @item to The name to give the file on the slave. If this field names a directory the file is placed within that directory with the basename of @var{from}. A name ending in @samp{/} is taken to be a directory even if one does not already exist with that name. If @var{to} begins with @file{X.}, an execution file will be created on the slave. Otherwise, if @var{to} begins with @file{D.} it names a data file to be used by some execution file. Otherwise, @var{to} should not be in the spool directory. @item user The name of the user who requested the transfer. @item options A list of options to control the transfer. The following options are defined (all options are single characters): @table @samp @item C The file has been copied to the spool directory (the master should use @var{temp} rather than @var{from}). @item c The file has not been copied to the spool directory (this is the default). @item d The slave should create directories as necessary (this is the default). @item f The slave should not create directories if necessary, but should fail the transfer instead. @item m The master should send mail to @var{user} when the transfer is complete. @item n The slave should send mail to @var{notify} when the transfer is complete. @item q Backslash quoting is applied to the @var{from}, @var{to}, @var{user}, and @var{notify} arguments. @xref{UUCP Protocol Commands}. This option was introduced in Taylor UUCP version 1.07. @end table @item temp If the @samp{C} option appears in @var{options}, this names the file to be sent. Otherwise if @var{from} is in the spool directory, @var{temp} is the same as @var{from}. Otherwise @var{temp} may be a dummy string, such as @file{D.0}. After the transfer has been succesfully completed, the master will delete the file @var{temp}. @item mode This is an octal number giving the mode of the file on the master. If the file is not in the spool directory, the slave will always create it with mode 0666, except that if (@var{mode} & 0111) is not zero (the file is executable), the slave will create the file with mode 0777. If the file is in the spool directory, some UUCP packages will use the algorithm above and some will always create the file with mode 0600. This field is ignored by UUPlus, since it is meaningless on DOS; UUPlus uses 0666 for outgoing files. @item notify This field may not be present, and in any case is only meaningful if the @samp{n} option appears in @var{options}. If the @samp{n} option appears, then, when the transfer is successfully completed, the slave will send mail to @var{notify}, which must be a legal mailing address on the slave. If a @var{size} field will appear but the @samp{n} option does not appear, @var{notify} will always be present, typically as the string @samp{dummy} or simply a pair of double quotes. @item size This field is only present when doing Taylor UUCP or SVR4 UUCP size negotiation. It is the size of the file in bytes. Taylor UUCP version 1.03 sends the size as a decimal integer, while versions 1.04 and up, and all other UUCP packages that support size negotiation, send the size in base 16 with a leading 0x. @end table The slave then responds with an @samp{S} command response. @table @samp @item SY @var{start} The slave is willing to accept the file, and file transfer begins. The @var{start} field will only be present when using file restart. It specifies the byte offset into the file at which to start sending. If this is a new file, @var{start} will be 0x0. @item SN2 The slave denies permission to transfer the file. This can mean that the destination directory may not be accessed, or that no requests are permitted. It implies that the file transfer will never succeed. @item SN4 The slave is unable to create the necessary temporary file. This implies that the file transfer might succeed later. @item SN6 This is only used by Taylor UUCP size negotiation. It means that the slave considers the file too large to transfer at the moment, but it may be possible to transfer it at some other time. @item SN7 This is only used by Taylor UUCP size negotiation. It means that the slave considers the file too large to ever transfer. @item SN8 This is only used by Taylor UUCP. It means that the file was already received in a previous conversation. This can happen if the receive acknowledgement was lost after it was sent by the receiver but before it was received by the sender. @item SN9 This is only used by Taylor UUCP (versions 1.05 and up) and UUPlus (versions 2.0 and up). It means that the remote system was unable to open another channel (see the discussion of the @samp{i} protocol for more information about channels). This implies that the file transfer might succeed later. @item SN10 This is reportedly used by SVR4 UUCP to mean that the file size is too large. @end table If the slave responds with @samp{SY}, a file transfer begins. When the file transfer is complete, the slave sends a @samp{C} command response. @table @samp @item CY The file transfer was successful. @item CYM The file transfer was successful, and the slave wishes to become the master; the master should send an @samp{H} command, described below. @item CN5 The temporary file could not be moved into the final location. This implies that the file transfer will never succeed. @end table @end table After the @samp{C} command response has been received (in the @samp{SY} case) or immediately (in an @samp{SN} case) the master will send another command. @node The R Command, The X Command, The S Command, UUCP Protocol Commands @subsubsection The R Command @cindex R UUCP protocol command @cindex UUCP protocol R command @table @asis @item master: @samp{R @var{from} @var{to} @var{user} -@var{options} @var{size}} The @samp{R} and the @samp{-} are literal characters. This is a request by the master to receive a file from the slave. I do not know how SVR4 UUCP or QFT implement file transfer restart in this case. @table @var @item from This is the name of the file on the slave which the master wishes to receive. It must not be in the spool directory, and it may not contain any wildcards. @item to This is the name of the file to create on the master. I do not believe that it can be a directory. It may only be in the spool directory if this file is being requested to support an execution either on the master or on some system other than the slave. @item user The name of the user who requested the transfer. @item options A list of options to control the transfer. The following options are defined (all options are single characters): @table @samp @item d The master should create directories as necessary (this is the default). @item f The master should not create directories if necessary, but should fail the transfer instead. @item m The master should send mail to @var{user} when the transfer is complete. @item q Backslash quoting is applied to the @var{from}, @var{to}, and @var{user} arguments. @xref{UUCP Protocol Commands}. This option was introduced in Taylor UUCP version 1.07. @end table @item size This only appears if Taylor UUCP size negotiation is being used. It specifies the largest file which the master is prepared to accept (when using SVR4 UUCP or QFT, this was specified in the @samp{-U} option during the initial handshake). @end table The slave then responds with an @samp{R} command response. UUPlus does not support @samp{R} requests, and always responds with @samp{RN2}. @table @samp @item RY @var{mode} [@var{size}] The slave is willing to send the file, and file transfer begins. The @var{mode} argument is the octal mode of the file on the slave. The master treats this just as the slave does the @var{mode} argument in the send command, q.v. I am told that SVR4 UUCP sends a trailing @var{size} argument. For some versions of BSD UUCP, the @var{mode} argument may have a trailing @samp{M} character (e.g., @samp{RY 0666M}). This means that the slave wishes to become the master. @item RN2 The slave is not willing to send the file, either because it is not permitted or because the file does not exist. This implies that the file request will never succeed. @item RN6 This is only used by Taylor UUCP size negotiation. It means that the file is too large to send, either because of the size limit specifies by the master or because the slave considers it too large. The file transfer might succeed later, or it might not (this may be cleared up in a later release of Taylor UUCP). @item RN9 This is only used by Taylor UUCP (versions 1.05 and up) and FSUUCP (versions 1.5 and up). It means that the remote system was unable to open another channel (see the discussion of the @samp{i} protocol for more information about channels). This implies that the file transfer might succeed later. @end table If the slave responds with @samp{RY}, a file transfer begins. When the file transfer is complete, the master sends a @samp{C} command. The slave pretty much ignores this, although it may log it. @table @samp @item CY The file transfer was successful. @item CN5 The temporary file could not be moved into the final location. @end table After the @samp{C} command response has been sent (in the @samp{RY} case) or immediately (in an @samp{RN} case) the master will send another command. @end table @node The X Command, The E Command, The R Command, UUCP Protocol Commands @subsubsection The X Command @cindex X UUCP protocol command @cindex UUCP protocol X command @table @asis @item master: @samp{X @var{from} @var{to} @var{user} -@var{options}} The @samp{X} and the @samp{-} are literal characters. This is a request by the master to, in essence, execute uucp on the slave. The slave should execute @samp{uucp @var{from} @var{to}}. @table @var @item from This is the name of the file or files on the slave which the master wishes to transfer. Any wildcards are expanded on the slave. If the master is requesting that the files be transferred to itself, the request would normally contain wildcard characters, since otherwise an @samp{R} command would suffice. The master can also use this command to request that the slave transfer files to a third system. @item to This is the name of the file or directory to which the files should be transferred. This will normally use a UUCP name. For example, if the master wishes to receive the files itself, it would use @samp{master!path}. @item user The name of the user who requested the transfer. @item options A list of options to control the transfer. As far as I know, only one option is defined: @table @samp @item q Backslash quoting is applied to the @var{from}, @var{to}, and @var{user} arguments. @xref{UUCP Protocol Commands}. This option was introduced in Taylor UUCP version 1.07. @end table @end table The slave then responds with an @samp{X} command response. FSUUCP does not support @samp{X} requests, and always responds with @samp{XN}. @table @samp @item XY The request was accepted, and the appropriate file transfer commands have been queued up for later processing. @item XN The request was denied. No particular reason is given. @end table In either case, the master will then send another command. @end table @node The E Command, The H Command, The X Command, UUCP Protocol Commands @subsubsection The E Command @cindex E UUCP protocol command @cindex UUCP protocol E command @table @asis @item master: @samp{E @var{from} @var{to} @var{user} -@var{options} @var{temp} @var{mode} @var{notify} @var{size} @var{command}} The @samp{E} command is only supported by Taylor UUCP 1.04 and up. It is used to make an execution request without requiring a separate @file{X.*} file. @ifclear faq @xref{Execution File Format}. @end ifclear It is only used when the command to be executed requires a single input file which is passed to it as standard input. All the fields have the same meaning as they do for an @samp{S} command, except for @var{options} and @var{command}. @table @var @item options A list of options to control the transfer. The following options are defined (all options are single characters): @table @samp @item C The file has been copied to the spool directory (the master should use @var{temp} rather than @var{from}). @item c The file has not been copied to the spool directory (this is the default). @item N No mail message should be sent, even if the command fails. This is the equivalent of the @samp{N} command in an @file{X.*} file. @item Z A mail message should be sent if the command fails (this is generally the default in any case). This is the equivalent of the @samp{Z} command in an @file{X.*} file. @item R Mail messages about the execution should be sent to the address in the @var{notify} field. This is the equivalent of the @samp{R} command in an @file{X.*} file. @item e The execution should be done with @file{/bin/sh}. This is the equivalent of the @samp{e} command in an @file{X.*} file. @item q Backslash quoting is applied to the @var{from}, @var{to}, @var{user}, and @var{notify} arguments. @xref{UUCP Protocol Commands}. This option was introduced in Taylor UUCP version 1.07. Note that the @var{command} argument is not backslash quoted---that argument is defined as the remainder of the line, and so is already permitted to contain any character. @end table @item command The command which should be executed. This is the equivalent of the @samp{C} command in an @file{X.*} file. @end table The slave then responds with an @samp{E} command response. These are the same as the @samp{S} command responses, but the initial character is @samp{E} rather than @samp{S}. If the slave responds with @samp{EY}, the file transfer begins. When the file transfer is complete, the slave sends a @samp{C} command response, just as for the @samp{S} command. After a successful file transfer, the slave is responsible for arranging for the command to be executed. The transferred file is passed as standard input, as though it were named in the @samp{I} and @samp{F} commands of an @file{X.*} file. After the @samp{C} command response has been received (in the @samp{EY} case) or immediately (in an @samp{EN} case) the master will send another command. @end table @node The H Command, , The E Command, UUCP Protocol Commands @subsubsection The H Command @cindex H UUCP protocol command @cindex UUCP protocol H command @table @asis @item master: @samp{H} This is used by the master to hang up the connection. The slave will respond with an @samp{H} command response. @table @samp @item HY The slave agrees to hang up the connection. In this case the master sends another @samp{HY} command. In some UUCP packages the slave will then send a third @samp{HY} command. At this point the protocol is shut down, and the final handshake is begun. @item HN The slave does not agree to hang up. In this case the master and the slave exchange roles. The next command will be sent by the former slave, which is the new master. The roles may be reversed several times during a single connection. @end table @end table @node The Final Handshake, , UUCP Protocol Commands, UUCP Protocol @subsection The Final Handshake @cindex final handshake After the protocol has been shut down, the final handshake is performed. This handshake has no real purpose, and some UUCP packages simply drop the connection rather than do it (in fact, some will drop the connection immediately after both sides agree to hangup, without even closing down the protocol). @table @asis @item caller: @samp{\020OOOOOO\000} @item called: @samp{\020OOOOOOO\000} @end table That is, the calling UUCP sends six @samp{O} characters and the called UUCP replies with seven @samp{O} characters. Some UUCP packages always send six @samp{O} characters. @ifset faq @format ------------------------------ From: UUCP @samp{g} Protocol Subject: UUCP @samp{g} Protocol @end format @end ifset @node g Protocol, f Protocol, UUCP Protocol, Protocols @section UUCP @samp{g} Protocol @cindex @samp{g} protocol @cindex protocol @samp{g} The @samp{g} protocol is a packet based flow controlled error correcting protocol that requires an eight bit clear connection. It is the original UUCP protocol, and is supported by all UUCP implementations. Many implementations of it are only able to support small window and packet sizes, specifically a window size of 3 and a packet size of 64 bytes, but the protocol itself can support up to a window size of 7 and a packet size of 4096 bytes. Complaints about the inefficiency of the @samp{g} protocol generally refer to specific implementations, rather than to the correctly implemented protocol. The @samp{g} protocol was originally designed for general packet drivers, and thus contains some features that are not used by UUCP, including an alternate data channel and the ability to renegotiate packet and window sizes during the communication session. The @samp{g} protocol is spoofed by many Telebit modems. When spoofing is in effect, each Telebit modem uses the @samp{g} protocol to communicate with the attached computer, but the data between the modems is sent using a Telebit proprietary error correcting protocol. This allows for very high throughput over the Telebit connection, which, because it is half-duplex, would not normally be able to handle the @samp{g} protocol very well at all. When a Telebit is spoofing the @samp{g} protocol, it forces the packet size to be 64 bytes and the window size to be 3. This discussion of the @samp{g} protocol explains how it works, but does not discuss useful error handling techniques. Some discussion of this can be found in Jamie E. Hanrahan's paper, cited @ifclear faq above (@pxref{UUCP Protocol Sources}). @end ifclear @ifset faq above. @end ifset All @samp{g} protocol communication is done with packets. Each packet begins with a six byte header. Control packets consist only of the header. Data packets contain additional data. The header is as follows: @table @asis @item @samp{\020} Every packet begins with a @kbd{^P}. @item @var{k} (1 <= @var{k} <= 9) The @var{k} value is always 9 for a control packet. For a data packet, the @var{k} value indicates how much data follows the six byte header. The amount of data is @ifinfo 2 ** (@var{k} + 4), where ** indicates exponentiation. @end ifinfo @iftex @tex $2^{k + 4}$. @end tex @end iftex Thus a @var{k} value of 1 means 32 data bytes and a @var{k} value of 8 means 4096 data bytes. The @var{k} value for a data packet must be between 1 and 8 inclusive. @item checksum low byte @itemx checksum high byte The checksum value is described below. @item control byte The control byte indicates the type of packet, and is described below. @item xor byte This byte is the xor of @var{k}, the checksum low byte, the checksum high byte and the control byte (i.e., the second, third, fourth and fifth header bytes). It is used to ensure that the header data is valid. @end table The control byte in the header is composed of three bit fields, referred to here as @var{tt} (two bits), @var{xxx} (three bits) and @var{yyy} (three bits). The control is @var{tt}@var{xxx}@var{yyy}, or @code{(@var{tt} << 6) + (@var{xxx} << 3) + @var{yyy}}. The @var{TT} field takes on the following values: @table @samp @item 0 This is a control packet. In this case the @var{k} byte in the header must be 9. The @var{xxx} field indicates the type of control packet; these types are described below. @item 1 This is an alternate data channel packet. This is not used by UUCP. @item 2 This is a data packet, and the entire contents of the attached data field (whose length is given by the @var{k} byte in the header) are valid. The @var{xxx} and @var{yyy} fields are described below. @item 3 This is a short data packet. Let the length of the data field (as given by the @var{k} byte in the header) be @var{l}. Let the first byte in the data field be @var{b1}. If @var{b1} is less than 128 (if the most significant bit of @var{b1} is 0), then there are @code{@var{l} - @var{b1}} valid bytes of data in the data field, beginning with the second byte. If @code{@var{b1} >= 128}, let @var{b2} be the second byte in the data field. Then there are @code{@var{l} - ((@var{b1} & 0x7f) + (@var{b2} << 7))} valid bytes of data in the data field, beginning with the third byte. In all cases @var{l} bytes of data are sent (and all data bytes participate in the checksum calculation) but some of the trailing bytes may be dropped by the receiver. The @var{xxx} and @var{yyy} fields are described below. @end table In a data packet (short or not) the @var{xxx} field gives the sequence number of the packet. Thus sequence numbers can range from 0 to 7, inclusive. The @var{yyy} field gives the sequence number of the last correctly received packet. Each communication direction uses a window which indicates how many unacknowledged packets may be transmitted before waiting for an acknowledgement. The window may range from 1 to 7, and may be different in each direction. For example, if the window is 3 and the last packet acknowledged was packet number 6, packet numbers 7, 0 and 1 may be sent but the sender must wait for an acknowledgement before sending packet number 2. This acknowledgement could come as the @var{yyy} field of a data packet, or as the @var{yyy} field of a @samp{RJ} or @samp{RR} control packet (described below). Each packet must be transmitted in order (the sender may not skip sequence numbers). Each packet must be acknowledged, and each packet must be acknowledged in order. In a control packet, the @var{xxx} field takes on the following values: @table @asis @item 1 @samp{CLOSE} The connection should be closed immediately. This is typically sent when one side has seen too many errors and wants to give up. It is also sent when shutting down the protocol. If an unexpected @samp{CLOSE} packet is received, a @samp{CLOSE} packet should be sent in reply and the @samp{g} protocol should halt, causing UUCP to enter the final handshake. @item 2 @samp{RJ} or @samp{NAK} The last packet was not received correctly. The @var{yyy} field contains the sequence number of the last correctly received packet. @item 3 @samp{SRJ} Selective reject. The @var{yyy} field contains the sequence number of a packet that was not received correctly, and should be retransmitted. This is not used by UUCP, and most implementations will not recognize it. @item 4 @samp{RR} or @samp{ACK} Packet acknowledgement. The @var{yyy} field contains the sequence number of the last correctly received packet. @item 5 @samp{INITC} Third initialization packet. The @var{yyy} field contains the maximum window size to use. @item 6 @samp{INITB} Second initialization packet. The @var{yyy} field contains the packet size to use. It requests a size of @ifinfo 2 ** (@var{yyy} + 5). @end ifinfo @iftex @tex $2^{yyy + 5}$. @end tex @end iftex Note that this is not the same coding used for the @var{k} byte in the packet header (it is 1 less). Most UUCP implementations that request a packet size larger than 64 bytes can handle any packet size up to that specified. @item 7 @samp{INITA} First initialization packet. The @var{yyy} field contains the maximum window size to use. @end table To compute the checksum, call the control byte (the fifth byte in the header) @var{c}. The checksum of a control packet is simply @code{0xaaaa - @var{c}}. The checksum of a data packet is @code{0xaaaa - (@var{check} ^ @var{c})}, where @code{^} denotes exclusive or, and @var{check} is the result of the following routine as run on the contents of the data field (every byte in the data field participates in the checksum, even for a short data packet). Below is the routine used by an early version of Taylor UUCP; it is a slightly modified version of a routine which John Gilmore patched from G.L.@: Chesson's original paper. The @code{z} argument points to the data and the @code{c} argument indicates how much data there is. @example int igchecksum (z, c) register const char *z; register int c; @{ register unsigned int ichk1, ichk2; ichk1 = 0xffff; ichk2 = 0; do @{ register unsigned int b; /* Rotate ichk1 left. */ if ((ichk1 & 0x8000) == 0) ichk1 <<= 1; else @{ ichk1 <<= 1; ++ichk1; @} /* Add the next character to ichk1. */ b = *z++ & 0xff; ichk1 += b; /* Add ichk1 xor the character position in the buffer counting from the back to ichk2. */ ichk2 += ichk1 ^ c; /* If the character was zero, or adding it to ichk1 caused an overflow, xor ichk2 to ichk1. */ if (b == 0 || (ichk1 & 0xffff) < b) ichk1 ^= ichk2; @} while (--c > 0); return ichk1 & 0xffff; @} @end example When the @samp{g} protocol is started, the calling UUCP sends an @samp{INITA} control packet with the window size it wishes the called UUCP to use. The called UUCP responds with an @samp{INITA} packet with the window size it wishes the calling UUCP to use. Pairs of @samp{INITB} and @samp{INITC} packets are then similarly exchanged. When these exchanges are completed, the protocol is considered to have been started. Note that the window and packet sizes are not a negotiation. Each system announces the window and packet size which the other system should use. It is possible that different window and packet sizes will be used in each direction. The protocol works this way on the theory that each system knows how much data it can accept without getting overrun. Therefore, each system tells the other how much data to send before waiting for an acknowledgement. When a UUCP package transmits a command, it sends one or more data packets. All the data packets will normally be complete, although some UUCP packages may send the last one as a short packet. The command string is sent with a trailing null byte, to let the receiving package know when the command is finished. Some UUCP packages require the last byte of the last packet sent to be null, even if the command ends earlier in the packet. Some packages may require all the trailing bytes in the last packet to be null, but I have not confirmed this. When a UUCP package sends a file, it will send a sequence of data packets. The end of the file is signalled by a short data packet containing zero valid bytes (it will normally be preceeded by a short data packet containing the last few bytes in the file). Note that the sequence numbers cover the entire communication session, including both command and file data. When the protocol is shut down, each UUCP package sends a @samp{CLOSE} control packet. @ifset faq @format ------------------------------ From: UUCP @samp{f} Protocol Subject: UUCP @samp{f} Protocol @end format @end ifset @node f Protocol, t Protocol, g Protocol, Protocols @section UUCP @samp{f} Protocol @cindex @samp{f} protocol @cindex protocol @samp{f} The @samp{f} protocol is a seven bit protocol which checksums an entire file at a time. It only uses the characters between @samp{\040} and @samp{\176} (ASCII @kbd{space} and @kbd{~}) inclusive, as well as the carriage return character. It can be very efficient for transferring text only data, but it is very inefficient at transferring eight bit data (such as compressed news). It is not flow controlled, and the checksum is fairly insecure over large files, so using it over a serial connection requires handshaking (XON/XOFF can be used) and error correcting modems. Some people think it should not be used even under those circumstances. I believe that the @samp{f} protocol originated in BSD versions of UUCP. It was originally intended for transmission over X.25 PAD links. The @samp{f} protocol has no startup or finish protocol. However, both sides typically sleep for a couple of seconds before starting up, because they switch the terminal into XON/XOFF mode and want to allow the changes to settle before beginning transmission. When a UUCP package transmits a command, it simply sends a string terminated by a carriage return. When a UUCP package transmits a file, each byte @var{b} of the file is translated according to the following table: @example 0 <= @var{b} <= 037: 0172, @var{b} + 0100 (0100 to 0137) 040 <= @var{b} <= 0171: @var{b} ( 040 to 0171) 0172 <= @var{b} <= 0177: 0173, @var{b} - 0100 ( 072 to 077) 0200 <= @var{b} <= 0237: 0174, @var{b} - 0100 (0100 to 0137) 0240 <= @var{b} <= 0371: 0175, @var{b} - 0200 ( 040 to 0171) 0372 <= @var{b} <= 0377: 0176, @var{b} - 0300 ( 072 to 077) @end example That is, a byte between @samp{\040} and @samp{\171} inclusive is transmitted as is, and all other bytes are prefixed and modified as shown. When all the file data is sent, a seven byte sequence is sent: two bytes of @samp{\176} followed by four ASCII bytes of the checksum as printed in base 16 followed by a carriage return. For example, if the checksum was 0x1234, this would be sent: @samp{\176\1761234\r}. The checksum is initialized to 0xffff. For each byte that is sent it is modified as follows (where @var{b} is the byte before it has been transformed as described above): @example /* Rotate the checksum left. */ if ((ichk & 0x8000) == 0) ichk <<= 1; else @{ ichk <<= 1; ++ichk; @} /* Add the next byte into the checksum. */ ichk += @var{b}; @end example When the receiving UUCP sees the checksum, it compares it against its own calculated checksum and replies with a single character followed by a carriage return. @table @samp @item G The file was received correctly. @item R The checksum did not match, and the file should be resent from the beginning. @item Q The checksum did not match, but too many retries have occurred and the communication session should be abandoned. @end table The sending UUCP checks the returned character and acts accordingly. @ifset faq @format ------------------------------ From: UUCP @samp{t} Protocol Subject: UUCP @samp{t} Protocol @end format @end ifset @node t Protocol, e Protocol, f Protocol, Protocols @section UUCP @samp{t} Protocol @cindex @samp{t} protocol @cindex protocol @samp{t} The @samp{t} protocol is intended for use on links which provide reliable end-to-end connections, such as TCP. It does no error checking or flow control, and requires an eight bit clear channel. I believe the @samp{t} protocol originated in BSD versions of UUCP. When a UUCP package transmits a command, it first gets the length of the command string, @var{c}. It then sends @code{((@var{c} / 512) + 1) * 512} bytes (the smallest multiple of 512 which can hold @var{c} bytes plus a null byte) consisting of the command string itself followed by trailing null bytes. When a UUCP package sends a file, it sends it in blocks. Each block contains at most 1024 bytes of data. Each block consists of four bytes containing the amount of data in binary (most significant byte first, the same format as used by the Unix function @code{htonl}) followed by that amount of data. The end of the file is signalled by a block containing zero bytes of data. @ifset faq @format ------------------------------ From: UUCP @samp{e} Protocol Subject: UUCP @samp{e} Protocol @end format @end ifset @node e Protocol, Big G Protocol, t Protocol, Protocols @section UUCP @samp{e} Protocol @cindex @samp{e} protocol @cindex protocol @samp{e} The @samp{e} protocol is similar to the @samp{t} protocol. It does no flow control or error checking and is intended for use over networks providing reliable end-to-end connections, such as TCP. The @samp{e} protocol originated in versions of HDB UUCP. When a UUCP package transmits a command, it simply sends the command as an ASCII string terminated by a null byte. When a UUCP package transmits a file, it sends the complete size of the file as an ASCII decimal number. The ASCII string is padded out to 20 bytes with null bytes (i.e. if the file is 1000 bytes long, it sends @samp{1000\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0}). It then sends the entire file. @ifset faq @format ------------------------------ From: UUCP @samp{G} Protocol Subject: UUCP @samp{G} Protocol @end format @end ifset @node Big G Protocol, i Protocol, e Protocol, Protocols @section UUCP @samp{G} Protocol @cindex @samp{G} protocol @cindex protocol @samp{G} The @samp{G} protocol is used by SVR4 UUCP. It is identical to the @samp{g} protocol, except that it is possible to modify the window and packet sizes. The SVR4 implementation of the @samp{g} protocol reportedly is fixed at a packet size of 64 and a window size of 7. Supposedly SVR4 chose to implement a new protocol using a new letter to avoid any potential incompatibilities when using different packet or window sizes. Most implementations of the @samp{g} protocol that accept packets larger than 64 bytes will also accept packets smaller than whatever they requested in the @samp{INITB} packet. The SVR4 @samp{G} implementation is an exception; it will only accept packets of precisely the size it requests in the INITB packet. @ifset faq @format ------------------------------ From: UUCP @samp{i} Protocol Subject: UUCP @samp{i} Protocol @end format @end ifset @node i Protocol, j Protocol, Big G Protocol, Protocols @section UUCP @samp{i} Protocol @cindex @samp{i} protocol @cindex protocol @samp{i} The @samp{i} protocol was written by Ian Lance Taylor (who also wrote this @ifclear faq manual). @end ifclear @ifset faq FAQ). @end ifset It was first used by Taylor UUCP version 1.04. It is a sliding window packet protocol, like the @samp{g} protocol, but it supports bidirectional transfers (i.e., file transfers in both directions simultaneously). It requires an eight bit clear connection. Several ideas for the protocol were taken from the paper @cite{A High-Throughput Message Transport System} by P.@: Lauder. I don't know where the paper was published, but the author's e-mail address is @email{piers@@cs.su.oz.au}. The @samp{i} protocol does not adopt his main idea, which is to dispense with windows entirely. This is because some links still do require flow control and, more importantly, because using windows sets a limit to the amount of data which the protocol must be able to resend upon request. To reduce the costs of window acknowledgements, the protocol uses a large window and only requires an ack at the halfway point. Each packet starts with a six byte header, optionally followed by data bytes with a four byte checksum. There are currently five defined packet types (@samp{DATA}, @samp{SYNC}, @samp{ACK}, @samp{NAK}, @samp{SPOS}, @samp{CLOSE}) which are described below. Although any packet type may include data, any data provided with an @samp{ACK}, @samp{NAK} or @samp{CLOSE} packet is ignored. Every @samp{DATA}, @samp{SPOS} and @samp{CLOSE} packet has a sequence number. The sequence numbers are independent for each side. The first packet sent by each side is always number 1. Each packet is numbered one greater than the previous packet, modulo 32. Every packet has a local channel number and a remote channel number. For all packets at least one channel number is zero. When a UUCP command is sent to the remote system, it is assigned a non-zero local channel number. All packets associated with that UUCP command sent by the local system are given the selected local channel number. All associated packets sent by the remote system are given the selected number as the remote channel number. This permits each UUCP command to be uniquely identified by the channel number on the originating system, and therefore each UUCP package can associate all file data and UUCP command responses with the appropriate command. This is a requirement for bidirectional UUCP transfers. The protocol maintains a single global file position, which starts at 0. For each incoming packet, any associated data is considered to occur at the current file position, and the file position is incremented by the amount of data contained. The exception is a packet of type @samp{SPOS}, which is used to change the file position. The reason for keeping track of the file position is described below. The header is as follows: @table @asis @item @samp{\007} Every packet begins with @kbd{^G}. @item @code{(@var{packet} << 3) + @var{locchan}} The five bit packet number combined with the three bit local channel number. @samp{DATA}, @samp{SPOS} and @samp{CLOSE} packets use the packet sequence number for the @var{packet} field. @samp{NAK} packet types use the @var{packet} field for the sequence number to be resent. @samp{ACK} and @samp{SYNC} do not use the @var{packet} field, and generally leave it set to 0. Packets which are not associated with a UUCP command from the local system use a local channel number of 0. @item @code{(@var{ack} << 3) + @var{remchan}} The five bit packet acknowledgement combined with the three bit remote channel number. The packet acknowledgement is the number of the last packet successfully received; it is used by all packet types. Packets which are not sent in response to a UUCP command from the remote system use a remote channel number of 0. @item @code{(@var{type} << 5) + (@var{caller} << 4) + @var{len1}} The three bit packet type combined with the one bit packet direction combined with the upper four bits of the data length. The packet direction bit is always 1 for packets sent by the calling UUCP, and 0 for packets sent by the called UUCP. This prevents confusion caused by echoed packets. @item @var{len2} The lower eight bits of the data length. The twelve bits of data length permit packets ranging in size from 0 to 4095 bytes. @item @var{check} The exclusive or of the second through fifth bytes of the header. This provides an additional check that the header is valid. @end table If the data length is non-zero, the packet is immediately followed by the specified number of data bytes. The data bytes are followed by a four byte CRC 32 checksum, with the most significant byte first. The CRC is calculated over the contents of the data field. The defined packet types are as follows: @table @asis @item 0 @samp{DATA} This is a plain data packet. @item 1 @samp{SYNC} @samp{SYNC} packets are exchanged when the protocol is initialized, and are described further below. @samp{SYNC} packets do not carry sequence numbers (that is, the @var{packet} field is ignored). @item 2 @samp{ACK} This is an acknowledgement packet. Since @samp{DATA} packets also carry packet acknowledgements, @samp{ACK} packets are only used when one side has no data to send. @samp{ACK} packets do not carry sequence numbers. @item 3 @samp{NAK} This is a negative acknowledgement. This is sent when a packet is received incorrectly, and means that the packet number appearing in the @var{packet} field must be resent. @samp{NAK} packets do not carry sequence numbers (the @var{packet} field is already used). @item 4 @samp{SPOS} This packet changes the file position. The packet contains four bytes of data holding the file position, most significant byte first. The next packet received will be considered to be at the named file position. @item 5 @samp{CLOSE} When the protocol is shut down, each side sends a @samp{CLOSE} packet. This packet does have a sequence number, which could be used to ensure that all packets were correctly received (this is not needed by UUCP, however, which uses the higher level @samp{H} command with an @samp{HY} response). @end table When the protocol starts up, both systems send a @samp{SYNC} packet. The @samp{SYNC} packet includes at least three bytes of data. The first two bytes are the maximum packet size the remote system should send, most significant byte first. The third byte is the window size the remote system should use. The remote system may send packets of any size up to the maximum. If there is a fourth byte, it is the number of channels the remote system may use (this must be between 1 and 7, inclusive). Additional data bytes may be defined in the future. The window size is the number of packets that may be sent before a packet is acknowledged. There is no requirement that every packet be acknowledged; any acknowledgement is considered to acknowledge all packets through the number given. In the current implementation, if one side has no data to send, it sends an @samp{ACK} when half the window is received. Note that the @samp{NAK} packet corresponds to the unused @samp{g} protocol @samp{SRJ} packet type, rather than to the @samp{RJ} packet type. When a @samp{NAK} is received, only the named packet should be resent, not any subsequent packets. Note that if both sides have data to send, but a packet is lost, it is perfectly reasonable for one side to continue sending packets, all of which will acknowledge the last packet correctly received, while the system whose packet was lost will be unable to send a new packet because the send window will be full. In this circumstance, neither side will time out and one side of the communication will be effectively shut down for a while. Therefore, any system with outstanding unacknowledged packets should arrange to time out and resend a packet even if data is being received. Commands are sent as a sequence of data packets with a non-zero local channel number. The last data packet for a command includes a trailing null byte (normally a command will fit in a single data packet). Files are sent as a sequence of data packets ending with one of length zero. The channel numbers permit a more efficient implementation of the UUCP file send command. Rather than send the command and then wait for the @samp{SY} response before sending the file, the file data is sent beginning immediately after the @samp{S} command is sent. If an @samp{SN} response is received, the file send is aborted, and a final data packet of length zero is sent to indicate that the channel number may be reused. If an @samp{SY} reponse with a file position indicator is received, the file send adjusts to the file position; this is why the protocol maintains a global file position. Note that the use of channel numbers means that each UUCP system may send commands and file data simultaneously. Moreover, each UUCP system may send multiple files at the same time, using the channel number to disambiguate the data. Sending a file before receiving an acknowledgement for the previous file helps to eliminate the round trip delays inherent in other UUCP protocols. @ifset faq @format ------------------------------ From: UUCP @samp{j} Protocol Subject: UUCP @samp{j} Protocol @end format @end ifset @node j Protocol, x Protocol, i Protocol, Protocols @section UUCP @samp{j} Protocol @cindex @samp{j} protocol @cindex protocol @samp{j} The @samp{j} protocol is a variant of the @samp{i} protocol. It was also written by Ian Lance Taylor, and first appeared in Taylor UUCP version 1.04. The @samp{j} protocol is a version of the @samp{i} protocol designed for communication links which intercept a few characters, such as XON or XOFF. It is not efficient to use it on a link which intercepts many characters, such as a seven bit link. The @samp{j} protocol performs no error correction or detection; that is presumed to be the responsibility of the @samp{i} protocol. When the @samp{j} protocol starts up, each system sends a printable ASCII string indicating which characters it wants to avoid using. The string begins with the ASCII character @kbd{^} (octal 136) and ends with the ASCII character @kbd{~} (octal 176). After sending this string, each system looks for the corresponding string from the remote system. The strings are composed of escape sequences: @samp{\ooo}, where @samp{o} is an octal digit. For example, sending the string @samp{^\021\023~} means that the ASCII XON and XOFF characters should be avoided. The union of the characters described in both strings (the string which is sent and the string which is received) is the set of characters which must be avoided in this conversation. Avoiding a printable ASCII character (octal 040 to octal 176, inclusive) is not permitted. After the exchange of characters to avoid, the normal @samp{i} protocol start up is done, and the rest of the conversation uses the normal @samp{i} protocol. However, each @samp{i} protocol packet is wrapped to become a @samp{j} protocol packet. Each @samp{j} protocol packet consists of a seven byte header, followed by data bytes, followed by index bytes, followed by a one byte trailer. The packet header looks like this: @table @asis @item @kbd{^} Every packet begins with the ASCII character @kbd{^}, octal 136. @item @var{high} @itemx @var{low} These two characters give the total number of bytes in the packet. Both @var{high} and @var{low} are printable ASCII characters. The length of the packet is @code{(@var{high} - 040) * 0100 + (@var{low} - 040)}, where @code{040 <= @var{high} < 0177} and @code{040 <= @var{low} < 0140}. This permits a length of 6079 bytes, but there is a further restriction on packet size described below. @item @kbd{=} The ASCII character @kbd{=}, octal 075. @item @var{data-high} @itemx @var{data-low} These two characters give the total number of data bytes in the packet. The encoding is as described for @var{high} and @var{low}. The number of data bytes is the size of the @samp{i} protocol packet wrapped inside this @samp{j} protocol packet. @item @kbd{@@} The ASCII character @kbd{@@}, octal 100. @end table The header is followed by the number of data bytes given in @var{data-high} and @var{data-low}. These data bytes are the @samp{i} protocol packet which is being wrapped in the @samp{j} protocol packet. However, each character in the @samp{i} protocol packet which the @samp{j} protocol must avoid is transformed into a printable ASCII character (recall that avoiding a printable ASCII character is not permitted). Two index bytes are used for each character which must be transformed. The index bytes immediately follow the data bytes. The index bytes are created in pairs. Each pair of index bytes encodes the location of a character in the @samp{i} protocol packet which was transformed to become a printable ASCII character. Each pair of index bytes also encodes the precise transformation which was performed. When the sender finds a character which must be avoided, it will transform it using one or two operations. If the character is 0200 or greater, it will subtract 0200. If the resulting character is less than 020, or is equal to 0177, it will xor by 020. The result is a printable ASCII character. The zero based byte index of the character within the @samp{i} protocol packet is determined. This index is turned into a two byte printable ASCII index, @var{index-high} and @var{index-low}, such that the index is @code{(@var{index-high} - 040) * 040 + (@var{index-low} - 040)}. @var{index-low} is restricted such that @code{040 <= @var{index-low} < 0100}. @var{index-high} is not permitted to be 0176, so @code{040 <= @var{index-high} < 0176}. @var{index-low} is then modified to encode the transformation: @itemize @bullet @item If the character transformation only had to subtract 0200, then @var{index-low} is used as is. @item If the character transformation only had to xor by 020, then 040 is added to @var{index-low}. @item If both operations had to be performed, then 0100 is added to @var{index-low}. However, if the value of @var{index-low} was initially 077, then adding 0100 would result in 0177, which is not a printable ASCII character. For that special case, @var{index-high} is set to 0176, and @var{index-low} is set to the original value of @var{index-high}. @end itemize The receiver decodes the index bytes as follows (this is the reverse of the operations performed by the sender, presented here for additional clarity): @itemize @bullet @item The first byte in the index is @var{index-high}, and the second is @var{index-low}. @item If @code{040 <= @var{index-high} < 0176}, the index refers to the data byte at position @code{(@var{index-high} - 040) * 040 + @var{index-low} % 040}. @item If @code{040 <= @var{index-low} < 0100}, then 0200 must be added to indexed byte. @item If @code{0100 <= @var{index-low} < 0140}, then 020 must be xor'ed to the indexed byte. @item If @code{0140 <= @var{index-low} < 0177}, then 0200 must be added to the indexed byte, and 020 must be xor'ed to the indexed byte. @item If @code{@var{index-high} == 0176}, the index refers to the data byte at position @code{(@var{index-low} - 040) * 040 + 037}. 0200 must be added to the indexed byte, and 020 must be xor'ed to the indexed byte. @end itemize This means the largest @samp{i} protocol packet which may be wrapped inside a @samp{j} protocol packet is @code{(0175 - 040) * 040 + (077 - 040) == 3007} bytes. The final character in a @samp{j} protocol packet, following the index bytes, is the ASCII character @kbd{~} (octal 176). The motivation behind using an indexing scheme, rather than escape characters, is to avoid data movement. The sender may simply add a header and a trailer to the @samp{i} protocol packet. Once the receiver has loaded the @samp{j} protocol packet, it may scan the index bytes, transforming the data bytes, and then pass the data bytes directly on to the @samp{i} protocol routine. @ifset faq @format ------------------------------ From: UUCP @samp{x} Protocol Subject: UUCP @samp{x} Protocol @end format @end ifset @node x Protocol, y Protocol, j Protocol, Protocols @section UUCP @samp{x} Protocol @cindex @samp{x} protocol @cindex protocol @samp{x} The @samp{x} protocol is used in Europe (and probably elsewhere) with machines that contain an builtin X.25 card and can send eight bit data transparently across X.25 circuits, without interference from the X.28 or X.29 layers. The protocol sends packets of 512 bytes, and relies on a write of zero bytes being read as zero bytes without stopping communication. It first appeared in the original System V UUCP implementation. @ifset faq @format ------------------------------ From: UUCP @samp{y} Protocol Subject: UUCP @samp{y} Protocol @end format @end ifset @node y Protocol, d Protocol, x Protocol, Protocols @section UUCP @samp{y} Protocol @cindex @samp{y} protocol @cindex protocol @samp{y} The @samp{y} protocol was developed by Jorge Cwik for use in FX UUCICO, a PC uucico program. It is designed for communication lines which handle error correction and flow control. It requires an eight bit clean connection. It performs error detection, but not error correction: when an error is detected, the line is dropped. It is a streaming protocol, like the @samp{f} protocol; there are no packet acknowledgements, so the protocol is efficient over a half-duplex communication line such as PEP. Every packet contains a six byte header: @table @asis @item sequence low byte @itemx sequence high byte A two byte sequence number, in little endian order. The first sequence number is 0. Since the first packet is always a sync packet (described below) the sequence number of the first data packet is always 1. Each system counts sequence numbers independently. @item length low byte @itemx length high byte A two byte data length, in little endian order. If the high bit of the sixteen bit field is clear, this is the number of data bytes which follow the six byte header. If the high bit is set, there is no data, and the length field is a type of control packet. @item checksum low byte @itemx checksum high byte A two byte checksum, in little endian order. The checksum is computed over the data bytes. The checksum algorithm is described below. If there are no data bytes, the checksum is sent as 0. @end table When the protocol starts up, each side must send a sync packet. This is a packet with a normal six byte header followed by data. The sequence number of the sync packet should be 0. Currently at least four bytes of data must be sent with the sync packet. Additional bytes should be ignored. They are defined as follows: @table @asis @item version The version number of the protocol. Currently this must be 1. Larger numbers should be ignored; it is the responsibility of the newer version to accommodate the older one. @item packet size The maximum data length to use divided by 256. This is sent as a single byte. The maximum data length permitted is 32768, which would be sent as 128. Customarily both systems will use the same maximum data length, the lower of the two requested. @item flags low byte @itemx flags high byte Two bytes of flags. None are currently defined. These bytes should be sent as 0, and ignored by the receiver. @end table A length field with the high bit set is a control packet. The following control packet types are defined: @table @asis @item 0xfffe @samp{YPKT_ACK} Acknowledges correct receipt of a file. @item 0xfffd @samp{YPKT_ERR} Indicates an incorrect checksum. @item 0xfffc @samp{YPKT_BAD} Indicates a bad sequence number, an invalid length, or some other error. @end table If a control packet other than @samp{YPKT_ACK} is received, the connection is dropped. If a checksum error is detected for a received packet, a @samp{YPKT_ERR} control packet is sent, and the connection is dropped. If a packet is received out of sequence, a @samp{YPKT_BAD} control packet is sent, and the connection is dropped. The checksum is initialized to 0xffff. For each data byte in a packet it is modified as follows (where @var{b} is the byte before it has been transformed as described above): @example /* Rotate the checksum left. */ if ((ichk & 0x8000) == 0) ichk <<= 1; else @{ ichk <<= 1; ++ichk; @} /* Add the next byte into the checksum. */ ichk += @var{b}; @end example This is the same algorithm as that used by the @samp{f} protocol. A command is sent as a sequence of data packets followed by a null byte. In the normal case, a command will fit into a single packet. The packet should be exactly the length of the command plus a null byte. If the command is too long, more packets are sent as required. A file is sent as a sequence of data packets, ending with a zero length packet. The data packets may be of any length greater than zero and less than or equal to the maximum permitted packet size specified in the initial sync packet. After the zero length packet ending a file transfer has been received, the receiving system sends a @samp{YPKT_ACK} control packet. The sending system waits for the @samp{YPKT_ACK} control packet before continuing; this wait should be done with a large timeout, since there may be a considerable amount of data buffered on the communication path. @ifset faq @format ------------------------------ From: UUCP @samp{d} Protocol Subject: UUCP @samp{d} Protocol @end format @end ifset @node d Protocol, h Protocol, y Protocol, Protocols @section UUCP @samp{d} Protocol @cindex @samp{d} protocol @cindex protocol @samp{d} The @samp{d} protocol is apparently used for DataKit muxhost (not RS-232) connections. No file size is sent. When a file has been completely transferred, a write of zero bytes is done; this must be read as zero bytes on the other end. @ifset faq @format ------------------------------ From: UUCP @samp{h} Protocol Subject: UUCP @samp{h} Protocol @end format @end ifset @node h Protocol, v Protocol, d Protocol, Protocols @section UUCP @samp{h} Protocol @cindex @samp{h} protocol @cindex protocol @samp{h} The @samp{h} protocol is apparently used in some places with HST modems. It does no error checking, and is not that different from the @samp{t} protocol. I don't know the details. @ifset faq @format ------------------------------ From: UUCP @samp{v} Protocol Subject: UUCP @samp{v} Protocol @end format @end ifset @node v Protocol, , h Protocol, Protocols @section UUCP @samp{v} Protocol @cindex @samp{v} protocol @cindex protocol @samp{v} The @samp{v} protocol is used by UUPC/extended, a PC UUCP program. It is simply a version of the @samp{g} protocol which supports packets of any size, and also supports sending packets of different sizes during the same conversation. There are many @samp{g} protocol implementations which support both, but there are also many which do not. Using @samp{v} ensures that everything is supported. @ifset faq @format ------------------------------ From: Thanks Subject: Thanks @end format Besides the papers and information acknowledged at the top of this article, the following people have contributed help, advice, suggestions and information: @format Earle Ake 513-429-6500 chris@@uuplus.com (Christopher J. Ambler) jhc@@iscp.bellcore.com (Jonathan Clark) jorge@@laser.satlink.net (Jorge Cwik) celit!billd@@UCSD.EDU (Bill Davidson) "Drew Derbyshire" erik@@pdnfido.fidonet.org Matthew Farwell dgilbert@@gamiga.guelphnet.dweomer.org (David Gilbert) kherron@@ms.uky.edu (Kenneth Herron) Mike Ipatow Romain Kang "Jonathan I. Kamens" "David J. MacKenzie" jum@@helios.de (Jens-Uwe Mager) peter@@xpoint.ruessel.sub.org (Peter Mandrella) david nugent Stephen.Page@@prg.oxford.ac.uk joey@@tessi.UUCP (Joey Pruett) James Revell Larry Rosenman Rich Salz evesg@@etlrips.etl.go.jp (Gjoen Stein) kls@@ditka.Chicago.COM (Karl Swartz) Dima Volodin John.Woods@@proteon.com (John Woods) jon@@console.ais.org (Jon Zeeff) Eric Ziegast ------------------------------ End of UUCP Internals Frequently Asked Questions ****************************** @end format @end ifset @c END-OF-FAQ @node Hacking, Acknowledgements, Protocols, Top @chapter Hacking Taylor UUCP This chapter provides the briefest of guides to the Taylor UUCP source code itself. @menu * System Dependence:: System Dependence * Naming Conventions:: Naming Conventions * Patches:: Patches @end menu @node System Dependence, Naming Conventions, Hacking, Hacking @section System Dependence The code is carefully segregated into a system independent portion and a system dependent portion. The system dependent code is in the @file{unix} subdirectory, and also in the file @file{sysh.unx} (also known as @file{sysdep.h}). With the right configuration parameters, the system independent code calls only ANSI C functions. Some of the less common ANSI C functions are also provided in the @file{lib} directory. The replacement function @code{strtol} in @file{lib/strtol.c} assumes that the characters @kbd{A} to @kbd{F} and @kbd{a} to @kbd{f} appear in strictly sequential order. The function @code{igradecmp} in @file{uuconf/grdcmp.c} assumes that the upper and lower case letters appear in order. Both assumptions are true for ASCII and EBCDIC, but neither is guaranteed by ANSI C. Disregarding these caveats, I believe that the system independent portion of the code is strictly conforming. That's not too exciting, since all the work is done in the system dependent code. I think that this code can conform to POSIX 1003.1, given the right compilation parameters. I'm a bit less certain about this, though. The code has been used on a 16 bit segmented system with no function prototypes, so I'm fairly certain that all casts to long and pointers are done when necessary. @node Naming Conventions, Patches, System Dependence, Hacking @section Naming Conventions I use a modified Hungarian naming convention for my variables and functions. As with all naming conventions, the code is rather opaque if you are not familiar with it, but becomes clear and easy to use with time. The first character indicates the type of the variable (or function return value). Sometimes additional characters are used. I use the following type prefixes: @table @samp @item a array; the next character is the type of an element @item b byte or character @item c count of something @item e stdio FILE * @item f boolean @item i generic integer @item l double @item o file descriptor (as returned by open, creat, etc.) @item p generic pointer @item q pointer to structure @item s structure @item u void (function return values only) @item z character string @end table A generic pointer (@code{p}) is sometimes a @code{void *}, sometimes a function pointer in which case the prefix is pf, and sometimes a pointer to another type, in which case the next character is the type to which it points (pf is overloaded). An array of strings (@code{char *[]}) would be named @code{az} (array of string). If this array were passed to a function, the function parameter would be named @code{paz} (pointer to array of string). Note that the variable name prefixes do not necessarily indicate the type of the variable. For example, a variable prefixed with @kbd{i} may be int, long or short. Similarly, a variable prefixed with @kbd{b} may be a char or an int; for example, the return value of @code{getchar} would be caught in an int variable prefixed with @kbd{b}. For a non-local variable (extern or file static), the first character after the type prefix is capitalized. Most static variables and functions use another letter after the type prefix to indicate which module they come from. This is to help distinguish different names in the debugger. For example, all static functions in @file{protg.c}, the @samp{g} protocol source code, use a module prefix of @samp{g}. This isn't too useful, as a number of modules use a module prefix of @samp{s}. @node Patches, , Naming Conventions, Hacking @section Patches I am always grateful for any patches sent in. Much of the flexibility and portability of the code is due to other people. Please do not hesitate to send me any changes you have found necessary or useful. When sending a patch, please send the output of the Unix @command{diff} program invoked with the @option{-c} option (if you have the GNU version of @command{diff}, use the @option{-p} option). Always invoke @command{diff} with the original file first and the modified file second. If your @command{diff} does not support @option{-c} (or you don't have @command{diff}), send a complete copy of the modified file (if you have just changed a single function, you can just send the new version of the function). In particular, please do not send @command{diff} output without the @option{-c} option, as it is useless. If you have made a number of changes, it is very convenient for me if you send each change as a separate mail message. Sometimes I will think that one change is useful but another one is not. If they are in different messages it is much easier for me to apply one but not the other. I rarely apply the patches directly. Instead I work my way through the hunks and apply each one separately. This ensures that the naming remains consistent, and that I understand all the code. If you can not follow all these rules, then don't. But if you do, it makes it more likely that I will incorporate your changes. I am not paid for my UUCP work, and my available time is unfortunately very restricted. The package is important to me, and I do what I can, but I can not do all that I would like, much less all that everybody else would like. Finally, please do not be offended if I do not reply to messages for some time, even a few weeks. I am often behind on my mail, and if I think your message deserves a considered reply I will often put it aside until I have time to deal with it. @node Acknowledgements, Index (concepts), Hacking, Top @chapter Acknowledgements This is a list of people who gave help or suggestions while I was working on the Taylor UUCP project. Appearance on this list does not constitute endorsement of the program, particularly since some of the comments were criticisms. I've probably left some people off, and I apologize for any oversight; it does not mean your contribution was unappreciated. First of all, I would like to thank the people at Infinity Development Systems (formerly AIRS, which lives on in the domain name) for permitting me to use their computers and @file{uunet} access. I would also like to thank Richard Stallman @email{rms@@gnu.org} for founding the Free Software Foundation, and John Gilmore @email{gnu@@toad.com} for writing the initial version of gnuucp (based on uuslave) which was a direct inspiration for this somewhat larger project. Chip Salzenberg @email{chip@@tct.com} has contributed many patches. @ifinfo Franc,ois @end ifinfo @iftex @tex Fran\c cois @end tex @end iftex Pinard @email{pinard@@iro.umontreal.ca} tirelessly tested the code and suggested many improvements. He also put together the initial version of this manual. Doug Evans contributed the zmodem protocol. Marc Boucher @email{marc@@CAM.ORG} contributed the code supporting the pipe port type. Jorge Cwik @email{jorge@@laser.satlink.net} contributed the @samp{y} protocol code. Finally, Verbus M. Counts @email{verbus@@westmark.com} and Centel Federal Systems, Inc., deserve special thanks, since they actually paid me money to port this code to System III. In alphabetical order: @example Meno Abels @email{Meno.Abels@@Technical.Adviser.com} "Earle F. Ake - SAIC" @email{ake@@Dayton.SAIC.COM} @email{mra@@searchtech.com} (Michael Almond) @email{cambler@@zeus.calpoly.edu} (Christopher J. Ambler) Brian W. Antoine @email{briana@@tau-ceti.isc-br.com} @email{jantypas@@soft21.s21.com} (John Antypas) @email{james@@bigtex.cactus.org} (James Van Artsdalen) @email{jima@@netcom.com} (Jim Avera) @email{nba@@sysware.DK} (Niels Baggesen) @email{uunet!hotmomma!sdb} (Scott Ballantyne) Zacharias Beckman @email{zac@@dolphin.com} @email{mike@@mbsun.ann-arbor.mi.us} (Mike Bernson) @email{bob@@usixth.sublink.org} (Roberto Biancardi) @email{statsci!scott@@coco.ms.washington.edu} (Scott Blachowicz) @email{bag%wood2.cs.kiev.ua@@relay.ussr.eu.net} (Andrey G Blochintsev) @email{spider@@Orb.Nashua.NH.US} (Spider Boardman) Gregory Bond @email{gnb@@bby.com.au} Marc Boucher @email{marc@@CAM.ORG} Ard van Breemen @email{ard@@cstmel.hobby.nl} @email{dean@@coplex.com} (Dean Brooks) @email{jbrow@@radical.com} (Jim Brownfield) @email{dave@@dlb.com} (Dave Buck) @email{gordon@@sneaky.lonestar.org} (Gordon Burditt) @email{dburr@@sbphy.physics.ucsb.edu} (Donald Burr) @email{mib@@gnu.ai.mit.edu} (Michael I Bushnell) Brian Campbell @email{brianc@@quantum.on.ca} Andrew A. Chernov @email{ache@@astral.msk.su} @email{jhc@@iscp.bellcore.com} (Jonathan Clark) @email{mafc!frank@@bach.helios.de} (Frank Conrad) Ed Carp @email{erc@@apple.com} @email{mpc@@mbs.linet.org} (Mark Clements) @email{verbus@@westmark.westmark.com} (Verbus M. Counts) @email{cbmvax!snark.thyrsus.com!cowan} (John Cowan) Bob Cunningham @email{bob@@soest.hawaii.edu} @email{jorge@@laser.satlink.net} (Jorge Cwik) @email{kdburg@@incoahe.hanse.de} (Klaus Dahlenburg) Damon @email{d@@exnet.co.uk} @email{celit!billd@@UCSD.EDU} (Bill Davidson) @email{hubert@@arakis.fdn.org} (Hubert Delahaye) @email{markd@@bushwire.apana.org.au} (Mark Delany) Allen Delaney @email{allen@@brc.ubc.ca} Gerriet M. Denkmann @email{gerriet@@hazel.north.de} @email{denny@@dakota.alisa.com} (Bob Denny) Drew Derbyshire @email{ahd@@kew.com} @email{ssd@@nevets.oau.org} (Steven S. Dick) @email{gert@@greenie.gold.sub.org} (Gert Doering) @email{gemini@@geminix.in-berlin.de} (Uwe Doering) Hans-Dieter Doll @email{hd2@@Insel.DE} @email{deane@@deane.teleride.on.ca} (Dean Edmonds) Mark W. Eichin @email{eichin@@cygnus.com} @email{erik@@pdnfido.fidonet.org} Andrew Evans @email{andrew@@airs.com} @email{dje@@cygnus.com} (Doug Evans) Marc Evans @email{marc@@synergytics.com} Dan Everhart @email{dan@@dyndata.com} @email{kksys!kegworks!lfahnoe@@cs.umn.edu} (Larry Fahnoe) Matthew Farwell @email{dylan@@ibmpcug.co.uk} @email{fenner@@jazz.psu.edu} (Bill Fenner) @email{jaf@@inference.com} (Jose A. Fernandez) "David J. Fiander" @email{golem!david@@news.lsuc.on.ca} Thomas Fischer @email{batman@@olorin.dark.sub.org} Mister Flash @email{flash@@sam.imash.ras.ru} @email{louis@@marco.de} (Ju"rgen Fluk) @email{erik@@eab.retix.com} (Erik Forsberg) @email{andy@@scp.caltech.edu} (Andy Fyfe) Lele Gaifax @email{piggy@@idea.sublink.org} @email{Peter.Galbavy@@micromuse.co.uk} @email{hunter@@phoenix.pub.uu.oz.au} (James Gardiner [hunter]) Terry Gardner @email{cphpcom!tjg01} @email{dgilbert@@gamiga.guelphnet.dweomer.org} (David Gilbert) @email{ol@@infopro.spb.su} (Oleg Girko) @email{jimmy@@tokyo07.info.com} (Jim Gottlieb) Benoit Grange @email{ben@@fizz.fdn.org} @email{elg@@elgamy.jpunix.com} (Eric Lee Green) @email{ryan@@cs.umb.edu} (Daniel R. Guilderson) @email{greg@@gagme.chi.il.us} (Gregory Gulik) Richard H. Gumpertz @email{rhg@@cps.com} Scott Guthridge @email{scooter@@cube.rain.com} Michael Haberler @email{mah@@parrot.prv.univie.ac.at} Daniel Hagerty @email{hag@@eddie.mit.edu} @email{jh@@moon.nbn.com} (John Harkin) @email{guy@@auspex.auspex.com} (Guy Harris) @email{hsw1@@papa.attmail.com} (Stephen Harris) Tom Ivar Helbekkmo @email{tih@@Norway.EU.net} Petri Helenius @email{pete@@fidata.fi} @email{gabe@@edi.com} (B. Gabriel Helou) Bob Hemedinger @email{bob@@dalek.mwc.com} Andrew Herbert @email{andrew@@werple.pub.uu.oz.au} @email{kherron@@ms.uky.edu} (Kenneth Herron) Peter Honeyman @email{honey@@citi.umich.edu} @email{jhood@@smoke.marlboro.vt.us} (John Hood) Mark Horsburgh @email{markh@@kcbbs.gen.nz} John Hughes @email{john@@Calva.COM} Mike Ipatow @email{mip@@fido.itc.e-burg.su} Bill Irwin @email{bill@@twg.bc.ca} @email{pmcgw!personal-media.co.jp!ishikawa} (Chiaki Ishikawa) @email{ai@@easy.in-chemnitz.de} (Andreas Israel) @email{iverson@@lionheart.com} (Tim Iverson) @email{bei@@dogface.austin.tx.us} (Bob Izenberg) @email{djamiga!djjames@@fsd.com} (D.J.James) Rob Janssen @email{cmgit!rob@@relay.nluug.nl} @email{harvee!esj} (Eric S Johansson) Kevin Johnson @email{kjj@@pondscum.phx.mcd.mot.com} @email{rj@@rainbow.in-berlin.de} (Robert Joop) Alan Judge @email{aj@@dec4ie.IEunet.ie} @email{chris@@cj_net.in-berlin.de} (Christof Junge) Romain Kang @email{romain@@pyramid.com} @email{tron@@Veritas.COM} (Ronald S. Karr) Brendan Kehoe @email{brendan@@cs.widener.edu} @email{warlock@@csuchico.edu} (John Kennedy) @email{kersing@@nlmug.nl.mugnet.org} (Jac Kersing) @email{ok@@daveg.PFM-Mainz.de} (Olaf Kirch) Gabor Kiss @email{kissg@@sztaki.hu} @email{gero@@gkminix.han.de} (Gero Kuhlmann) @email{rob@@pact.nl} (Rob Kurver) "C.A. Lademann" @email{cal@@zls.gtn.com} @email{kent@@sparky.IMD.Sterling.COM} (Kent Landfield) Tin Le @email{tin@@saigon.com} @email{lebaron@@inrs-telecom.uquebec.ca} (Gregory LeBaron) @email{karl@@sugar.NeoSoft.Com} (Karl Lehenbauer) @email{alex@@hal.rhein-main.de} (Alexander Lehmann) @email{merlyn@@digibd.com} (Merlyn LeRoy) @email{clewis@@ferret.ocunix.on.ca} (Chris Lewis) @email{gdonl@@ssi1.com} (Don Lewis) @email{libove@@libove.det.dec.com} (Jay Vassos-Libove) @email{bruce%blilly@@Broadcast.Sony.COM} (Bruce Lilly) Godfrey van der Linden @email{Godfrey_van_der_Linden@@NeXT.COM} Ted Lindgreen @email{tlindgreen@@encore.nl} @email{andrew@@cubetech.com} (Andrew Loewenstern) "Arne Ludwig" @email{arne@@rrzbu.hanse.de} Matthew Lyle @email{matt@@mips.mitek.com} @email{djm@@eng.umd.edu} (David J. MacKenzie) John R MacMillan @email{chance!john@@sq.sq.com} @email{jum@@helios.de} (Jens-Uwe Mager) Giles D Malet @email{shrdlu!gdm@@provar.kwnet.on.ca} @email{mem@@mv.MV.COM} (Mark E. Mallett) @email{pepe@@dit.upm.es} (Jose A. Manas) @email{peter@@xpoint.ruessel.sub.org} (Peter Mandrella) @email{martelli@@cadlab.sublink.org} (Alex Martelli) W Christopher Martin @email{wcm@@geek.ca.geac.com} Yanek Martinson @email{yanek@@mthvax.cs.miami.edu} @email{thomasm@@mechti.wupper.de} (Thomas Mechtersheimer) @email{jm@@aristote.univ-paris8.fr} (Jean Mehat) @email{me@@halfab.freiburg.sub.org} (Udo Meyer) @email{les@@chinet.chi.il.us} (Leslie Mikesell) @email{bug@@cyberdex.cuug.ab.ca} (Trever Miller) @email{mmitchel@@digi.lonestar.org} (Mitch Mitchell) Emmanuel Mogenet @email{mgix@@krainte.jpn.thomson-di.fr} @email{rmohr@@infoac.rmi.de} (Rupert Mohr) Jason Molenda @email{molenda@@sequent.com} @email{ianm@@icsbelf.co.uk} (Ian Moran) @email{jmorriso@@bogomips.ee.ubc.ca} (John Paul Morrison) @email{brian@@ilinx.wimsey.bc.ca} (Brian J. Murrell) @email{service@@infohh.rmi.de} (Dirk Musstopf) @email{lyndon@@cs.athabascau.ca} (Lyndon Nerenberg) @email{rolf@@saans.north.de} (Rolf Nerstheimer) @email{tom@@smart.bo.open.de} (Thomas Neumann) @email{mnichols@@pacesetter.com} Richard E. Nickle @email{trystro!rick@@Think.COM} @email{stephan@@sunlab.ka.sub.org} (Stephan Niemz) @email{raymond@@es.ele.tue.nl} (Raymond Nijssen) @email{nolan@@helios.unl.edu} (Michael Nolan) david nugent @email{david@@csource.oz.au} Jim O'Connor @email{jim@@bahamut.fsc.com} @email{kevin%kosman.uucp@@nrc.com} (Kevin O'Gorman) Petri Ojala @email{ojala@@funet.fi} @email{oneill@@cs.ulowell.edu} (Brian 'Doc' O'Neill) @email{Stephen.Page@@prg.oxford.ac.uk} Peter Palfrader @email{peter@@palfrader.org} @email{abekas!dragoman!mikep@@decwrl.dec.com} (Mike Park) Tim Peiffer @email{peiffer@@cs.umn.edu} @email{don@@blkhole.resun.com} (Don Phillips) "Mark Pizzolato 415-369-9366" @email{mark@@infocomm.com} John Plate @email{plate@@infotek.dk} @email{dplatt@@ntg.com} (Dave Platt) @email{eldorado@@tharr.UUCP} (Mark Powell) Mark Powell @email{mark@@inet-uk.co.uk} @email{pozar@@kumr.lns.com} (Tim Pozar) @email{joey@@tessi.UUCP} (Joey Pruett) Paul Pryor @email{ptp@@fallschurch-acirs2.army.mil} @email{putsch@@uicc.com} (Jeff Putsch) @email{ar@@nvmr.robin.de} (Andreas Raab) Vadim Radionov @email{rvp@@zfs.lg.ua} Jarmo Raiha @email{jarmo@@ksvltd.FI} James Revell @email{revell@@uunet.uu.net} Scott Reynolds @email{scott@@clmqt.marquette.Mi.US} @email{mcr@@Sandelman.OCUnix.On.Ca} (Michael Richardson) Kenji Rikitake @email{kenji@@rcac.astem.or.jp} @email{arnold@@cc.gatech.edu} (Arnold Robbins) @email{steve@@Nyongwa.cam.org} (Steve M. Robbins) Ollivier Robert @email{Ollivier.Robert@@keltia.frmug.fr.net} Serge Robyns @email{sr@@denkart.be} Lawrence E. Rosenman @email{ler@@lerami.lerctr.org} Jeff Ross @email{jeff@@wisdom.bubble.org} Aleksey P. Rudnev @email{alex@@kiae.su} "Heiko W.Rupp" @email{hwr@@pilhuhn.ka.sub.org} @email{wolfgang@@wsrcc.com} (Wolfgang S. Rupprecht) @email{tbr@@tfic.bc.ca} (Tom Rushworth) Peter Rye @email{prye@@picu-sgh.demon.co.uk} @email{jsacco@@ssl.com} (Joseph E. Sacco) @email{rsalz@@bbn.com} (Rich Salz) Curt Sampson @email{curt@@portal.ca} @email{sojurn!mike@@hobbes.cert.sei.cmu.edu} (Mike Sangrey) Nickolay Saukh @email{nms@@ussr.EU.net} Ignatios Souvatzis @email{is@@jocelyn.rhein.de} @email{heiko@@lotte.sax.de} (Heiko Schlittermann) Eric Schnoebelen @email{eric@@cirr.com} @email{russell@@alpha3.ersys.edmonton.ab.ca} (Russell Schulz) @email{scott@@geom.umn.edu} Igor V. Semenyuk @email{iga@@argrd0.argonaut.su} Christopher Sawtell @email{chris@@gerty.equinox.gen.nz} @email{schuler@@bds.sub.org} (Bernd Schuler) @email{uunet!gold.sub.org!root} (Christian Seyb) Marcus Shang @email{marcus.shang@@canada.cdev.com} @email{s4mjs!mjs@@nirvo.nirvonics.com} (M. J. Shannon Jr.) @email{shields@@tembel.org} (Michael Shields) @email{peter@@ficc.ferranti.com} (Peter da Silva) @email{vince@@victrola.sea.wa.us} (Vince Skahan) @email{frumious!pat} (Patrick Smith) @email{roscom!monty@@bu.edu} (Monty Solomon) @email{sommerfeld@@orchard.medford.ma.us} (Bill Sommerfeld) Julian Stacey @email{stacey@@guug.de} @email{evesg@@etlrips.etl.go.jp} (Gjoen Stein) Harlan Stenn @email{harlan@@mumps.pfcs.com} Ralf Stephan @email{ralf@@ark.abg.sub.org} @email{johannes@@titan.westfalen.de} (Johannes Stille) @email{chs@@antic.apu.fi} (Hannu Strang) @email{ralf@@reswi.ruhr.de} (Ralf E. Stranzenbach) @email{sullivan@@Mathcom.com} (S. Sullivan) Shigeya Suzuki @email{shigeya@@dink.foretune.co.jp} @email{kls@@ditka.Chicago.COM} (Karl Swartz) @email{swiers@@plains.NoDak.edu} Oleg Tabarovsky @email{olg@@olghome.pccentre.msk.su} @email{ikeda@@honey.misystems.co.jp} (Takatoshi Ikeda) John Theus @email{john@@theus.rain.com} @email{rd@@aii.com} (Bob Thrush) ppKarsten Thygesen @email{karthy@@dannug.dk} Graham Toal @email{gtoal@@pizzabox.demon.co.uk} @email{rmtodd@@servalan.servalan.com} (Richard Todd) Michael Ju. Tokarev @email{mjt@@tls.msk.ru} Martin Tomes @email{mt00@@controls.eurotherm.co.uk} Len Tower @email{tower-prep@@ai.mit.edu} Mark Towfiq @email{justice!towfiq@@Eingedi.Newton.MA.US} @email{mju@@mudos.ann-arbor.mi.us} (Marc Unangst) Matthias Urlichs @email{urlichs@@smurf.noris.de} Tomi Vainio @email{tomppa@@fidata.fi} @email{a3@@a3.xs4all.nl} (Adri Verhoef) Andrew Vignaux @email{ajv@@ferrari.datamark.co.nz} @email{vogel@@omega.ssw.de} (Andreas Vogel) Dima Volodin @email{dvv@@hq.demos.su} @email{jos@@bull.nl} (Jos Vos) @email{jv@@nl.net} (Johan Vromans) David Vrona @email{dave@@sashimi.wwa.com} @email{Marcel.Waldvogel@@nice.usergroup.ethz.ch} (Marcel Waldvogel) @email{steve@@nshore.org} (Stephen J. Walick) @email{syd@@dsinc.dsi.com} (Syd Weinstein) @email{gerben@@rna.indiv.nluug.nl} (Gerben Wierda) @email{jbw@@cs.bu.edu} (Joe Wells) @email{frnkmth!twwells.com!bill} (T. William Wells) Peter Wemm @email{Peter_Wemm@@zeus.dialix.oz.au} @email{mauxci!eci386!woods@@apple.com} (Greg A. Woods) @email{John.Woods@@proteon.com} (John Woods) Michael Yu.Yaroslavtsev @email{mike@@yaranga.ipmce.su} Alexei K. Yushin @email{root@@july.elis.crimea.ua} @email{jon@@console.ais.org} (Jon Zeeff) Matthias Zepf @email{agnus@@amylnd.stgt.sub.org} Eric Ziegast @email{uunet!ziegast} @end example @node Index (concepts), Index (configuration file), Acknowledgements, Top @unnumbered Concept Index @printindex cp @node Index (configuration file), , Index (concepts), Top @unnumbered Configuration File Index @printindex fn @contents @bye uucp-1.07/uusched.in0000664000076400007640000000047307665321757010126 #!/bin/sh # uusched # Call all systems which have work in a random order # # Copyright (C) 1992, 1993 Ian Lance Taylor # # Please feel free do whatever you like with this exciting shell # script. # # This is pretty trivial, since all the functionality was moved into # uucico itself. # exec @SBINDIR@/uucico -r1 $* uucp-1.07/uuto.in0000664000076400007640000000365207665321757007464 #!/bin/sh # uuto # Send files to a user on another system. # # Copyright (C) 1992, 1993, 2002 Ian Lance Taylor # # Please feel free do whatever you like with this exciting shell # script. # # This is pretty trivial, since all the functionality was moved into # uucp itself. The -t means to interpret the final argument as # system!user, the -R means to copy directories recursively, and the # -c means to not copy the files to the spool directory (may be # overriden by -C or -p). # usage="Taylor UUCP @VERS@, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor Usage: $0 [options] file1 [file2...] system!user -c,--nocopy: Do not copy local files to spool directory (default) -C,-p,--copy: Copy local files to spool directory -d,--directories: Create necessary directories (default) -f,--nodirectories: Do not create directories (fail if they do not exist) -g,--grade grade: Set job grade (must be alphabetic) -m,--mail: Report status of copy by mail -n,--notify user: Report status of copy by mail to remote user -R,--recursive: Copy directories recursively (default) -r,--nouucico: Do not start uucico daemon -s,--status file: Report completion status to file -j,--jobid: Report job id -W,--noexpand: Do not add current directory to remote filenames -u,--usage name: Set user name -x,--debug debug: Set debugging level -I,--config file: Set configuration file to use -v,--version: Print version and exit --help: Print help and exit Report bugs to taylor-uucp@gnu.org" case $# in 1) case "z${1}" in z--help) echo "$usage" exit 0 ;; z--version) echo "uuto (Taylor UUCP) @VERS@" echo "Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor" echo "This program is free software; you may redistribute it under the terms of" echo "the GNU General Public LIcense. This program has ABSOLUTELY NO WARRANTY." exit 0 ;; *) ;; esac ;; *) ;; esac exec @BINDIR@/uucp -t -R -c $* uucp-1.07/cu.10000664000076400007640000001701607665321755006626 ''' $Id: cu.1,v 1.10 2002/03/05 22:13:33 ian Rel $ .TH cu 1 "Taylor UUCP 1.07" .SH NAME cu \- Call up another system .SH SYNOPSIS .B cu [ options ] [ system | phone | "dir" ] .SH DESCRIPTION The .I cu command is used to call up another system and act as a dial in terminal. It can also do simple file transfers with no error checking. .I cu takes a single argument, besides the options. If the argument is the string "dir" cu will make a direct connection to the port. This may only be used by users with write access to the port, as it permits reprogramming the modem. Otherwise, if the argument begins with a digit, it is taken to be a phone number to call. Otherwise, it is taken to be the name of a system to call. The .B \-z or .B \-\-system option may be used to name a system beginning with a digit, and the .B \-c or .B \-\-phone option may be used to name a phone number that does not begin with a digit. .I cu locates a port to use in the UUCP configuration files. If a simple system name is given, it will select a port appropriate for that system. The .B \-p, \-\-port, \-l, \-\-line, \-s and .B \-\-speed options may be used to control the port selection. When a connection is made to the remote system, .I cu forks into two processes. One reads from the port and writes to the terminal, while the other reads from the terminal and writes to the port. .I cu provides several commands that may be used during the conversation. The commands all begin with an escape character, initially .B ~ (tilde). The escape character is only recognized at the beginning of a line. To send an escape character to the remote system at the start of a line, it must be entered twice. All commands are either a single character or a word beginning with .B % (percent sign). .I cu recognizes the following commands: .TP 5 .B ~. Terminate the conversation. .TP 5 .B ~! command Run command in a shell. If command is empty, starts up a shell. .TP 5 .B ~$ command Run command, sending the standard output to the remote system. .TP 5 .B ~| command Run command, taking the standard input from the remote system. .TP 5 .B ~+ command Run command, taking the standard input from the remote system and sending the standard output to the remote system. .TP 5 .B ~#, ~%break Send a break signal, if possible. .TP 5 .B ~c directory, ~%cd directory Change the local directory. .TP 5 .B ~> file Send a file to the remote system. This just dumps the file over the communication line. It is assumed that the remote system is expecting it. .TP 5 .B ~< Receive a file from the remote system. This prompts for the local file name and for the remote command to execute to begin the file transfer. It continues accepting data until the contents of the .B eofread variable are seen. .TP 5 .B ~p from to, ~%put from to Send a file to a remote Unix system. This runs the appropriate commands on the remote system. .TP 5 .B ~t from to, ~%take from to Retrieve a file from a remote Unix system. This runs the appropriate commands on the remote system. .TP 5 .B ~s variable value Set a .I cu variable to the given value. If value is not given, the variable is set to .B true. .TP 5 .B ~! variable Set a .I cu variable to .B false. .TP 5 .B ~z Suspend the cu session. This is only supported on some systems. On systems for which ^Z may be used to suspend a job, .B ~^Z will also suspend the session. .TP 5 .B ~%nostop Turn off XON/XOFF handling. .TP 5 .B ~%stop Turn on XON/XOFF handling. .TP 5 .B ~v List all the variables and their values. .TP 5 .B ~? List all commands. .I cu also supports several variables. They may be listed with the .B ~v command, and set with the .B ~s or .B ~! commands. .TP 5 .B escape The escape character. Initially .B ~ (tilde). .TP 5 .B delay If this variable is true, .I cu will delay for a second after recognizing the escape character before printing the name of the local system. The default is true. .TP 5 .B eol The list of characters which are considered to finish a line. The escape character is only recognized after one of these is seen. The default is carriage return, ^U, ^C, ^O, ^D, ^S, ^Q, ^R. .TP 5 .B binary Whether to transfer binary data when sending a file. If this is false, then newlines in the file being sent are converted to carriage returns. The default is false. .TP 5 .B binary-prefix A string used before sending a binary character in a file transfer, if the .B binary variable is true. The default is ^V. .TP 5 .B echo-check Whether to check file transfers by examining what the remote system echoes back. This probably doesn't work very well. The default is false. .TP 5 .B echonl The character to look for after sending each line in a file. The default is carriage return. .TP 5 .B timeout The timeout to use, in seconds, when looking for a character, either when doing echo checking or when looking for the .B echonl character. The default is 30. .TP 5 .B kill The character to use delete a line if the echo check fails. The default is ^U. .TP 5 .B resend The number of times to resend a line if the echo check continues to fail. The default is 10. .TP 5 .B eofwrite The string to write after sending a file with the .B ~> command. The default is ^D. .TP 5 .B eofread The string to look for when receiving a file with the .B ~< command. The default is $, which is intended to be a typical shell prompt. .TP 5 .B verbose Whether to print accumulated information during a file transfer. The default is true. .SH OPTIONS The following options may be given to .I cu. .TP 5 .B \-e, \-\-parity=even Use even parity. .TP 5 .B \-o, \-\-parity=odd Use odd parity. .TP 5 .B \-\-parity=none Use no parity. No parity is also used if both .B \-e and .B \-o are given. .TP 5 .B \-h, \-\-halfduplex Echo characters locally (half-duplex mode). .TP 5 .B \-\-nostop Turn off XON/XOFF handling (it is on by default). .TP 5 .B \-E char, \-\-escape char Set the escape character. Initially .B ~ (tilde). To eliminate the escape character, use .B -E ''. .TP 5 .B \-z system, \-\-system system The system to call. .TP 5 .B \-c phone-number, \-\-phone phone-number The phone number to call. .TP 5 .B \-p port, \-\-port port Name the port to use. .TP 5 .B \-a port Equivalent to .B \-\-port port. .TP 5 .B \-l line, \-\-line line Name the line to use by giving a device name. This may be used to dial out on ports that are not listed in the UUCP configuration files. Write access to the device is required. .TP 5 .B \-s speed, \-\-speed speed The speed (baud rate) to use. .TP 5 .B \-# Where # is a number, equivalent to .B \-\-speed #. .TP 5 .B \-n, \-\-prompt Prompt for the phone number to use. .TP 5 .B \-d Enter debugging mode. Equivalent to .B \-\-debug all. .TP 5 .B \-x type, \-\-debug type Turn on particular debugging types. The following types are recognized: abnormal, chat, handshake, uucp-proto, proto, port, config, spooldir, execute, incoming, outgoing. Only abnormal, chat, handshake, port, config, incoming and outgoing are meaningful for .I cu. Multiple types may be given, separated by commas, and the .B \-\-debug option may appear multiple times. A number may also be given, which will turn on that many types from the foregoing list; for example, .B \-\-debug 2 is equivalent to .B \-\-debug abnormal,chat. .B \-\-debug all may be used to turn on all debugging options. .TP 5 .B \-I file, \-\-config file Set configuration file to use. This option may not be available, depending upon how .I cu was compiled. .TP 5 .B \-v, \-\-version Report version information and exit. .TP 5 .B \-\-help Print a help message and exit. .SH BUGS This program does not work very well. .SH AUTHOR Ian Lance Taylor uucp-1.07/uucp.10000664000076400007640000001204107665321757007166 ''' $Id: uucp.1,v 1.12 2002/03/05 22:13:33 ian Rel $ .TH uucp 1 "Taylor UUCP 1.07" .SH NAME uucp \- Unix to Unix copy .SH SYNOPSIS .B uucp [ options ] source-file destination-file .PP .B uucp [ options ] source-file... destination-directory .SH DESCRIPTION The .I uucp command copies files between systems. Each .I file argument is either a pathname on the local machine or is of the form .IP system!path .LP which is interpreted as being on a remote system. In the first form, the contents of the first file are copied to the second. In the second form, each source file is copied into the destination directory. A file be transferred to or from .I system2 via .I system1 by using .IP system1!system2!path. .LP Any pathname that does not begin with / or ~ will be appended to the current directory (unless the .B \-W or .B \--noexpand option is used); this resulting path will not necessarily exist on a remote system. A pathname beginning with a simple ~ starts at the UUCP public directory; a pathname beginning with ~name starts at the home directory of the named user. The ~ is interpreted on the appropriate system. Note that some shells will interpret a simple ~ to the local home directory before .I uucp sees it; to avoid this the ~ must be quoted. Shell metacharacters ? * [ ] are interpreted on the appropriate system, assuming they are quoted to prevent the shell from interpreting them first. The copy does not take place immediately, but is queued up for the .I uucico (8) daemon; the daemon is started immediately unless the .B \-r or .B \-\-nouucico switch is given. In any case, the next time the remote system is called the file(s) will be copied. .SH OPTIONS The following options may be given to .I uucp. .TP 5 .B \-c, \-\-nocopy Do not copy local source files to the spool directory. If they are removed before being processed by the .I uucico (8) daemon, the copy will fail. The files must be readable by the .I uucico (8) daemon, and by the invoking user. .TP 5 .B \-C, \-\-copy Copy local source files to the spool directory. This is the default. .TP 5 .B \-d, \-\-directories Create all necessary directories when doing the copy. This is the default. .TP 5 .B \-f, \-\-nodirectories If any necessary directories do not exist for the destination path, abort the copy. .TP 5 .B \-R, \-\-recursive If any of the source file names are directories, copy their contents recursively to the destination (which must itself be a directory). .TP 5 .B \-g grade, \-\-grade grade Set the grade of the file transfer command. Jobs of a higher grade are executed first. Grades run 0 ... 9 A ... Z a ... z from high to low. .TP 5 .B \-m, \-\-mail Report completion or failure of the file transfer by .I mail (1). .TP 5 .B \-n user, \-\-notify user Report completion or failure of the file transfer by .I mail (1) to the named user on the remote system. .TP 5 .B \-r, \-\-nouucico Do not start .I uucico (8) daemon immediately; merely queue up the file transfer for later execution. .TP 5 .B \-j, \-\-jobid Print jobid on standard output. The job may be later cancelled by passing the jobid to the .B \-k switch of .I uustat (1). It is possible for some complex operations to produce more than one jobid, in which case each will be printed on a separate line. For example .br .in +0.5i .nf uucp sys1!~user1/file1 sys2!~user2/file2 ~user3 .fi .in -0.5i will generate two separate jobs, one for the system .I sys1 and one for the system .I sys2. .TP 5 .B \-W, \-\-noexpand Do not prepend remote relative path names with the current directory. .TP 5 .B \-t, \-\-uuto This option is used by the .I uuto shell script. It causes .I uucp to interpret the final argument as .I system!user. The file(s) are sent to .I ~/receive/USER/LOCAL on the remote system, where .I USER is from the final argument and .I LOCAL is the local UUCP system name. Also, .I uucp will act as though .I \-\-notify user were specified. .TP 5 .B \-x type, \-\-debug type Turn on particular debugging types. The following types are recognized: abnormal, chat, handshake, uucp-proto, proto, port, config, spooldir, execute, incoming, outgoing. Only abnormal, config, spooldir and execute are meaningful for .I uucp. Multiple types may be given, separated by commas, and the .B \-\-debug option may appear multiple times. A number may also be given, which will turn on that many types from the foregoing list; for example, .B \-\-debug 2 is equivalent to .B \-\-debug abnormal,chat. .TP 5 .B \-I file, \-\-config file Set configuration file to use. This option may not be available, depending upon how .I uucp was compiled. .TP 5 .B \-v, \-\-version Report version information and exit. .TP 5 .B \-\-help Print a help message and exit. .SH SEE ALSO mail(1), uux(1), uustat(1), uucico(8) .SH BUGS Some of the options are dependent on the capabilities of the .I uucico (8) daemon on the remote system. The .I \-n and .I \-m switches do not work when transferring a file from one remote system to another. File modes are not preserved, except for the execute bit. The resulting file is owned by the uucp user. .SH AUTHOR Ian Lance Taylor uucp-1.07/uustat.10000664000076400007640000002753307665321757007553 ''' $Id: uustat.1,v 1.14 2002/03/05 22:15:10 ian Rel $ .TH uustat 1 "Taylor UUCP 1.07" .SH NAME uustat \- UUCP status inquiry and control .SH SYNOPSIS .B uustat \-a .PP .B uustat \-\-all .PP .B uustat [ .B \-eKRiMNQ ] [ .B \-sS system ] [ .B \-uU user ] [ .B \-cC command ] [ .B \-oy hours ] [ .B \-B lines ] [ .B \-\-executions ] [ .B \-\-kill-all ] [ .B \-\-rejuvenate-all ] [ .B \-\-prompt ] [ .B \-\-mail ] [ .B \-\-notify ] [ .B \-\-no-list ] [ .B \-\-system system ] [ .B \-\-not-system system ] [ .B \-\-user user ] [ .B \-\-not-user user ] [ .B \-\-command command ] [ .B \-\-not-command command ] [ .B \-\-older-than hours ] [ .B \-\-younger-than hours ] [ .B \-\-mail-lines lines ] .PP .B uustat [ .B \-kr jobid ] [ .B \-\-kill jobid ] [ .B \-\-rejuvenate jobid ] .PP .B uustat \-q [ .B \-sS system ] [ .B \-oy hours ] [ .B \-\-system system ] [ .B \-\-not-system system ] [ .B \-\-older-than hours ] [ .B \-\-younger-than hours ] .PP .B uustat \-\-list [ .B \-sS system ] [ .B \-oy hours ] [ .B \-\-system system ] [ .B \-\-not-system system ] [ .B \-\-older-than hours ] [ .B \-\-younger-than hours ] .PP .B uustat \-m .PP .B uustat \-\-status .PP .B uustat \-p .PP .B uustat \-\-ps .SH DESCRIPTION The .I uustat command can display various types of status information about the UUCP system. It can also be used to cancel or rejuvenate requests made by .I uucp (1) or .I uux (1). By default .I uustat displays all jobs queued up for the invoking user, as if given the .B \-\-user option with the appropriate argument. If any of the .B \-a, .B \-\-all, .B \-e, .B \-\-executions, .B \-s, .B \-\-system, .B \-S, .B \-\-not-system, .B \-u, .B \-\-user, .B \-U, .B \-\-not-user, .B \-c, .B \-\-command, .B \-C, .B \-\-not-command, .B \-o, .B \-\-older-than, .B \-y, .B \-\-younger-than options are given, then all jobs which match the combined specifications are displayed. The .B \-K or .B \-\-kill-all option may be used to kill off a selected group of jobs, such as all jobs more than 7 days old. .SH OPTIONS The following options may be given to .I uustat. .TP 5 .B \-a, \-\-all List all queued file transfer requests. .TP 5 .B \-e, \-\-executions List queued execution requests rather than queued file transfer requests. Queued execution requests are processed by .I uuxqt (8) rather than .I uucico (8). Queued execution requests may be waiting for some file to be transferred from a remote system. They are created by an invocation of .I uux (1). .TP 5 .B \-s system, \-\-system system List all jobs queued up for the named system. These options may be specified multiple times, in which case all jobs for all the systems will be listed. If used with .B \-\-list only the systems named will be listed. .TP 5 .B \-S system, \-\-not-system system List all jobs queued for systems other than the one named. These options may be specified multiple times, in which case no jobs from any of the specified systems will be listed. If used with .B \-\-list only the systems not named will be listed. These options may not be used with .B \-s or .B \-\-system. .TP 5 .B \-u user, \-\-user user List all jobs queued up for the named user. These options may be specified multiple times, in which case all jobs for all the users will be listed. .TP 5 .B \-U user, \-\-not-user user List all jobs queued up for users other than the one named. These options may be specified multiple times, in which case no jobs from any of the specified users will be listed. These options may not be used with .B \-u or .B \-\-user. .TP 5 .B \-c command, \-\-command command List all jobs requesting the execution of the named command. If .B command is .I ALL this will list all jobs requesting the execution of some command (as opposed to simply requesting a file transfer). These options may be specified multiple times, in which case all jobs requesting any of the commands will be listed. .TP 5 .B \-C command, \-\-not-command command List all jobs requesting execution of some command other than the named command, or, if .B command is .I ALL, list all jobs that simply request a file transfer (as opposed to requesting the execution of some command). These options may be specified multiple times, in which case no job requesting one of the specified commands will be listed. These options may not be used with .B \-c or .B \-\-command. .TP 5 .B \-o hours, \-\-older-than hours List all queued jobs older than the given number of hours. If used with .B \-\-list only systems whose oldest job is older than the given number of hours will be listed. .TP 5 .B \-y hours, \-\-younger-than hours List all queued jobs younger than the given number of hours. If used with .B \-\-list only systems whose oldest job is younger than the given number of hours will be listed. .TP 5 .B \-k jobid, \-\-kill jobid Kill the named job. The job id is shown by the default output format, as well as by the .B \-j or .B \-\-jobid option to .I uucp (1) or .I uux (1). A job may only be killed by the user who created the job, or by the UUCP administrator or the superuser. The .B \-k or .B \-\-kill options may be used multiple times on the command line to kill several jobs. .TP 5 .B \-r jobid, \-\-rejuvenate jobid Rejuvenate the named job. This will mark it as having been invoked at the current time, affecting the output of the .B \-o, .B \-\-older-than, .B \-y, or .B \-\-younger-than options and preserving it from any automated cleanup daemon. The job id is shown by the default output format, as well as by the .B \-j or .B \-\-jobid options to .I uucp (1) or .I uux (1). A job may only be rejuvenated by the user who created the job, or by the UUCP administrator or the superuser. The .B \-r or .B \-\-rejuvenate options may be used multiple times on the command line to rejuvenate several jobs. .TP 5 .B \-q, \-\-list Display the status of commands, executions and conversations for all remote systems for which commands or executions are queued. The .B \-s, .B \-\-system, .B \-S, .B \-\-not-system, .B \-o, .B \-\-older-than, .B \-y, and .B \-\-younger-than options may be used to restrict the systems which are listed. Systems for which no commands or executions are queued will never be listed. .TP 5 .B \-m, \-\-status Display the status of conversations for all remote systems. .TP 5 .B \-p, \-\-ps Display the status of all processes holding UUCP locks on systems or ports. .TP 5 .B \-i, \-\-prompt For each listed job, prompt whether to kill the job or not. If the first character of the input line is .I y or .I Y the job will be killed. .TP 5 .B \-K, \-\-kill-all Automatically kill each listed job. This can be useful for automatic cleanup scripts, in conjunction with the .B \-\-mail and .B \-\-notify options. .TP 5 .B \-R, \-\-rejuvenate-all Automatically rejuvenate each listed job. This may not be used with .B \-\-kill-all. .TP 5 .B \-M, \-\-mail For each listed job, send mail to the UUCP administrator. If the job is killed (due to .B \-\-kill-all or .B \-\-prompt with an affirmative response) the mail will indicate that. A comment specified by the .B \-\-comment option may be included. If the job is an execution, the initial portion of its standard input will be included in the mail message; the number of lines to include may be set with the .B \-\-mail-lines option (the default is 100). If the standard input contains null characters, it is assumed to be a binary file and is not included. .TP 5 .B \-N, \-\-notify For each listed job, send mail to the user who requested the job. The mail is identical to that sent by the .B \-M or .B \-\-mail options. .TP 5 .B \-W comment, \-\-comment comment Specify a comment to be included in mail sent with the .B \-M, .B \-\-mail, .B \-N, or .B \-\-notify options. .TP 5 .B \-B lines, \-\-mail-lines lines When the .B \-M, .B \-\-mail, .B \-N, or .B \-\-notify options are used to send mail about an execution with standard input, this option controls the number of lines of standard input to include in the message. The default is 100. .TP 5 .B \-Q, \-\-no-list Do not actually list the job, but only take any actions indicated by the .B \-i, .B \-\-prompt, .B \-K, .B \-\-kill-all, .B \-M, .B \-\-mail, .B \-N or .B \-\-notify options. .TP 5 .B \-x type, \-\-debug type Turn on particular debugging types. The following types are recognized: abnormal, chat, handshake, uucp-proto, proto, port, config, spooldir, execute, incoming, outgoing. Only abnormal, config, spooldir and execute are meaningful for .I uustat. Multiple types may be given, separated by commas, and the .B \-\-debug option may appear multiple times. A number may also be given, which will turn on that many types from the foregoing list; for example, .B \-\-debug 2 is equivalent to .B \-\-debug abnormal,chat. .TP 5 .B \-I file, \-\-config file Set configuration file to use. This option may not be available, depending upon how .I uustat was compiled. .TP 5 .B \-v, \-\-version Report version information and exit. .TP 5 .B \-\-help Print a help message and exit. .SH EXAMPLES .br .nf uustat --all .fi Display status of all jobs. A sample output line is as follows: .br .in +0.5i .nf bugsA027h bugs ian 04-01 13:50 Executing rmail ian@airs.com (sending 1283 bytes) .fi .in -0.5i The format is .br .in +0.5i .nf jobid system user queue-date command (size) .fi .in -0.5i The jobid may be passed to the .B \-\-kill or .B \-\-rejuvenate options. The size indicates how much data is to be transferred to the remote system, and is absent for a file receive request. The .B \-\-system, .B \-\-not-system, .B \-\-user, .B \-\-not-user, .B \-\-command, .B \-\-not-command, .B \-\-older-than, and .B \-\-younger-than options may be used to control which jobs are listed. .br .nf uustat --executions .fi Display status of queued up execution requests. A sample output line is as follows: .br .in +0.5i .nf bugs bugs!ian 05-20 12:51 rmail ian .fi .in -0.5i The format is .br .in +0.5i .nf system requestor queue-date command .fi .in -0.5i The .B \-\-system, .B \-\-not-system, .B \-\-user, .B \-\-not-user, .B \-\-command, .B \-\-not-command, .B \-\-older-than, and .B \-\-younger-than options may be used to control which requests are listed. .br .nf uustat --list .fi Display status for all systems with queued up commands. A sample output line is as follows: .br .in +0.5i .nf bugs 4C (1 hour) 0X (0 secs) 04-01 14:45 Dial failed .fi .in -0.5i This indicates the system, the number of queued commands, the age of the oldest queued command, the number of queued local executions, the age of the oldest queued execution, the date of the last conversation, and the status of that conversation. .br .nf uustat --status .fi Display conversation status for all remote systems. A sample output line is as follows: .br .in +0.5i .nf bugs 04-01 15:51 Conversation complete .fi .in -0.5i This indicates the system, the date of the last conversation, and the status of that conversation. If the last conversation failed, .I uustat will indicate how many attempts have been made to call the system. If the retry period is currently preventing calls to that system, .I uustat also displays the time when the next call will be permitted. .br .nf uustat --ps .fi Display the status of all processes holding UUCP locks. The output format is system dependent, as .I uustat simply invokes .I ps (1) on each process holding a lock. .br .in +0.5i .nf uustat --command rmail --older-than 168 --kill-all --no-list --mail --notify --comment "Queued for over 1 week" .fi .in -0.5i This will kill all .I rmail commands that have been queued up waiting for delivery for over 1 week (168 hours). For each such command, mail will be sent both to the UUCP administrator and to the user who requested the rmail execution. The mail message sent will include the string given by the .B \-\-comment option. The .B \-\-no-list option prevents any of the jobs from being listed on the terminal, so any output from the program will be error messages. .SH SEE ALSO ps(1), rmail(1), uucp(1), uux(1), uucico(8), uuxqt(8) .SH AUTHOR Ian Lance Taylor (ian@airs.com) uucp-1.07/uux.10000664000076400007640000001620207665321760007030 ''' $Id: uux.1,v 1.15 2002/03/05 22:20:48 ian Rel $ .TH uux 1 "Taylor UUCP 1.07" .SH NAME uux \- Remote command execution over UUCP .SH SYNOPSIS .B uux [ options ] command .SH DESCRIPTION The .I uux command is used to execute a command on a remote system, or to execute a command on the local system using files from remote systems. The command is not executed immediately; the request is queued until the .I uucico (8) daemon calls the system and executes it. The daemon is started automatically unless one of the .B \-r or .B \-\-nouucico options is given. The actual command execution is done by the .I uuxqt (8) daemon. File arguments can be gathered from remote systems to the execution system, as can standard input. Standard output may be directed to a file on a remote system. The command name may be preceded by a system name followed by an exclamation point if it is to be executed on a remote system. An empty system name is taken as the local system. Each argument that contains an exclamation point is treated as naming a file. The system which the file is on is before the exclamation point, and the pathname on that system follows it. An empty system name is taken as the local system; this must be used to transfer a file to a command being executed on a remote system. If the path is not absolute, it will be appended to the current working directory on the local system; the result may not be meaningful on the remote system. A pathname may begin with ~/, in which case it is relative to the UUCP public directory (usually /usr/spool/uucppublic or /var/spool/uucppublic) on the appropriate system. A pathname may begin with ~name/, in which case it is relative to the home directory of the named user on the appropriate system. Standard input and output may be redirected as usual; the pathnames used may contain exclamation points to indicate that they are on remote systems. Note that the redirection characters must be quoted so that they are passed to .I uux rather than interpreted by the shell. Append redirection (>>) does not work. All specified files are gathered together into a single directory before execution of the command begins. This means that each file must have a distinct base name. For example, .br .in +0.5i .nf uux 'sys1!diff sys2!~user1/foo sys3!~user2/foo >!foo.diff' .fi .in -0.5i will fail because both files will be copied to sys1 and stored under the name foo. Arguments may be quoted by parentheses to avoid interpretation of exclamation points. This is useful when executing the .I uucp command on a remote system. A request to execute an empty command (e.g., .I uux sys!) will create a poll file for the specified system. The exit status of .I uux is one of the codes found in the header file .B sysexits.h. In particular, .B EX_OK ( .B 0 ) indicates success, and .B EX_TEMPFAIL ( .B 75 ) indicates a temporary failure. .SH OPTIONS The following options may be given to .I uux. .TP 5 .B \-, \-p, \-\-stdin Read standard input and use it as the standard input for the command to be executed. .TP 5 .B \-c, \-\-nocopy Do not copy local files to the spool directory. This is the default. If they are removed before being processed by the .I uucico (8) daemon, the copy will fail. The files must be readable by the .I uucico (8) daemon, as well as the by the invoker of .I uux. .TP 5 .B \-C, \-\-copy Copy local files to the spool directory. .TP 5 .B \-l, \-\-link Link local files into the spool directory. If a file can not be linked because it is on a different device, it will be copied unless one of the .B \-c or .B \-\-nocopy options also appears (in other words, use of .B \-\-link switches the default from .B \-\-nocopy to .B \-\-copy). If the files are changed before being processed by the .I uucico (8) daemon, the changed versions will be used. The files must be readable by the .I uucico (8) daemon, as well as by the invoker of .I uux. .TP 5 .B \-g grade, \-\-grade grade Set the grade of the file transfer command. Jobs of a higher grade are executed first. Grades run 0 ... 9 A ... Z a ... z from high to low. .TP 5 .B \-n, \-\-notification=no Do not send mail about the status of the job, even if it fails. .TP 5 .B \-z, \-\-notification=error Send mail about the status of the job if an error occurs. For many .I uuxqt daemons, including the Taylor UUCP .I uuxqt, this is the default action; for those, .B \-\-notification=error will have no effect. However, some .I uuxqt daemons will send mail if the job succeeds unless the .B \-\-notification=error option is used, and some other .I uuxqt daemons will not send mail if the job fails unless the .B \-\-notification=error option is used. .TP 5 .B \-r, \-\-nouucico Do not start the .I uucico (8) daemon immediately; merely queue up the execution request for later processing. .TP 5 .B \-j, \-\-jobid Print jobids on standard output. A jobid will be generated for each file copy operation required to perform the operation. These file copies may be cancelled by passing the jobid to the .B \-\-kill switch of .I uustat (1), which will make the execution impossible to complete. .TP 5 .B \-a address, \-\-requestor address Report job status to the specified e-mail address. .TP 5 .B \-x type, \-\-debug type Turn on particular debugging types. The following types are recognized: abnormal, chat, handshake, uucp-proto, proto, port, config, spooldir, execute, incoming, outgoing. Only abnormal, config, spooldir and execute are meaningful for .I uux. Multiple types may be given, separated by commas, and the .B \-\-debug option may appear multiple times. A number may also be given, which will turn on that many types from the foregoing list; for example, .B \-\-debug 2 is equivalent to .B \-\-debug abnormal,chat. .TP 5 .B \-I file, \-\-config file Set configuration file to use. This option may not be available, depending upon how .I uux was compiled. .TP 5 .B \-v, \-\-version Report version information and exit. .TP 5 .B \-\-help Print a help message and exit. .SH EXAMPLES .br .nf uux -z - sys1!rmail user1 .fi Execute the command ``rmail user1'' on the system sys1, giving it as standard input whatever is given to .I uux as standard input. If a failure occurs, send a message using .I mail (1). .br .nf uux 'diff -c sys1!~user1/file1 sys2!~user2/file2 >!file.diff' .fi Fetch the two named files from system sys1 and system sys2 and execute .I diff putting the result in file.diff in the current directory. The current directory must be writable by the .I uuxqt (8) daemon for this to work. .br .nf uux 'sys1!uucp ~user1/file1 (sys2!~user2/file2)' .fi Execute .I uucp on the system sys1 copying file1 (on system sys1) to sys2. This illustrates the use of parentheses for quoting. .SH RESTRICTIONS The remote system may not permit you to execute certain commands. Many remote systems only permit the execution of .I rmail and .I rnews. Some of the options are dependent on the capabilities of the .I uuxqt (8) daemon on the remote system. .SH SEE ALSO mail(1), uustat(1), uucp(1), uucico(8), uuxqt(8) .SH BUGS Files can not be referenced across multiple systems. Too many jobids are output by .B \-\-jobid, and there is no good way to cancel a local execution requiring remote files. .SH AUTHOR Ian Lance Taylor (ian@airs.com) uucp-1.07/uucico.80000664000076400007640000001472207665321756007517 ''' $Id: uucico.8,v 1.20 2002/03/05 22:20:48 ian Rel $ .TH uucico 8 "Taylor UUCP 1.07" .SH NAME uucico \- UUCP file transfer daemon .SH SYNOPSIS .B uucico [ options ] .SH DESCRIPTION The .I uucico daemon processes file transfer requests queued by .I uucp (1) and .I uux (1). It is started when .I uucp or .I uux is run (unless they are given the .B \-r option). It is also typically started periodically using entries in the .I crontab table(s). When invoked with .B \-r1, .B \-\-master, .B \-s, .B \-\-system, or .B \-S, the daemon will place a call to a remote system, running in master mode. Otherwise the daemon will start in slave mode, accepting a call from a remote system. Typically a special login name will be set up for UUCP which automatically invokes .I uucico when a call is made. When .I uucico terminates, it invokes the .I uuxqt (8) daemon, unless the .B \-q or .B \-\-nouuxqt option is given; .I uuxqt (8) executes any work orders created by .I uux (1) on a remote system, and any work orders created locally which have received remote files for which they were waiting. If a call fails, .I uucico will normally refuse to retry the call until a certain (configurable) amount of time has passed. This may be overriden by the .B -f, .B --force, or .B -S option. The .B \-l, .B \-\-prompt, .B \-e, or .B \-\-loop options may be used to force .I uucico to produce its own prompts of "login: " and "Password:". When another daemon calls in, it will see these prompts and log in as usual. The login name and password will normally be checked against a separate list kept specially for .I uucico rather than the .I /etc/passwd file; it is possible on some systems to direct .I uucico to use the .I /etc/passwd file. The .B \-l or .B \--prompt option will prompt once and then exit; in this mode the UUCP administrator or the superuser may use the .B \-u or .B \--login option to force a login name, in which case .I uucico will not prompt for one. The .B \-e or .B \--loop option will prompt again after the first session is over; in this mode .I uucico will permanently control a port. If .I uucico receives a SIGQUIT, SIGTERM or SIGPIPE signal, it will cleanly abort any current conversation with a remote system and exit. If it receives a SIGHUP signal it will abort any current conversation, but will continue to place calls to (if invoked with .B \-r1 or .B \-\-master) and accept calls from (if invoked with .B \-e or .B \-\-loop) other systems. If it receives a SIGINT signal it will finish the current conversation, but will not place or accept any more calls. .SH OPTIONS The following options may be given to .I uucico. .TP 5 .B \-r1, \-\-master Start in master mode (call out to a system); implied by .B \-s, .B \-\-system, or .B \-S. If no system is specified, call any system for which work is waiting to be done. .TP 5 .B \-r0, \-\-slave Start in slave mode. This is the default. .TP 5 .B \-s system, \-\-system system Call the named system. .TP 5 .B \-S system Call the named system, ignoring any required wait. This is equivalent to .B \-s system \-f. .TP 5 .B \-f, \-\-force Ignore any required wait for any systems to be called. .TP 5 .B \-l, \-\-prompt Prompt for login name and password using "login: " and "Password:". This allows .I uucico to be easily run from .I inetd (8). The login name and password are checked against the UUCP password file, which probably has no connection to the file .I /etc/passwd. The .B \-\-login option may be used to force a login name, in which cause .I uucico will only prompt for a password. .TP 5 .B \-p port, \-\-port port Specify a port to call out on or to listen to. .TP 5 .B \-e, \-\-loop Enter endless loop of login/password prompts and slave mode daemon execution. The program will not stop by itself; you must use .I kill (1) to shut it down. .TP 5 .B \-w, \-\-wait After calling out (to a particular system when .B \-s, .B \-\-system, or .B \-S is specifed, or to all systems which have work when just .B \-r1 or .B \-\-master is specifed), begin an endless loop as with .B \-\-loop. .TP 5 .B \-q, \-\-nouuxqt Do not start the .I uuxqt (8) daemon when finished. .TP 5 .B \-c, \-\-quiet If no calls are permitted at this time, then don't make the call, but also do not put an error message in the log file and do not update the system status (as reported by .I uustat (1)). This can be convenient for automated polling scripts, which may want to simply attempt to call every system rather than worry about which particular systems may be called at the moment. This option also suppresses the log message indicating that there is no work to be done. .TP 5 .B \-C, \-\-ifwork Only call the system named by .B \-s, .B \-\-system or .B \-S if there is work for that system. .TP 5 .B \-D, \-\-nodetach Do not detach from the controlling terminal. Normally .I uucico detaches from the terminal before each call out to another system and before invoking .I uuxqt. This option prevents this. .TP 5 .B \-u name, \-\-login name Set the login name to use instead of that of the invoking user. This option may only be used by the UUCP administrator or the superuser. If used with .B \-\-prompt, this will cause .I uucico to prompt only for the password, not the login name. .TP 5 .B \-z, \-\-try-next If a call fails after the remote system is reached, try the next alternate rather than simply exiting. .TP 5 .B \-i type, \-\-stdin type Set the type of port to use when using standard input. The only support port type is TLI, and this is only available on machines which support the TLI networking interface. Specifying .B \-iTLI causes .I uucico to use TLI calls to perform I/O. .TP 5 .B \-x type, \-X type, \-\-debug type Turn on particular debugging types. The following types are recognized: abnormal, chat, handshake, uucp-proto, proto, port, config, spooldir, execute, incoming, outgoing. Multiple types may be given, separated by commas, and the .B \-\-debug option may appear multiple times. A number may also be given, which will turn on that many types from the foregoing list; for example, .B \-\-debug 2 is equivalent to .B \-\-debug abnormal,chat. The debugging output is sent to the debugging file, which may be printed using .I uulog -D. .TP 5 .B \-I file, \-\-config file Set configuration file to use. This option may not be available, depending upon how .I uucico was compiled. .TP 5 .B \-v, \-\-version Report version information and exit. .TP 5 .B \-\-help Print a help message and exit. .SH SEE ALSO kill(1), uucp(1), uux(1), uustat(1), uuxqt(8) .SH AUTHOR Ian Lance Taylor uucp-1.07/uuxqt.80000664000076400007640000000372307665321760007410 ''' $Id: uuxqt.8,v 1.10 2002/03/05 22:20:48 ian Rel $ .TH uuxqt 8 "Taylor UUCP 1.07" .SH NAME uuxqt \- UUCP execution daemon .SH SYNOPSIS .B uuxqt [ options ] .SH DESCRIPTION The .I uuxqt daemon executes commands requested by .I uux (1) from either the local system or from remote systems. It is started automatically by the .I uucico (8) daemon (unless .I uucico (8) is given the .B \-q or .B \-\-nouuxqt option). There is normally no need to run this command, since it will be invoked by .I uucico (8). However, it can be used to provide greater control over the processing of the work queue. Multiple invocations of .I uuxqt may be run at once, as controlled by the .I max-uuxqts configuration command. .SH OPTIONS The following options may be given to .I uuxqt. .TP 5 .B \-c command, \-\-command command Only execute requests for the specified command. For example: .br .in +0.5i .nf uuxqt --command rmail .fi .in -0.5i .TP 5 .B \-s system, \-\-system system Only execute requests originating from the specified system. .TP 5 .B \-x type, \-\-debug type Turn on particular debugging types. The following types are recognized: abnormal, chat, handshake, uucp-proto, proto, port, config, spooldir, execute, incoming, outgoing. Only abnormal, config, spooldir and execute are meaningful for .I uuxqt. Multiple types may be given, separated by commas, and the .B \-\-debug option may appear multiple times. A number may also be given, which will turn on that many types from the foregoing list; for example, .B \-\-debug 2 is equivalent to .B \-\-debug abnormal,chat. The debugging output is sent to the debugging file, which may be printed using .I uulog -D. .TP 5 .B \-I file, \-\-config Set configuration file to use. This option may not be available, depending upon how .I uuxqt was compiled. .TP 5 .B \-v, \-\-version Report version information and exit. .TP 5 .B \-\-help Print a help message and exit. .SH SEE ALSO uucp(1), uux(1), uucico(8) .SH AUTHOR Ian Lance Taylor (ian@airs.com) uucp-1.07/contrib/0000777000076400007640000000000007665533774007660 5uucp-1.07/contrib/Dial.Hayes0000664000076400007640000000363307665321760011435 #!xchat # @(#) dial.hayes V1.1 Tue Sep 1 13:59:58 1992 (Bob Denny) # # xchat script for dialing a vanilla Hayes modem # # Usage: # xchat dial.hayes telno # # where telno is the telephone number, subject to pause and wait # character modification. # # Uncomment the first two lines after "start:" to get debugging # in file "Dial.Log" # # Flush input, zero counter, set telephone number if supplied, # else fail if no telephone number given. # start: ### dbgfile Dial.Log ### dbgset 15 zero flush ifnstr notelno 0 telno 0 goto initmodem # # Missing telephone number. # notelno: logerr No telephone number given failed # # Reset the modem to nonvolatile profile. # Allow 3 sec. for response, as some modems are slow to reset. # initmodem: count ifgtr cantinit 4 send ATZ\r timeout initmodem 3000 expect setupmodem OK # # No response from modem # cantinit: logerr Can't wake modem failed # # Send the stuff needed to initialize the modem to the modes # needed for the particular modem flavor. The string below # is provided as a vanilla example. Allow 2 sec. for the # modem to respond to this command. # setupmodem: sleep 1000 send ATM0S7=90S11=120\r timeout setupfail 2000 expect setupfail ERROR expect dialnumber OK # # Modem barfed or died on setup command. # setupfail: logerr Error in modem setup string failed # # Dial the supplied number. Handle the various errors that # can come back from the modem by logging the error. # dialnumber: sleep 1000 send ATDT dial send \r flush timeout timeout 90000 expect connected CONNECT expect busy BUSY expect nocarrier NO CARRIER expect noanswer NO ANSWER expect nodialtone NO DIALTONE # # Success! # connected: success # # Handle modem dial failures # timeout: logerr Modem or carrier timeout. failed busy: logerr BUSY failed nocarrier: logerr NO CARRIER failed noanswer: logerr NO ANSWER failed nodialtone: logerr NO DIALTONE failed # # end # uucp-1.07/contrib/Hangup.Hayes0000664000076400007640000000246107665321760012004 #!xchat # @(#) Hangup.Hayes V1.1 Tue Sep 1 14:04:25 1992 (Bob Denny) # # xchat script for hanging up a Hayes-type modem. When used with Taylor # UUCP, this script should be run as the dialer-complete and dialer-abort # script with xchat. # # Usage: # xchat Hangup.Hayes [ x ] # # where 'x' is any string. If it is present, this script will log the # modem reset as an ABORT reset, otherwise it will not log anything. # # Uncomment the lines starting with '###' to get debugging log. # start: ### dbgfile Hangup.Log ### dbgset 15 zero sleep 2000 # Wait for trailing garbage flush # Toss it out ifnstr wakemodem 0 # No abort indicator log Hangup on abort # # Get modem's attention via Hayes 'escape' protocol. # wakemodem: sleep 4000 send +++ sleep 4000 send \r timeout reset 2000 expect reset OK # # We're (probably) in command mode. Use ATZ (reset) to hang up # as some modems don't behave well with ATH0 command. # reset: send ATZ\r timeout silent 5000 expect done OK # # Finished, modem is back in initial state. # done: success # # No response to escape protocol. Log the error and force DTR low # in an attempt to get control of the modem. Then send ATZ just to # make sure. # silent: logerr Hangup: no response from modem hangup # Force DTR low as last gasp send ATZ\r sleep 5000 failed uucp-1.07/contrib/Login.LAT0000664000076400007640000000612607665321760011203 #!xchat # @(#) login.LAT V1.2 Tue Sep 1 13:25:28 1992 # # xchat script for logging into a VMS system through a LAT # terminal server port. If no VMS password parameter supplied, # skips password phase of VMS login. If LAT-password supplied, # will log into LAT server using that password. NOTE: does not # handle the situation where a LAT password is needed but no # VMS password is needed. # # Usage: # xchat login.LAT sysname username [ password [ LAT-password ] ] # # History: # rbd Fri Aug 14 13:37:06 1992 # Changes for Lantronix ETS-16. It says "type help at the Local> # prompt..." then it gives the prompt for real! Prompt may need # to be something other than "Local>". We match the real Local> # prompt by matching the leading newline! # # rbd Tue Sep 1 13:04:32 1992 # Remove absolute path name from log file. Now defaults per config. # start: dbgfile Login.Log dbgset 15 sleep 2000 # Wait 2 seconds flush # Flush typeahead ifnstr svrstart 3 # Skip server password if not given # # Starting point if server password supplied. Handle situation # where the server line may have been left waiting for username # or at local> prompt. # getsvrpwp: zero l0: count # Get server's password prompt ifgtr deadmux 5 # die if 5 cr's don't do it send \r timeout l0 1000 # Wait and try again expect dosvrpw ssword> expect svrlogin ername> expect connect \nLocal> # # Send server's password. Fail if don't get Username # or Local> prompt. # dosvrpw: zero l2: sendstr 3 send \r timeout badsvrpw 5000 # Die if invalid expect svrlogin ername> expect connect \nLocal> # # Starting point if NO server password supplied. Handle situation # where the server line may have been left at local> prompt. # svrstart: zero l1: count # Get username> or local> prompt ifgtr deadmux 5 # Die if 5 cr's don't do it send \r timeout l1 1000 # Wait and try again expect svrlogin ername> expect connect \nLocal> # # Server asked for a username. Just give 'uucp'. # svrlogin: send uucp\r timeout deadmux 2000 expect connect \nLocal> # # At this point, we have the Local> prompt. Send the connect # command for the specified LAT host service name, and wait for # VMS "Username:" prompt. Die after 10 seconds. # connect: send c\s sendstr 0 send \r timeout nologin 10000 expect gotlogin ername: # # Got VMS "Username:" prompt. Send the username. If a password # was given, wait for the "Password:" prompt. Die after 10 seconds. # if no password was given, we're done! # gotlogin: sendstr 1 send \r ifnstr done 2 timeout nopasswd 10000 expect gotpasswd ssword: # # Got VMS "Password:" prompt. Send the password and we're done! # gotpasswd: sendstr 2 send \r # # Success! # done: success # # ERROR HANDLERS # # # LAT server appears dead. Fail. # deadmux: logerr No response from LAT server failed # # The server password was bad. Fail. # badsvrpw: logerr Invalid LAT server password failed # # VMS system appears to be dead. Fail. # nologin: logerr No VMS Username: prompt failed # # Failed to get "Password:" prompt. Fail. # nopasswd: logerr No VMS Password: prompt. Invalid password? failed uucp-1.07/contrib/Login.PortSel0000664000076400007640000000573407665321760012157 #!xchat # @(#) Login.PortSelUnix V1.0 Tue Sep 1 14:57:05 1992 (Bob Denny) # # NOTE: Untested with xchat V1.1. Taken from DECUS UUCP. # # From: "Kent C. Brodie" # uucp: {uunet!marque,csd4.milw.wisc.edu}!moocow!brodie # special script for "uwmcsd4", have to go through a port selector (and then # log in via standard Unix procedures). # # Also included is the ability to wait in the port selector queue. # Be forwarned that the debug log can get pretty big depending on # how many times you "wait" in the queue. # (C) 1989 Kent C. Brodie - Medical College of Wisconsin # P0 is systemname , P1 is username, P2 is password. zero # send a CR to get the selector's attention. Sleep a little bit # due to large login text of selector. It sends "Which System?" # when it's ready. getprtslct: count ifgtr noprtslct 6 break send \r sleep 2000 flush expect prtslctok ystem? timeout getprtslct 15000 noprtslct: logerr Sent cr, no "Which System?" from port selector failed # Send the system name. We either get "OK" (connected), or we # get "No ports available, would you like to wait?" (wait in queue) prtslctok: zero sendstr 0 send \r expect connected OK expect prtslctwait wait? timeout noconnect 10000 # Usually we get "nn Your place in queue" messages. JUST in case we # get a free port right away, check for 'Are you ready?' as well. prtslctwait: zero send Y\r expect prtslctque queue expect prtslctrdy ready? timeout prtwaitbad 70000 prtwaitbad: logerr Sent "Y" to wait in queue, did not get valid response. failed # Here's where we wait in the queue. The port selector sends us a status # message about once a minute. We either get "nn Your place in queue" # or we get "System Available. Are you Ready?". # If something goes wrong, we time out waiting for either response. # The reason we don't sleep for 40-50 seconds is because as SOON as the # port is ready, it informs us. If we wait too long, it drops us. # This setup is laid out for a maximum of 20 "tries" which is ABOUT # 20 minutes. Note: This constant retrying can make log files # kind of big.... prtslctque: count ifgtr prtslcttry 20 expect prtslctque queue expect prtslctrdy ready? timeout noportwait 70000 prtslcttry: logerr Too many (20) wait/retries -- queue too busy. failed prtslctrdy: send Y\r expect connected OK timeout noconnect 20000 noportwait: logerr Timed out awaiting place in port queue failed noconnect: logerr Sent system name, no "OK" from selector failed # standard Unix login stuff. Send cr, expect "ogin:", if no, send a break # (which tells Unix to try the next bit rate) and try again. connected: send \r zero goto waitlogin sendbreak: count ifgtr nolgi 6 flush break waitlogin: expect gotlogin ogin: timeout sendbreak 5000 nolgi: logerr No login: prompt failed gotlogin: sendstr 1 send \r expect gotword word: timeout nopwd 10000 nopwd: logerr No password: prompt failed gotword: sendstr 2 send \r success uucp-1.07/contrib/Login.VMS0000664000076400007640000000411607665321760011225 #!xchat # @(#) Login.VMS V1.1 Tue Sep 1 13:24:54 1992 (Bob Denny) # # # xchat script for logging into a VMS system. If no VMS password # parameter supplied, skips password phase of VMS login. If syspass # parameter given, will go through steps needed to log into a VMS # system where a "system password" was set on the port. # # Cannot handle situation where system password is required but # no password needed. # # # Usage: # xchat Login.VMS username [ password [ syspass ] ] # # Uncomment the lines starting with "###" to get debug logging. # start: ### dbgfile Login.Log ### dbgset 15 sleep 2000 # Wait 2 seconds zero flush # Flush typeahead ifnstr login 2 # Skip sys passwd if not given # # Need system password. Send to get bell. # Try 5 times at 2 sec. intervals. Skip to do # username if we see "Username:". # syspass: count ifgtr nobell 5 # Fail after 5 tries send \r timeout syspass 2000 # Wait 2 sec. and try again expect gotbell \007 expect gotlogin Username: # # Got the bell. Send the system password. Repeat 5 times # at 2 sec. intervals. Fail if we don't get Username: # gotbell: zero sleep 2000 l1: count ifgtr nologin 5 # Fail after 5 tries sendstr 2 send \r timeout l1 2000 # Wait 2 sec. and try again expect gotlogin Username: # # Start here if no system password supplied. # Send until we get Username: Try 5 times at 2 sec. intervals. # login: count ifgtr nologin 5 # Fail after 5 tries send \r timeout login 2000 # Wait 2 sec. and try again expect gotlogin Username: # # Got VMS "Username:" prompt. Send the username. If a password # was given, wait for the "Password:" prompt. Die after 10 seconds. # if no password was given, we're done! # gotlogin: sendstr 0 send \r ifnstr done 1 timeout nopasswd 10000 expect gotpasswd Password: # # Got VMS "Password:" prompt. Send the password and we're done! # gotpasswd: sendstr 1 send \r # # Success! # done: success # # ERROR HANDLERS # nobell: logerr No VMS system password prompt (bell) failed nologin: logerr No VMS Username: prompt failed nopasswd: logerr No VMS Password: prompt. failed uucp-1.07/contrib/Makefile.uurt0000664000076400007640000000145607665321760012230 # $Id: Makefile,v 1.4 1994/04/14 17:47:52 kdburg Exp $ # Makefile for uurate 1.10 # # Prefix directory for installation directories. prefix = /usr/local # Directory where the needed .h files are installed (uucp.h ...). uucpsrcs = ../ # Where uurate is installed BIN=$(prefix)/bin # Where uurate's man is installed MAN=$(prefix)/man/man1 # The directory to look in for Taylor style configuration files newconfigdir = $(prefix)/conf/uucp # Flags to use when compiling uurate CC=gcc -O2 CFLAGS=-I.. -Wall LDFLAGS=-s -lm SHELL=/bin/sh PROGS=uurate #----------- MORECFLAGS= -I. -I$(uucpsrcs) -DNEWCONFIGLIB=\"$(newconfigdir)\" all: $(PROGS) uurate: uurate.c $(CC) $(CFLAGS) $(MORECFLAGS) $@.c -o $@ $(LDFLAGS) install: $(PROGS) cp $(PROGS) $(BIN) cp uurate.man $(MAN)/uurate.1 clean: rm -f $(PROGS) core uucp-1.07/contrib/Makefile.xchat0000664000076400007640000000067307665321760012340 # # Makefile for xchat 1.1 # # Bob Denny - Tue Sep 1 15:58:22 1992 # CC=cc SHELL=/bin/sh BIN=/usr/local/lib/uucp PROGS=xchat #----------- all: $(PROGS) install: $(PROGS) @for i in $(PROGS) ; do \ echo "Install $$i into $(BIN)..." ; \ cp $$i $(BIN) ; \ echo "Set ownership and protection..." ; \ /bin/chmod 0555 $(BIN)/$$i ; \ /bin/chown bin $(BIN)/$$i ; \ /bin/chgrp bin $(BIN)/$$i ; \ done clean: rm -f $(PROGS) core uucp-1.07/contrib/README0000664000076400007640000000634707665321760010456 This is the README file for the Taylor UUCP contrib directory. This directory contains contributed shell scripts and programs that you may find useful. Not actually included here, but nonetheless useful, is the TUA program distributed by Lele Gaifax . It can do various sorts of analysis of any type of UUCP log file. It should be available from most FTP sites. xchat.c, xchat.man, README-XCHAT, xc-conf.h-dist, Makefile.xchat: A program by Bob Denny that may be invoked by the ``chat-program'' command for any of the various types of chat scripts. It is driven by scripts which are written in its own little language. It is a powerful program that can add a lot of flexibility to your chat scripts. Dial.Hayes, Hangup.Hayes, Ringback.Hayes, Login.LAT, Login.PortSel, Login.VMS: Sample scripts for xchat. uucomp.shar A set of programs which automatically compresses outgoing data in the spool directory. The remote system must cooperate when using this. It can cut down on phone usage when applicable. Contributed by Ed Carp. uurate.c, uurate.man, README-UURATE, Makefile.uurt: A nifty little program by Bob Denny which analyzes the Log and Stats file and prints various sorts of reports. This version was tweaked by Stephan Niemz and Klaus Dahlenburg. uutraf: Another program to produce neat reports from your log files, this one a perl script by Johan Vromans. savelog.sh, savelog.man: A handy shell script to rename a log file and cycle old versions through a set of names, throwing away the oldest one. It will also optionally compress the old log files. I believe that this is originally from smail. It was written by Ronald S. Karr and Landon Curt Noll, and was given to me by Bob Denny. uureroute.perl: A perl script to reroute all mail queued up for one host to another. Written by Bill Campbell and contributed by Francois Pinard. stats.sh: A gawk script by Zacharias Beckman which reads the Stats file and prints the last 80 lines as a nicely formatted table. uuq.sh: A uuq workalike shell script by Zacharias Beckman. uupoll.shar: uupoll and autopoll programs contributed by Klaus Dahlenburg. uupoll can be used to automatically poll all systems, or a list of systems; autopoll will poll and then retry failed calls. uudemon.shar: An implementation of the HDB uudemon.poll script by Donald Burr. uuxconv: A program by Richard E. Nickle to help convert SPOOLDIR_HDB spool directories to SPOOLDIR_TAYLOR spool directories (note that it is not necessary to convert your spool directories at all; the SPOOLDIR_TAYLOR approach may be slightly more efficient). dialHDB.c: A program by Daniel Hagerty which permits using HDB dialer programs as chat programs. amiga.c: A wrapper program to run uucico from a cron table under Amiga SVR4 (apparently a wrapper is required). This was contributed by Lawrence E. Rosenman. tstout.c: A program to remove a user from utmp and wtmp, essentially logging them out. I put this together from BSD code. I need it to use tstuu with the system UUCP on Ultrix 4.0, for reasons that escape me. Most people will have little use for this. uucp-1.07/contrib/README-UURATE0000664000076400007640000000132707665321760011452 uurate V1.10 - Gather and display Taylor UUCP traffic statistics Bob Denny (denny@alisa.com) - Thu Sep 3 19:47:41 1992 (V1.2.1) Klaus Dahlenburg (kdburg@incoahe.hanse.de) - Tue Sep 28 18:11:34 CET 1993 See the man page for documentation. Installation: ------------ (1) Copy or sym-link Makefile.uurt to Makefile. (2) Edit Makefile: set BIN where you want uurate to be installed, MAN where the man page should go to, and set CFLAGS to point to the directory containing the UUCP sources (this is .. by default). Don't forget to set newconfigdir= to point to the directory where the (Taylor-uucp)config is stored. (3) Type ``make'' to compile the program. (4) Type ``make install'' to install the program. uucp-1.07/contrib/README-XCHAT0000664000076400007640000000250607665321760011314 This is xchat V1.1 (Tue Sep 1 15:50:56 1992) Introduction: ------------ Xchat is a general-purpose dialing and login program designed for use with Taylor UUCP as a "chat-program", taking the place (or augmenting) the built-in chat scripting facility. It provides the ability to closely control timeouts, multiple simultaneous expect strings with separate actions, extended terminal control, modem command character pacing, and more. When used in conjunction with Taylor UUCP's configuration features, xchat can provide you the ability to manage the most intricate login, dial and hangup needs. The scripts are written in a shell-like (well, sort-of) style with labels, commands, and parameters, easing the task of writing procedures for complex terminal communications situations. Installation: ------------ (1) Copy xc-conf.h-dist to xc-conf.h, then edit xc-conf.h to reflect your condifuration. A description of the settings is in that file. (2) Copy Makefile.xchat to Makefile and edit it to set BIN to where you want xchat installed. (2) Do a 'make' to build xchat. (3) Do a 'make install' to install it. (4) Format and print xchat.8, and install it if you want. (5) Print out copies of the scripts in the ./scripts subdirectory. (6) Read xchat.8 and the scripts together. Author: ------ Robert B. Denny (denny@alisa.com) uucp-1.07/contrib/Ringback.Hayes0000664000076400007640000000471007665321760012301 #!xchat # @(#) modified from dial.hayes V1.1 Tue Sep 1 13:59:58 1992 (Bob Denny) # modification by prye@picu-sgh.demon.co.uk (Peter Rye) # Fri May 15 18:46:06 BST 1998 # # xchat script for using "ringback" with a vanilla Hayes modem # # Usage: # xchat Hayes.Ringback telno # # where telno is the telephone number, subject to pause and wait # character modification. # # Uncomment the first two lines after "start:" to get debugging # in file "Dial.Log" # # Flush input, zero counter, set telephone number if supplied, # else fail if no telephone number given. # start: ##dbgfile Dial.Log ##dbgset 15 zero flush ifnstr notelno 0 telno 0 goto initmodem # # Missing telephone number. # notelno: logerr No telephone number given failed # # Reset the modem to nonvolatile profile. # Allow 3 sec. for response, as some modems are slow to reset. # initmodem: count ifgtr cantinit 4 send ATZ\r timeout initmodem 3000 expect setupmodem OK # # No response from modem # cantinit: logerr Can't wake modem failed # # Send the stuff needed to initialize the modem to the modes # needed for the particular modem flavor. The string below # is provided as a vanilla example. Allow 2 sec. for the # modem to respond to this command. # setupmodem: sleep 1000 send ATZ\r timeout setupfail 2000 expect setupfail ERROR expect dialnumber OK # # Modem barfed or died on setup command. # setupfail: logerr Error in modem setup string failed # # Dial the supplied number. Handle the various errors that # can come back from the modem by logging the error. # dialnumber: sleep 1000 send ATDT dial send \r flush timeout hangup 4000 expect errconnect CONNECT expect busy BUSY expect nocarrier NO CARRIER expect noanswer NO ANSWER expect nodialtone NO DIALTONE hangup: hangup sleep 4000 send +++ sleep 4000 send \r sleep 1000 send ATZ\r timeout resetup 2000 expect resetup OK resetup: sleep 8000 send ATZ\r expect redial OK redial: sleep 1000 send ATDT dial send \r flush timeout timeout 30000 expect connected CONNECT expect busy BUSY expect nocarrier NO CARRIER expect noanswer NO ANSWER expect nodialtone NO DIALTONE # # Success! # connected: success # # Handle modem dial failures # timeout: logerr Modem or carrier timeout. failed errconnect: logerr Connected on first call. failed busy: logerr BUSY failed nocarrier: logerr NO CARRIER failed noanswer: logerr NO ANSWER failed nodialtone: logerr NO DIALTONE failed # # end # uucp-1.07/contrib/amiga.c0000664000076400007640000000376707665321760011023 /* Wrapper code for Taylor UUCP on Amiga Unix (SVR4) for cron invoked UUCP */ /* uucico. */ /* The problem: Cron and rmail (as forked by uuxqt, which may in turn invoke uucico) are not "licensed" processes. Any process that grabs a controlling terminal needs to be licensed. Taylor UUCP needs controlling terminals. Taylor UUCP does relinquish the controlling terminal before fork(), so the "UUCP" license is appropriate. This simple program does the "right" thing, but *MUST* be SETUID ROOT. To use this program, you must move 'uucico' to 'uucico.real' (or change the *name = below), compile this program, move it to where uucico was originally, and make it SETUID ROOT. This program is intended to be used as a wapper for Taylor UUCP's uucico so that the annoying 'unlicensed user attempted to fork' messages are eliminated. */ /* Written by: Lawrence E. Rosenman Modified by: Donald Phillips */ #include #include #include #include #include #include int main(int argc,char *argv[],char *envp) { struct passwd *pw; char *name = {"/usr/lib/uucp/uucico.real"}; if (sysm68k(_m68k_LIMUSER,EUA_GET_LIC) == 0 ) { /* are we unlicensed? */ if (sysm68k(_m68k_LIMUSER,EUA_UUCP) == -1) { /* yes, get a "uucp" license */ fprintf(stderr,"sysm68k failed, errno=%d\n",errno); /* we didn't grab it? */ exit(errno); } pw = getpwnam("uucp"); /* get the Password Entry for uucp */ if (pw == NULL) { fprintf(stderr,"User ID \"uucp\" doesn't exist.\n"); exit(1); } setgid(pw->pw_gid); /* set gid to uucp */ setuid(pw->pw_uid); /* set uid to uucp */ } argv[0]=name; /* have PS not lie... */ execv(name, argv); /* go to the real program */ exit(errno); } uucp-1.07/contrib/dialHDB.c0000664000076400007640000001201307665321760011154 /* # File: dialHDB.c # Author: Daniel Hagerty , hag@eddie.mit.edu # Copyright (C) 1993 # Date: Fri Nov 26 19:22:31 1993 # Description: Program for using HDB dialers for dialing modems, exiting with 0 on success, else failure. # Version: 1.0 # Revision History: ###### ### 11/26/93 Hag - File creation ###### ### 1/5/94 Hag - Finally got around to finishing this damn thing. ###### */ /* Basic theory behind this program- dialHDB forks into two processes, a monitor parent, and a child that does the exec of the dialer. Child pretty much just execs the dialer program, unless there's an exec problem, in which case the child sends the parent a SIGUSR1 to indicate failed execution. */ #include #include #include #include #include #define kUsage "Usage:\n\t%s dialerPath device number speed\n\ %s dialer -h device speed\n" #define kExitErrFlag 0x80 /* & in with exit code to determine error */ #define kErrorMask 0x0f /* Mask to determine error code */ /* Error code defines as lifted from an HDB dialer */ #define RCE_NULL 0 /* general purpose or unknown error code */ #define RCE_INUSE 1 /* line in use */ #define RCE_SIG 2 /* signal aborted dialer */ #define RCE_ARGS 3 /* invalid arguments */ #define RCE_PHNO 4 /* invalid phone number */ #define RCE_SPEED 5 /* invalid baud rate -or- bad connect baud */ #define RCE_OPEN 6 /* can't open line */ #define RCE_IOCTL 7 /* ioctl error */ #define RCE_TIMOUT 8 /* timeout */ #define RCE_NOTONE 9 /* no dial tone */ #define RCE_BUSY 13 /* phone is busy */ #define RCE_NOCARR 14 /* no carrier */ #define RCE_ANSWER 15 /* no answer */ /* Structure definition to map error codes to strings */ typedef struct { int errNum; char *errString; } errTable; const errTable errors[]= { { RCE_NULL, "Unknown Error" }, { RCE_INUSE, "Line is being used" }, { RCE_SIG, "Recieved fatal signal" }, { RCE_ARGS, "Bad arguments" }, { RCE_PHNO, "Invalid phone number" }, { RCE_SPEED, "Invalid baud rate or bad connection" }, { RCE_OPEN, "Unable to open line" }, { RCE_IOCTL, "ioctl error" }, { RCE_TIMOUT, "Timed out" }, { RCE_NOTONE, "No dialtone" }, { RCE_BUSY, "Phone number is busy" }, { RCE_NOCARR, "No carrier" }, { RCE_ANSWER, "No answer" }, { 0,NULL} }; /* Function Prototypes */ int figureStat(int stat); char *findInTable(int error); void badExec(void); char *dialerName; /* basename of our dialer program */ char *dialerPath; /* full path of dialer program */ main(int argc,char *argv[]) { int parent; /* pid of parent process */ int child; /* pid of child process */ int stat; /* exit status of child process */ char *temp; /* used to get basename of dialer */ if(argc!=5) { fprintf(stderr,kUsage,argv[0],argv[0]); exit(1); } dialerPath=argv[1]; dialerName= (temp=strrchr(argv[1],'/'))!=NULL ? temp+1 : argv[1]; parent=getpid(); signal(SIGUSR1,badExec); /* set up for possible failed exec */ if((child=fork())<0) { perror("fork"); exit(2); } if(child>0) /* We're parent, wait for child to exit */ { /* Set up to ignore signals so we can report them on stderror */ signal(SIGHUP,SIG_IGN); signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN); wait(&stat); /* wait for child to exit */ exit(figureStat(stat)); /* figure out our exit code and die */ } else /* child process */ { close(0); /* close of modem file desc, since HDB */ close(1); /* doesn't use them */ dup2(2,1); /* and remap stdout to stderr, just in case */ if(execvp(argv[1],argv+1)<0) /* exec program with argv shifted by 1 */ { /* if exec fails, send SIGUSR1 to parent */ kill(parent,SIGUSR1); exit(0); } } exit(0); } /* Figure out whether or not dialer ran succesfully, and return with 0 if it worked, otherwise error */ int figureStat(int stat) { int exit; int errFlag; int error; if(WIFSIGNALED(stat)) /* determine if exit was from signal or what */ { fprintf(stderr,"Error: Dialer %s recieved signal %d.\n",dialerName, WTERMSIG(stat)); return(1); } if(WIFSTOPPED(stat)) { fprintf(stderr,"Error: Dialer %s recieved signal %d.\n",dialerName, WSTOPSIG(stat)); return(1); } exit=WEXITSTATUS(stat); errFlag=exit&kExitErrFlag; /* Is the error flag set? */ if(errFlag) { char *errString; error=exit&kErrorMask; errString=findInTable(error); /* find it's string, print it on stderr */ fprintf(stderr,"Error: %s - %s.\n",dialerName,errString); /* and return */ return(1); } return(0); } /* Support routine, look up exit code in error table, and return pointer to proper string */ char *findInTable(int error) { int i=0; for(i=0;errors[i].errString!=NULL;i++) { if(errors[i].errNum==error) return(errors[i].errString); } /* Still here, return the top entry, for unknown error */ return(errors[0].errString); } /* Called by signal if we recieve SIGUSR 1 */ void badExec(void) { fprintf(stderr,"Error: %s - Execution problem.\n",dialerPath); exit(1); } uucp-1.07/contrib/savelog.man0000664000076400007640000000530007665321760011717 .\" @(#)man/man8/savelog.an 1.2 24 Oct 1990 05:18:46 .de pP .if n .sp 1 .if t .sp .4 .. .de tP .pP .ta \\n(pDu .ti -\\n(pDu .. .TH SAVELOG X_MAN8_EXT_X "31 January 1988" "Local" .SH NAME savelog \- cycle and truncate log files .SH SYNOPSIS .na .B X_UTIL_BIN_DIR_X/savelog [ .B \-m .I mode ] [ .B \-u .I user ] [ .B \-g .I group ] [ .B \-c .I cycle ] [ .B \-t ] [ .B \-l ] .I logfile .br .ad .SH DESCRIPTION The .I savelog command renames and optionally compresses a log file and cycles it through a set of names based on the original log file, removing the last name in the cycle. .SH OPTIONS The .I savelog command accepts the following options: .TP \fB\-m\fP \fImode\fP Change the permissions mode for renamed log files to .IR mode . By default the mode is unchanged. .TP \fB\-u\fP \fIuser\fP Change the owner for renamed log files to .IR user . By default the owner is unchanged. .TP \fB\-g\fP \fIgroup\fP Change the group for renamed log files to .IR group . By default the group is unchanged. .TP \fB\-c\fP \fIcycle\fP Save .I cycle versions of the logfile, where .I cycle is a decimal number. The default value is 7. .TP .B \-l Do not compress log files. By default, a compression program is used, if one is available. .TP .B \-t Ensure that a new logfile exists when the savelog operation is complete. Use of .BR \-m , .BR \-u or .BR \-g imply this, ensuring that the logfile will have the designated mode. .SH "OPERATION" The given logfile is cycled through files named: .RS OLD/\fIfile\fP.\fInumber\fP .RE where .I file is the basename for the logfile and where .I number ranges from 0 to one less then the .I cycle count specified for the command. The .I OLD dirctory is created, as necessary, and is under the same directory as the logfile itself. .PP This cycle operation is accomplished by renaming the file numbered .IR cycle -2 to a file numbered .IR cycle -1 and so on until the file numbered 0 is renamed to the file numbered 1. If compression is being used, the first cycle file is compressed after being renamed to cycle 1. After the cycle files are moved through the various names, the filefile itself is moved to the cycle 0 file. This cycle normally occurs once every time .I savelog is executed. If the log file does not exist, savelog ignores it and does not cycle the OLD files. .PP If compression is being used, then compressed log files will have an additional suffix appropriate for the compression program that is used. .SH "SEE ALSO" .IR smail (X_MAN5_EXT_X) and .IR smail (X_MAN8_EXT_X). .SH COPYRIGHT Copyright(C)1987, 1988 Ronald S. Karr and Landon Curt Noll .br See a file COPYING, distributed with the source code, or type .I "smail \-bc" for distribution rights and restrictions associated with this software. uucp-1.07/contrib/savelog.sh0000775000076400007640000001434007665321760011565 #! /bin/sh # @(#)util/savelog.sh 1.4 26 Oct 1991 22:49:39 # # savelog - save a log file # # Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll # # See the file COPYING, distributed with smail, for restriction # and warranty information. # # usage: savelog [-m mode] [-u user] [-g group] [-t] [-c cycle] [-l] file... # # -m mode - chmod log files to mode # -u user - chown log files to user # -g group - chgrp log files to group # -c cycle - save cycle versions of the logfile (default: 7) # -t - touch file # -l - don't compress any log files (default: compress) # file - log file names # # The savelog command saves and optionally compresses old copies of files # into an 'dir'/OLD sub-directory. The 'dir' directory is determined from # the directory of each 'file'. # # Older version of 'file' are named: # # OLD/'file'. # # where is the version number, 0 being the newest. By default, # version numbers > 0 are compressed (unless -l prevents it). The # version number 0 is never compressed on the off chance that a process # still has 'file' opened for I/O. # # If the 'file' does not exist or if it is zero length, no further processing # is performed. However if -t was also given, it will be created. # # For files that do exist and have lengths greater than zero, the following # actions are performed. # # 1) Version numered files are cycled. That is version 6 is moved to # version 7, version is moved to becomes version 6, ... and finally # version 0 is moved to version 1. Both compressed names and # uncompressed names are cycled, regardless of -t. Missing version # files are ignored. # # 2) The new OLD/file.1 is compressed and is changed subject to # the -m, -u and -g flags. This step is skipped if the -t flag # was given. # # 3) The main file is moved to OLD/file.0. # # 4) If the -m, -u, -g or -t flags are given, then file is created # (as an empty file) subject to the given flags. # # 5) The new OLD/file.0 is chanegd subject to the -m, -u and -g flags. # # Note: If the OLD sub-directory does not exist, it will be created # with mode 0755. # # Note: If no -m, -u or -g flag is given, then the primary log file is # not created. # # Note: Since the version numbers start with 0, version number # is never formed. The count must be at least 2. # # Bugs: If a process is still writing to the file.0 and savelog # moved it to file.1 and compresses it, data could be lost. # Smail does not have this problem in general because it # restats files often. # common location PATH="X_UTIL_PATH_X:X_SECURE_PATH_X"; export PATH COMPRESS="X_COMPRESS_X" COMP_FLAG="X_COMP_FLAG_X" DOT_Z="X_DOT_Z_X" CHOWN="X_CHOWN_X" GETOPT="X_UTIL_BIN_DIR_X/getopt" # parse args exitcode=0 # no problems to far prog=$0 mode= user= group= touch= count=7 set -- `$GETOPT m:u:g:c:lt $*` if [ $# -eq 0 -o $? -ne 0 ]; then echo "usage: $prog [-m mode][-u user][-g group][-t][-c cycle][-l] file ..." 1>&2 exit 1 fi for i in $*; do case $i in -m) mode=$2; shift 2;; -u) user=$2; shift 2;; -g) group=$2; shift 2;; -c) count=$2; shift 2;; -t) touch=1; shift;; -l) COMPRESS=""; shift;; --) shift; break;; esac done if [ "$count" -lt 2 ]; then echo "$prog: count must be at least 2" 1>&2 exit 2 fi # cycle thru filenames while [ $# -gt 0 ]; do # get the filename filename=$1 shift # catch bogus files if [ -b "$filename" -o -c "$filename" -o -d "$filename" ]; then echo "$prog: $filename is not a regular file" 1>&2 exitcode=3 continue fi # if not a file or empty, do nothing major if [ ! -s $filename ]; then # if -t was given and it does not exist, create it if [ ! -z "$touch" -a ! -f $filename ]; then touch $filename if [ "$?" -ne 0 ]; then echo "$prog: could not touch $filename" 1>&2 exitcode=4 continue fi if [ ! -z "$user" ]; then $CHOWN $user $filename fi if [ ! -z "$group" ]; then chgrp $group $filename fi if [ ! -z "$mode" ]; then chmod $mode $filename fi fi continue fi # be sure that the savedir exists and is writable savedir=`expr "$filename" : '\(.*\)/'` if [ -z "$savedir" ]; then savedir=./OLD else savedir=$savedir/OLD fi if [ ! -s $savedir ]; then mkdir $savedir if [ "$?" -ne 0 ]; then echo "$prog: could not mkdir $savedir" 1>&2 exitcode=5 continue fi chmod 0755 $savedir fi if [ ! -d $savedir ]; then echo "$prog: $savedir is not a directory" 1>&2 exitcode=6 continue fi if [ ! -w $savedir ]; then echo "$prog: directory $savedir is not writable" 1>&2 exitcode=7 continue fi # deterine our uncompressed file names newname=`expr "$filename" : '.*/\(.*\)'` if [ -z "$newname" ]; then newname=$savedir/$filename else newname=$savedir/$newname fi # cycle the old compressed log files cycle=`expr $count - 1` rm -f $newname.$cycle $newname.$cycle$DOT_Z while [ "$cycle" -gt 1 ]; do # --cycle oldcycle=$cycle cycle=`expr $cycle - 1` # cycle log if [ -f $newname.$cycle$DOT_Z ]; then mv -f $newname.$cycle$DOT_Z $newname.$oldcycle$DOT_Z fi if [ -f $newname.$cycle ]; then # file was not compressed for some reason move it anyway mv -f $newname.$cycle $newname.$oldcycle fi done # compress the old uncompressed log if needed if [ -f $newname.0 ]; then if [ -z "$COMPRESS" ]; then newfile=$newname.1 mv $newname.0 $newfile else newfile=$newname.1$DOT_Z $COMPRESS $COMP_FLAG < $newname.0 > $newfile rm -f $newname.0 fi if [ ! -z "$user" ]; then $CHOWN $user $newfile fi if [ ! -z "$group" ]; then chgrp $group $newfile fi if [ ! -z "$mode" ]; then chmod $mode $newfile fi fi # move the file into the file.0 holding place mv -f $filename $newname.0 # replace file if needed if [ ! -z "$touch" -o ! -z "$user" -o \ ! -z "$group" -o ! -z "$mode" ]; then touch $filename fi if [ ! -z "$user" ]; then $CHOWN $user $filename fi if [ ! -z "$group" ]; then chgrp $group $filename fi if [ ! -z "$mode" ]; then chmod $mode $filename fi # fix the permissions on the holding place file.0 file if [ ! -z "$user" ]; then $CHOWN $user $newname.0 fi if [ ! -z "$group" ]; then chgrp $group $newname.0 fi if [ ! -z "$mode" ]; then chmod $mode $newname.0 fi done exit $exitcode uucp-1.07/contrib/stats.sh0000775000076400007640000000164107665321760011263 #!/bin/sh # # uuspeed - a script to parse a Taylor UUCP Stats file into pretty results. # Zacharias J. Beckman. # # Modified 20/12/96 by Mark Horsburgh (markh@kcbbs.gen.nz) to calculate # correct average transmission rate and to give bytes/s rather than baud # (since baud is not really the correct name for it and bits/s actually # depends on the number of start and stop bits etc) grep bytes /usr/spool/uucp/Stats | grep -v 'bytes 0.00 secs' | grep -v 'failed after' | tail -80 | \ gawk ' BEGIN { printf(" UUCP transmission history:\n"); format=" %8d bytes %8s(%8s) in %7.2f sec = %5.0f bytes/s, %5.1fK / min\n"; bytes = 0.0 seconds = 0.0 } { if ($6 > 100) { printf (format, $6, $5, $2, $9, $6/$9, ($6/$9*60)/1024); bytes += $6 seconds += $9 } } END { printf (" average speed %5.0f bytes/s, %4.1fK/min\n", bytes/seconds,bytes/seconds*60/1024); } ' uucp-1.07/contrib/tstout.c0000664000076400007640000001107407665321760011275 /* tstout.c Put together by Ian Lance Taylor This program is used to logout a program run by the tstuu program. I needed this because on Ultrix 4.0 I can't get the uucp program to run without invoking it via /bin/login and having it start up as a shell. If I don't do it this way, it gets a SIGSEGV trap for some reason. Most systems probably don't need to do things this way. It will only work on BSD systems anyhow, I suspect. The code for this comes from "UNIX Network Programming" by W. Richard Stevens, Prentice-Hall 1990. Most of it is from 4.3BSD, as noted in the comments. This program must run suid to root. */ #include #include #include #include #include #include #include static int logout P((const char *zdev)); static void logwtmp P((const char *zdev, const char *zname, const char *zhost)); int main (argc, argv) int argc; char **argv; { char *z; if (argc != 2 || strncmp (argv[1], "/dev/", sizeof "/dev/" - 1) != 0) { fprintf (stderr, "Usage: tstout device\n"); exit (EXIT_FAILURE); } z = argv[1] + 5; if (logout (z)) logwtmp (z, "", ""); chmod (argv[1], 0666); chown (argv[1], 0, 0); *z = 'p'; chmod (argv[1], 0666); chown (argv[1], 0, 0); exit (EXIT_SUCCESS); } /* * Copyright (c) 1988 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)logout.c 5.2 (Berkeley) 2/17/89"; #endif /* LIBC_SCCS and not lint */ #define UTMPFILE "/etc/utmp" /* 0 on failure, 1 on success */ static int logout(line) register const char *line; { register FILE *fp; struct utmp ut; int rval; time_t time(); if (!(fp = fopen(UTMPFILE, "r+"))) return(0); rval = 0; while (fread((char *)&ut, sizeof(struct utmp), 1, fp) == 1) { if (!ut.ut_name[0] || strncmp(ut.ut_line, line, sizeof(ut.ut_line))) continue; bzero(ut.ut_name, sizeof(ut.ut_name)); bzero(ut.ut_host, sizeof(ut.ut_host)); (void)time((time_t *)&ut.ut_time); (void)fseek(fp, (long)-sizeof(struct utmp), L_INCR); (void)fwrite((char *)&ut, sizeof(struct utmp), 1, fp); (void)fseek(fp, (long)0, L_INCR); rval = 1; } (void)fclose(fp); return(rval); } /* * Copyright (c) 1988 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)logwtmp.c 5.2 (Berkeley) 9/20/88"; #endif /* LIBC_SCCS and not lint */ #define WTMPFILE "/usr/adm/wtmp" static void logwtmp(line, name, host) const char *line, *name, *host; { struct utmp ut; struct stat buf; int fd; time_t time(); char *strncpy(); if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0) return; if (!fstat(fd, &buf)) { (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); (void)time((time_t *)&ut.ut_time); if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp)) (void)ftruncate(fd, buf.st_size); } (void)close(fd); } uucp-1.07/contrib/uuclean0000664000076400007640000000213407665321760011143 # This is a sample uuclean shell script # Copyright (C) 1992 Ian Lance Taylor # Do whatever you like with this script. # # Set some variables bindir=/usr/local/bin spooldir=/usr/spool/uucp # # Warn about all mail over two days old $(bindir)/uustat -c rmail -o 48 -N -Q -W"Unable to deliver; will try up to one week" # Return all mail over a week old $(bindir)/uustat -c rmail -o 168 -K -M -N -Q -W"Could not be delivered for over one week" # Throw away other requests over a week old $(bindir)/uustat -o 168 -K -M -N -Q -W"Over one week old" # Warn about any non rmail executions over three days old $(bindir)/uustat -o 72 -C rmail -M -N -Q -W"Unable to execute for three days" # # Now delete any old spool files find $(spooldir) -ctime +8 -name '[CDX].*' -print -exec rm -f \{\} \; # Delete any old temporary files find $(spooldir) -atime +1 -ctime +1 -name 'TM.*' -print -exec rm -f \{\} \; # Delete any old preserved files find $(spooldir)/.Preserve -atime +14 -ctime +14 -print -exec rm -f \{\} \; # Delete any old failed execution files find $(spooldir)/.Failed -atime +14 -ctime +14 -print -exec rm -f \{\} \; uucp-1.07/contrib/uucomp.shar0000664000076400007640000004135507665321760011763 #! /bin/sh # # Created by shar, version 0.5 - 04/10/91 # # This is a shell archive, meaning: # 1. Remove everything about the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh to create: # # length name # ------ ------------------------------------- # 128 uucomp-1.1/Compress # 264 uucomp-1.1/Copyright # 410 uucomp-1.1/INTERNALS # 1069 uucomp-1.1/Makefile # 3528 uucomp-1.1/README # 632 uucomp-1.1/crmail.c # 632 uucomp-1.1/crnews.c # 108 uucomp-1.1/tags # 3506 uucomp-1.1/uucomp.c # 383 uucomp-1.1/uucomp.h # if test ! -d uucomp-1.1 ; then mkdir uucomp-1.1 fi # # Archive number 1 # This archive created Tue Sep 28 20:21:14 1993 # echo "shar: extracting uucomp-1.1/Compress - (128 characters)" if test -f 'uucomp-1.1/Compress' ; then echo shar: will not over-write existing file uucomp-1.1/Compress else sed 's/^X//' << \SHAR_EOF > 'uucomp-1.1/Compress' Xfor i in $* Xdo X if [ -d /usr/spool/uucp/$i ] X then X# echo Looking at $i X cd /usr/spool/uucp/$i X /usr/bin/uucomp C.* X fi Xdone SHAR_EOF if test 128 -ne "`wc -c < 'uucomp-1.1/Compress'`" ; then echo "shar: ***** error transmitting file uucomp-1.1/Compress (should have been 128 characters, but was "`wc -c < 'uucomp-1.1/Compress'`" characters) *****" fi fi touch 0715110393 uucomp-1.1/Compress chmod 0700 uucomp-1.1/Compress echo "shar: extracting uucomp-1.1/Copyright - (264 characters)" if test -f 'uucomp-1.1/Copyright' ; then echo shar: will not over-write existing file uucomp-1.1/Copyright else sed 's/^X//' << \SHAR_EOF > 'uucomp-1.1/Copyright' X X/* X * X * Copyright 1993 by Ed Carp (erc@apple.com) All rights reserved. X * X * Permission is hereby granted for any non-commercial use of this X * program, as long as this copyright notice remains intact. Commercial X * users may contact me - I'm easy. X * X */ X SHAR_EOF if test 264 -ne "`wc -c < 'uucomp-1.1/Copyright'`" ; then echo "shar: ***** error transmitting file uucomp-1.1/Copyright (should have been 264 characters, but was "`wc -c < 'uucomp-1.1/Copyright'`" characters) *****" fi fi touch 0715174993 uucomp-1.1/Copyright chmod 0600 uucomp-1.1/Copyright echo "shar: extracting uucomp-1.1/INTERNALS - (410 characters)" if test -f 'uucomp-1.1/INTERNALS' ; then echo shar: will not over-write existing file uucomp-1.1/INTERNALS else sed 's/^X//' << \SHAR_EOF > 'uucomp-1.1/INTERNALS' XThis is the basic workflow for uucomp: X Xfor (every argv) Xdo X if not "C." file skip X if open fail, skip X read 1 line from C. file X grab second and 10th field (second is data file name, X 10th is command name) X if open fail on second field, skip X if 10th field isn't "rmail" or "rnews", skip X execute "gzip -9" on second field X change "rmail" and "rnews" to "crmail" and "crnews", respectively X in C. file Xdone SHAR_EOF if test 410 -ne "`wc -c < 'uucomp-1.1/INTERNALS'`" ; then echo "shar: ***** error transmitting file uucomp-1.1/INTERNALS (should have been 410 characters, but was "`wc -c < 'uucomp-1.1/INTERNALS'`" characters) *****" fi fi touch 0715174693 uucomp-1.1/INTERNALS chmod 0600 uucomp-1.1/INTERNALS echo "shar: extracting uucomp-1.1/Makefile - (1069 characters)" if test -f 'uucomp-1.1/Makefile' ; then echo shar: will not over-write existing file uucomp-1.1/Makefile else sed 's/^X//' << \SHAR_EOF > 'uucomp-1.1/Makefile' X# X# Makefile generated with genmake - version 1.1 08/22/92 X# X# genmake is Copyright 1991 by Edwin R. Carp X# X# GENMAKE -B/usr/bin -tsp [files] X# X XCC = gcc -O6 XCFLAGS = $(INCLUDE) XSOURCES = crmail.c crnews.c uucomp.c XOBJECTS = crmail.o crnews.o uucomp.o XPROGRAMS = /usr/bin/crmail /usr/bin/crnews /usr/bin/uucomp X Xall: $(PROGRAMS) tags X X/usr/bin/crmail: crmail.o X $(CC) $(CFLAGS) -o crmail crmail.o $(LDFLAGS) -O X strip crmail X chmod 755 crmail X mv crmail /usr/bin X X/usr/bin/crnews: crnews.o X $(CC) $(CFLAGS) -o crnews crnews.o $(LDFLAGS) -O X strip crnews X chmod 755 crnews X mv crnews /usr/bin X X/usr/bin/uucomp: uucomp.o X $(CC) $(CFLAGS) -o uucomp uucomp.o $(LDFLAGS) -O X strip uucomp X chmod 755 uucomp X mv uucomp /usr/bin X Xclean: X /bin/rm -f $(OBJECTS) MAKELOG eddep makedep X Xclobber: X /bin/rm -f $(OBJECTS) $(PROGRAMS) MAKELOG eddep makedep *~ *.bak *.BAK X /bin/rm -f tags X Xhidden: X echo "make all > MAKELOG 2>&1 &"|/bin/sh X Xmakefile: X genmake -B/usr/bin -tsp $(SOURCES) & X Xmakeall: X genmake -B/usr/bin -tsp *.c & X Xtags: $(SOURCES) X ctags $(SOURCES) > tags X SHAR_EOF if test 1069 -ne "`wc -c < 'uucomp-1.1/Makefile'`" ; then echo "shar: ***** error transmitting file uucomp-1.1/Makefile (should have been 1069 characters, but was "`wc -c < 'uucomp-1.1/Makefile'`" characters) *****" fi fi touch 0714235093 uucomp-1.1/Makefile chmod 0600 uucomp-1.1/Makefile echo "shar: extracting uucomp-1.1/README - (3528 characters)" if test -f 'uucomp-1.1/README' ; then echo shar: will not over-write existing file uucomp-1.1/README else sed 's/^X//' << \SHAR_EOF > 'uucomp-1.1/README' XLike most people these days, I'm looking for ways to make my computing Xenvironment more efficient. This environment consists of a 486, a 386, Xand a 386SL laptop, all of which run Taylor uucp under Linux. The 386 Xlaptop gets used a lot, since it goes wherever I go and I answer a lot Xof news and email every day. Often, I must use other people's facilities X(phone lines and such) to send out replies and post news if I'm not at home. XSince it's not fair to the client for them to pay for my zone calls back Xto my home in Fremont, I place the calls on my phone card. Unfortunately, XPacBell is very proud of the services they offer, especially in regards Xto this convenience of automatically charging calls to my house wherever I Xmay be. Considering that this can be very expensive to do, I searched for Xa way to cut my phone bill down to something I could afford to pay each Xmonth without fainting every time I saw the bill. X XThe first thing I did was to go out and plunk $195 for a 14.4KB modem. XThat helped, but C-News is very slow on my laptop, and batching articles Xis even slower, and email (of course) isn't batched at all. Even with XMNP5 compression turned on, this doesn't make for a very efficient setup, Xeven at high speeds. X XPlaying around with uucp told me that the line turnaround wasn't that much Xoverhead, nor was sending the C./X. files (the execute files) - the real Xoverhead was sending out uncompressed news and especially email, since XI subscribe to several mailing lists and digests can run quite large. X XI looked at uubatch, but the most current version I could find (1.05) was Xnot compatible with Taylor uucp (and I had no other alternative), so I Xdecided to write my own. Experiments with "gzip -9" convinced me that Xthat was the way to go, since gzip gives email and news 60 to 75 percent Xcompression, which would tend to cut one's phone bill significantly. X XYou hold in your mailbox (or news reader) the end result of that effort. XBear in mind that (1) this is a "first cut" and while it is unlikely that Xthere are very many bugs, there are certainly places where the programs could Xbe improved and tuned. Suggestions and comments are welcome! X XTo install: X X 1. Feed this to shar. X 2. Look at the Makefile. Make sure that the paths for X things are set up correctly. X 3. Look at uucomp.h and make sure that the path and X options for COMPRESS/UNCOMPRESS are set up properly. X 3. Type "make". This will make uucomp, crmail, and crnews X and will place them in /usr/bin. Move Compress into X /usr/lib/uucp. X 4. Make an entry in crontab to do X /usr/lib/uucp/Compress site1 site2 site3... X occasionally. It is suggested that this be done fairly X frequently. Alternately, you could set up a login shell X for selected sites to run uucomp every time that site X logged in. X 5. Don't forget to add /usr/bin/crmail and /usr/bin/crnews X to the list of programs allowed to be executed in your X Permissions file (if running HDB UUCP), or whatever is X appropriate for your version of uucp. X XEnjoy! Any questions or comments can be sent to erc@apple.com. X XNote: This is tuned for Taylor uucp, but would not be particularly Xdifficult to adapt to other version of uucp. See the file INTERNALS for Xdetails of how this works. X XJuly 15, 1993 XEd Carp Xerc@apple.com X------------------------------------------------------------------------------ XChanges since 1.0: X X Version Date Description X X 1.1 08/04/93 Added sanity check in C. file (check that X 'E' is first char in file, otherwise skip) SHAR_EOF if test 3528 -ne "`wc -c < 'uucomp-1.1/README'`" ; then echo "shar: ***** error transmitting file uucomp-1.1/README (should have been 3528 characters, but was "`wc -c < 'uucomp-1.1/README'`" characters) *****" fi fi touch 0804224993 uucomp-1.1/README chmod 0600 uucomp-1.1/README echo "shar: extracting uucomp-1.1/crmail.c - (632 characters)" if test -f 'uucomp-1.1/crmail.c' ; then echo shar: will not over-write existing file uucomp-1.1/crmail.c else sed 's/^X//' << \SHAR_EOF > 'uucomp-1.1/crmail.c' X/* X * crmail - get compressed mail from host, uncompress X * WARNING: This may be insecure! X */ X X/* X * X * Copyright 1993 by Ed Carp (erc@apple.com) All rights reserved. X * X * Permission is hereby granted for any non-commercial use of this X * program, as long as this copyright notice remains intact. Commercial X * users may contact me - I'm easy. X * X */ X X#include X#include "uucomp.h" Xmain (argc, argv) Xint argc; Xchar **argv; X{ X char cmd[1024]; X int i; X X sprintf (cmd, "%s|%s ", UNCOMPRESS, RMAIL); X for (i = 1; i < argc; i++) X { X strcat (cmd, argv[i]); X strcat (cmd, " "); X } X system (cmd); X exit (0); X} SHAR_EOF if test 632 -ne "`wc -c < 'uucomp-1.1/crmail.c'`" ; then echo "shar: ***** error transmitting file uucomp-1.1/crmail.c (should have been 632 characters, but was "`wc -c < 'uucomp-1.1/crmail.c'`" characters) *****" fi fi touch 0715195493 uucomp-1.1/crmail.c chmod 0600 uucomp-1.1/crmail.c echo "shar: extracting uucomp-1.1/crnews.c - (632 characters)" if test -f 'uucomp-1.1/crnews.c' ; then echo shar: will not over-write existing file uucomp-1.1/crnews.c else sed 's/^X//' << \SHAR_EOF > 'uucomp-1.1/crnews.c' X/* X * crnews - get compressed news from host, uncompress X * WARNING: This may be insecure! X */ X X/* X * X * Copyright 1993 by Ed Carp (erc@apple.com) All rights reserved. X * X * Permission is hereby granted for any non-commercial use of this X * program, as long as this copyright notice remains intact. Commercial X * users may contact me - I'm easy. X * X */ X X#include X#include "uucomp.h" Xmain (argc, argv) Xint argc; Xchar **argv; X{ X char cmd[1024]; X int i; X X sprintf (cmd, "%s|%s ", UNCOMPRESS, RNEWS); X for (i = 1; i < argc; i++) X { X strcat (cmd, argv[i]); X strcat (cmd, " "); X } X system (cmd); X exit (0); X} SHAR_EOF if test 632 -ne "`wc -c < 'uucomp-1.1/crnews.c'`" ; then echo "shar: ***** error transmitting file uucomp-1.1/crnews.c (should have been 632 characters, but was "`wc -c < 'uucomp-1.1/crnews.c'`" characters) *****" fi fi touch 0715195593 uucomp-1.1/crnews.c chmod 0600 uucomp-1.1/crnews.c echo "shar: extracting uucomp-1.1/tags - (108 characters)" if test -f 'uucomp-1.1/tags' ; then echo shar: will not over-write existing file uucomp-1.1/tags else sed 's/^X//' << \SHAR_EOF > 'uucomp-1.1/tags' Xmain crmail.c /^main (argc, argv)$/ Xmain crnews.c /^main (argc, argv)$/ Xmain uucomp.c /^main (argc, argv)$/ SHAR_EOF if test 108 -ne "`wc -c < 'uucomp-1.1/tags'`" ; then echo "shar: ***** error transmitting file uucomp-1.1/tags (should have been 108 characters, but was "`wc -c < 'uucomp-1.1/tags'`" characters) *****" fi fi touch 0804224993 uucomp-1.1/tags chmod 0600 uucomp-1.1/tags echo "shar: extracting uucomp-1.1/uucomp.c - (3506 characters)" if test -f 'uucomp-1.1/uucomp.c' ; then echo shar: will not over-write existing file uucomp-1.1/uucomp.c else sed 's/^X//' << \SHAR_EOF > 'uucomp-1.1/uucomp.c' X/* X * uucomp - compress outgoing news/mail X * X * usage: uucomp C.* X * X * This works for Taylor uucp (available from prep.ai.mit.edu:/pub/gnu/uucp*), X * but I don't promise it works for anyone else's uucp package. Basically, this X * is a quick-n-dirty hack to get compressed mail and news to a uucp site. This X * becomes important when you're on the other end of a 1200 baud packet radio X * link, where the throughput can be 60 CPS (or lower). It also tends to hide X * any nasties that people might want to say to you, since the packets *are* X * public readable. Yes, I looked at uubatch, but it was too complicated for X * me to figure out , and it didn't work with Taylor-uucp. This is almost X * too simple to work... X * X * To use this little guy, do something like this in the .bashrc or .profile X * or .cshrc of the uucp's login shell: X * X * cd /usr/spool/uucp/ X * /usr/bin/uucomp C.* X * exec /usr/lib/uucp/uucico X * X * This program was written by Ed Carp (erc@apple.com). It can be used for any X * non-commercial purpose. This software is freely redistributable. X */ X X/* X * X * Copyright 1993 by Ed Carp (erc@apple.com) All rights reserved. X * X * Permission is hereby granted for any non-commercial use of this X * program, as long as this copyright notice remains intact. Commercial X * users may contact me - I'm easy. X * X */ X X#include X#include "uucomp.h" X#undef NULL X#define NULL (0) Xmain (argc, argv) Xint argc; Xchar **argv; X{ X int i, j, sw, ctr = 0, errflag = 0, mctr = 0, nctr = 0, skipctr = 0; X char scr[64], rcmd[10], line[1024], lineout[1024]; X char *strtok (), *ptr, *lineptr, compfile[32]; X FILE *in; X X printf ("uucomp 1.1 08/04/93 ... by erc@apple.com\nscanning %d files.", argc - 1); X for (i = 1; i < argc; i++) X { X if (strncmp (argv[i], "C.", 2) != 0) X { X skipctr++; X continue; X } X if ((in = fopen (argv[i], "r+")) == (FILE *) NULL) X { X skipctr++; X continue; X } X fgets (line, 1022, in); X if(*line != 'E') X { X skipctr++; X continue; X } X line[strlen (line) - 1] = NULL; X rewind (in); X *lineout = NULL; X lineptr = line; X sw = errflag = 0; X printf ("."); X fflush (stdout); X for (j = 0;; j++) X { X ptr = strtok (lineptr, " "); X if (ptr == NULL) X break; X lineptr = NULL; X if (j == 1) X { X if (access (ptr, 4) == EOF) X { X#ifdef DEBUG X printf ("skip: file '%s' doesn't exist\n", ptr); X#endif X errflag = 1; X break; /* X * skip it if the data file isn't X * there yet X */ X } X strcpy (compfile, ptr); X } X if (j == 9) X { X if (strcmp (ptr, "rmail") != 0 && strcmp (ptr, "rnews") != 0) X { X#ifdef DEBUG X printf ("skip: '%s' wrong command\n", ptr); X#endif X errflag = 1; X break; X } X if (strcmp (ptr, "rmail") == 0) X mctr++; X if (strcmp (ptr, "rnews") == 0) X nctr++; X sw = 1; X strcat (lineout, "c"); X } X strcat (lineout, ptr); X strcat (lineout, " "); X } X if (errflag == 1) X { X skipctr++; X fclose (in); X continue; X } X fprintf (in, "%s\n", lineout); X fclose (in); X sprintf (line, X "%s -fc > /tmp/uucomp.%d < %s;cp /tmp/uucomp.%d %s", X COMPRESS, getpid (), compfile, getpid (), compfile); X system (line); X ctr++; X } X sprintf (line, "/tmp/uucomp.%d", getpid ()); X unlink (line); X printf ("\n%d skipped, %d compressed (%d mail, %d news).\n", X skipctr, ctr, mctr, nctr); X exit (0); X} SHAR_EOF if test 3506 -ne "`wc -c < 'uucomp-1.1/uucomp.c'`" ; then echo "shar: ***** error transmitting file uucomp-1.1/uucomp.c (should have been 3506 characters, but was "`wc -c < 'uucomp-1.1/uucomp.c'`" characters) *****" fi fi touch 0804224693 uucomp-1.1/uucomp.c chmod 0600 uucomp-1.1/uucomp.c echo "shar: extracting uucomp-1.1/uucomp.h - (383 characters)" if test -f 'uucomp-1.1/uucomp.h' ; then echo shar: will not over-write existing file uucomp-1.1/uucomp.h else sed 's/^X//' << \SHAR_EOF > 'uucomp-1.1/uucomp.h' X/* X * X * Copyright 1993 by Ed Carp (erc@apple.com) All rights reserved. X * X * Permission is hereby granted for any non-commercial use of this X * program, as long as this copyright notice remains intact. Commercial X * users may contact me - I'm easy. X * X */ X X#define COMPRESS "/usr/bin/gzip -9c" X#define UNCOMPRESS "/usr/bin/gzip -dc" X#define RMAIL "rmail" X#define RNEWS "rnews" SHAR_EOF if test 383 -ne "`wc -c < 'uucomp-1.1/uucomp.h'`" ; then echo "shar: ***** error transmitting file uucomp-1.1/uucomp.h (should have been 383 characters, but was "`wc -c < 'uucomp-1.1/uucomp.h'`" characters) *****" fi fi touch 0715190293 uucomp-1.1/uucomp.h chmod 0600 uucomp-1.1/uucomp.h echo End of all shell archives exit 0 uucp-1.07/contrib/uudemon.shar0000664000076400007640000000467607665321760012134 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'Poll' <<'END_OF_FILE' X# HDB-ish poll file X# X# Format: ... X# ONLY ONE TAB BETWEEN FIELDS... more may work, but I have absolutely no X# idea if it will work at all. X# X# comment lines (begin with `#') are ignored. X Xdschub 20 21 22 Xgd 20 21 22 END_OF_FILE if test 244 -ne `wc -c <'Poll'`; then echo shar: \"'Poll'\" unpacked with wrong size! fi # end of 'Poll' fi if test -f 'uudemon.poll' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'uudemon.poll'\" else echo shar: Extracting \"'uudemon.poll'\" \(941 characters\) sed "s/^X//" >'uudemon.poll' <<'END_OF_FILE' X#!/bin/sh X# X# This is my impersonation of the HDB uudemon.poll script. X# Yes, I know, this is very clumsy and clunky... ahh well, I've always X# been better at C/pascal/etc than Shell programming... :( X X# change LIBDIR to where UUCP library/conf. files are X# change SPOOLDIR to the UUCP spool directory. It must be HDB-ish. XLIBDIR=/usr/lib/uucp; export LIBDIR XSPOOLDIR=/usr/spool/uucp; export SPOOLDIR X X### no changes needed past here ### X XHOUR=`date +%H`; export HOUR X Xif [ -f ${LIBDIR}/Poll ]; then X for SYS in `uuname` X do X CHOICES="`grep "^$SYS[ ]" ${LIBDIR}/Poll | awk -F' ' \ X '{ print $2 }'`" X DOIT="no" X for H in $CHOICES X do X if [ "$HOUR" = "$H" ]; then X DOIT="yes" X fi X done X if [ "$DOIT" = "yes" ]; then X if [ ! -d ${SPOOLDIR}/${SYS} ]; then X mkdir ${SPOOLDIR}/${SYS} X fi X chmod 755 ${SPOOLDIR}/${SYS} X touch ${SPOOLDIR}/${SYS}/C.${SYS}n0000 X chmod 644 ${SPOOLDIR}/${SYS}/C.${SYS}n0000 X fi X done Xfi END_OF_FILE if test 941 -ne `wc -c <'uudemon.poll'`; then echo shar: \"'uudemon.poll'\" unpacked with wrong size! fi chmod +x 'uudemon.poll' # end of 'uudemon.poll' fi echo shar: End of shell archive. exit 0 uucp-1.07/contrib/uupoll.shar0000664000076400007640000027565207665321760012004 #!/bin/sh # This is a shell archive (produced by shar 3.49) # To extract the files from this archive, save it to a file, remove # everything above the "!/bin/sh" line above, and type "sh file_name". # # made 04/17/1994 02:21 UTC by ian@comton.airs.com # Source directory /disk4/ian # # existing files will NOT be overwritten unless -c is specified # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 2602 -r--r--r-- uupoll/Makefile # 3636 -r--r--r-- uupoll/README # 4718 -r--r--r-- uupoll/autopoll.8c # 44031 -r--r--r-- uupoll/autopoll.c # 3884 -r--r--r-- uupoll/conf.h # 4787 -r--r--r-- uupoll/uupoll.8c # 27587 -r--r--r-- uupoll/uupoll.c # # ============= uupoll/Makefile ============== if test ! -d 'uupoll'; then echo 'x - creating directory uupoll' mkdir 'uupoll' fi if test -f 'uupoll/Makefile' -a X"$1" != X"-c"; then echo 'x - skipping uupoll/Makefile (File already exists)' else echo 'x - extracting uupoll/Makefile (Text)' sed 's/^X//' << 'SHAR_EOF' > 'uupoll/Makefile' && # This is the Makefile for uupoll and autopoll # borrowed and hacked from Taylor UUCP 1.04 X # Prefix directory for installation directories. prefix = /usr/local X # The user name/group that should own the resulting executables. # Both should run suid. owner = uucp.daemon X # Which mode should the resulting executables have. emode = 4111 X # Where to install autopoll. This definition requires $(prefix)/lib to exist. lbindir = $(prefix)/lib/uucp X # Where are the sources from uucp-Taylor uucp.h, uuconf.h, policy.h. # the following assumes that our sources are in uucp-1.05/contrib/uupoll # and the required .h files are in main directory for uucp-1.05 uucpsrcs = ../../ X # Where to install uupoll bbindir = $(prefix)/bin X # Where to install man pages. Section 8 for daemons. man8dir = $(prefix)/man/man8 man8ext = .8c X # Define programs and flags CC = gcc CFLAGS = -O2 LDFLAGS = -s LIBS = X INSTALL = /usr/bin/install -c INSTALL_PROGRAM = $(INSTALL) INSTALL_DATA = $(INSTALL) -m 644 X # # Nothing else to configure # X SHELL = /bin/sh X VERSION = 1.00 X MORECFLAGS = -I. -I$(uucpsrcs) -Wall X PROGRAMS = uupoll autopoll X UUPOLLOBJS = uupoll.o AUTOOBJS = autopoll.o X ALLOBJS = uupoll.o autopoll.o X all: $(PROGRAMS) X install: $(PROGRAMS) X if test -d $(lbindir); then true; else mkdir $(lbindir); fi X if test -d $(bbindir); then true; else mkdir $(bbindir); fi X -if test -f $(lbindir)/autopoll.old; then rm -f $(lbindir)/autopoll; else mv $(lbindir)/autopoll $(lbindir)/autopoll.old; fi X -if test -f $(bbindir)/uupoll.old; then rm -f $(bbindir)/uupoll; else mv $(bbindir)/uupoll $(bbindir)/uupoll.old; fi X $(INSTALL_PROGRAM) autopoll $(lbindir)/autopoll X $(INSTALL_PROGRAM) uupoll $(bbindir)/uupoll X chown $(owner) $(lbindir)/autopoll $(bbindir)/uupoll X chmod $(emode) $(lbindir)/autopoll $(bbindir)/uupoll X $(INSTALL_DATA) uupoll.8c $(man8dir)/uupoll$(man8ext) X $(INSTALL_DATA) autopoll.8c $(man8dir)/autopoll$(man8ext) X uninstall: X rm -f $(lbindir)/autopoll $(bbindir)/uupoll X rm -f $(man8dir)/autopoll$(man8ext) $(man8dir)/uupoll$(man8ext) X -cp $(lbindir)/autopoll.old $(lbindir)/autopoll X -cp $(bbindir)/uupoll.old $(bbindir)/uupoll X -chown $(owner) $(lbindir)/autopoll $(bbindir)/uupoll X -chmod $(emode) $(lbindir)/autopoll $(bbindir)/uupoll X uupoll: $(UUPOLLOBJS) X $(CC) $(LDFLAGS) -o uupoll $(UUPOLLOBJS) $(LIBS) X autopoll: $(AUTOOBJS) X $(CC) $(LDFLAGS) -o autopoll $(AUTOOBJS) $(LIBS) X .c.o: X $(CC) -c $(CFLAGS) $(MORECFLAGS) $< X X clean: X rm -f $(ALLOBJS) $(PROGRAMS) X mostlyclean: clean X TAGS: X etags *.h *.c X # Header file dependencies. These are maintained by hand. X $(ALLOBJS): conf.h X .NOEXPORT: SHAR_EOF chmod 0444 uupoll/Makefile || echo 'restore of uupoll/Makefile failed' Wc_c="`wc -c < 'uupoll/Makefile'`" test 2602 -eq "$Wc_c" || echo 'uupoll/Makefile: original size 2602, current size' "$Wc_c" fi # ============= uupoll/README ============== if test -f 'uupoll/README' -a X"$1" != X"-c"; then echo 'x - skipping uupoll/README (File already exists)' else echo 'x - extracting uupoll/README (Text)' sed 's/^X//' << 'SHAR_EOF' > 'uupoll/README' && X The package consists of the following files: X X - autopoll.c X - autopoll.8c X - conf.h X - Makefile X - README X - uupoll.c X - uupoll.8c X CAVEAT: uupoll as well as autopoll are created, tested and run on a NeXT running NeXTstep 2.1+ only! Autopoll will take the same arguments as uucico and may well work with them the same way uucico works but it has only been tested to call uucico with the options: X X -s -S -f -r1 -C -D (as well as the long form of these options) X so far. All options given to autopoll will be passed verbatim to uucico. X DESCRIPTION: The program uupoll was created to be a full replacement for the vendor supplied one on a NeXT computer. That uupoll checked any site name against the "hardwired" L.sys and did end with a "Bus error" if the site could not be found. There was no source available to modify so it had to be created from scratch. The program autopoll has no equivalent an the NeXT. The intentions behind it was to automate the task of rescheduling any failed call. It may be started by an entry in the crontab tables in just the same way uucico is started (it will start uucico): X 05 5 * * * uucp /usr/local/lib/uucp/autopoll -r1 >>/tmp/poll.log 2>&1 X Any messages go to stderr or a file (if compiled with that option); in case the file could not be opened it will use stdout to tell you just that and quit. To catch any output one may place the string X X >>/tmp/poll.log 2>&1 X into the command line as well. Uupoll as well as autopoll will place only a start message into the logfiles in case they are invoked manually from the shell. If the call fails autopoll will reschedule uucico for a later time by means of an AT job. The messages given by uupoll and autopoll carry an indicator to inform about the nature of an error; they are: X - (I) informal message; such as ".. started" ".. ended". - (W) there might be an error but the program decided to go ahead. X The exit code will be at least 4. - (E) a severe error was encountered that either aborts the whole run or X only the task for one site will be aborted. X The exit code will be at least 8. - (C) a catastrophic error has been found such as unable to fork. The run X is aborted. X The exit code will be at least 16. The final message will show the exit code the programm has terminated with. X For more information see the man pages or look into the source. X INSTALLATION: all files should be placed in one folder. Then examine and change the files Makefile and conf.h to meet your needs. To compile uupoll some files of uucp must be available (see Makefile: uucpsrcs) If not already there change to the directory which contain the files and type: X make X this should compile UUPOLL and AUTOPOLL. There should only be a warning that rcsid is defined but not used. Before actually installing you should test the programs to be working as desired. Then check the Makefile for the final placement of the modules and the man pages. Make sure the ownership and setuid is what you need on your machine to run the program(s). Then su to root and type: X make install X which should install the above programs and the man pages in the appropriate directories. Some word on the coding: have mercy! This is my second project in 'C'; any suggestions that may improve the style/coding are welcome however. X In case of any problems that can't be solved feel free to contact the author at: X Klaus Dahlenburg Timezone : GMT + 2 P.O.Box 1267 email : kdburg@incoahe.hanse.de D-21249 Tostedt Fax : +49 4287 676 X Germany Voice : +49 4287 681 X Have fun! SHAR_EOF chmod 0444 uupoll/README || echo 'restore of uupoll/README failed' Wc_c="`wc -c < 'uupoll/README'`" test 3636 -eq "$Wc_c" || echo 'uupoll/README: original size 3636, current size' "$Wc_c" fi # ============= uupoll/autopoll.8c ============== if test -f 'uupoll/autopoll.8c' -a X"$1" != X"-c"; then echo 'x - skipping uupoll/autopoll.8c (File already exists)' else echo 'x - extracting uupoll/autopoll.8c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'uupoll/autopoll.8c' && .\" .\" @(#)autopoll.8c 1.4 (incoahe) 5/09/1993 .\" .TH AUTOPOLL 8C "May 09, 1993" .UC 6 .SH NAME autopoll \- automatic \s-1UUCP\s+1 file transfer supervisor .SH SYNOPSIS .B autopoll [ .BI options ] .SH DESCRIPTION file transfer requests placed by .IR uucp (1) or .IR uux (1) are handled by .IR uucico (8C). .IR uucico will be invoked immediately by the above programs unless the \-r option is given which queues the request for later processing. This is typically done by entries in the .IR crontab table(s) which will invoke .IR uucico. .IR uucico can also be invoked by .IR uupoll (8C). All methods have in common that there is no automatic retry by .IR uucico itself in case the call failed for some reason. Either manual intervention or some sort of scripts must be used to overcome this limitation. .PP .IR Autopoll can be used to automate up to a certain degree the task of rescheduling a call. None of the standard programs already mentioned need to be modified to get this working. Also not recommended (see BUGS section) .IR uucico may be an alias to .IR autopoll as all arguments passed to .IR autopoll will be copied verbatim to .IR uucico. In case this is done by link or other means the original .I uucio must still be available in a directory outside of the normal search path otherwise .I autopoll can't do what it's intended to do and will form a loop. .PP When .IR autopoll is called thre will be a check on the \-s, \-S and \-f option to see whether this is a specific call or not. Also the \-S and the \-f option must be checked to determine the type of call: honor any imposed wait for a site or not. Any call to ourself or to an unknown site will be refused. The known sites will be obtained by a call to .IR uuname(1). All other options will not be checked in any way. Next to this .IR uucico is called and the exit code is checked for a `1' which indicates that the call failed for some reason whatsoever. A `0' exit code will be interpreted as a success and .IR autopoll ends immediate. If the call seems to be unsuccessful a new call is scheduled for any site whose .Status files have a retry period greater than 0. The retry will be scheduled by means of placing an .IR at job at the time of the failing call plus any wait time given. For those sites that have been called with either the \-f or \-S option the retry time will be the time of the failing call plus 120 seconds. .PP In case the time calculated from the values found in a \.Status file is lower than the current time, the current time plus 60 seconds will be taken as the retry time. .PP A site will .IR not be automatically called again if one of the following conditions is met: .PP \- .IR uucico is terminated by a signal .PP \- either fork() or exec() failed .PP \- the .IR at command failed for any reasons. .PP \- if no wait should be honored and the retry time is found to be zero. (this may indicate a `Wrong time to call' condition. .PP There are other circumstances that may lead to not reschedule a call or not to call .IR uucico at all, all of which should be accompanied by (a) self explanatory message(s). .SH BUGS \- invalid options will make .IR uucico fail. The exit code for this type is the same as for any other failure; this can reschedule the call over and over again or never. .PP \- .IR autopoll may not work as expected when called with options other than \-r1, \-s, \-S or \-f. .PP \- a rescheduled call may fail with `wrong time to call' the second time but will be rescheduled again. The times to call won't be checked by .IR autopoll and the .Status file may not indicate this in case the \-c option is given. .PP \- in case the ..._DIR points to an invalid file a `Bus error' my pop up during the `exec' call. .PP \- the `chat-timeout' value may have to be increased when using .IR autopoll. An indication to do that is that the call fails short after `CONNECT' has been received with `Time out in chat script'. .PP \- the site names given will be checked aginst the output of .I uuname without any alias expansion done. .PP \- the text strings whithin the \.Status files will not be used to detect a failing call. .SH FILES .nf /usr/local/lib/uucp UUCP internal utilities /usr/lib/uucp /usr/local/bin UUCP internal utilities /usr/bin /usr/spool/uucp/.Status/ Status files for each site /usr/spool/uucp/ UUCP spool area. one of its sub- X directories will hold the null jobs. /tmp/poll.log This file is present only if autopoll X has been compiled to place the messages X into a file. Otherwise all messages will X go to stderr. The directory as well as X the name may be different. .fi .SH SEE ALSO uucp(1C), uux(1C), uucico(8C), uupoll(8C), uuname(1C), sort(1), uniq(1), at(1) SHAR_EOF chmod 0444 uupoll/autopoll.8c || echo 'restore of uupoll/autopoll.8c failed' Wc_c="`wc -c < 'uupoll/autopoll.8c'`" test 4718 -eq "$Wc_c" || echo 'uupoll/autopoll.8c: original size 4718, current size' "$Wc_c" fi # ============= uupoll/autopoll.c ============== if test -f 'uupoll/autopoll.c' -a X"$1" != X"-c"; then echo 'x - skipping uupoll/autopoll.c (File already exists)' else echo 'x - extracting uupoll/autopoll.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'uupoll/autopoll.c' && /* ---------------------------------------------------------------------------* X X Name: autopoll X X Author: Klaus Dahlenburg X X Status: Public domain X X Copyright: none; claiming it to be your work will adversly affect X your image to be a good programmer. X X Function: Autopoll may be called just as uucico is called. The difference X is that autopoll will call uucico and if the return code is X not zero a check is made on the status files to see which site's X call failed. Those sites will be called again at that time found X in the status file plus any imposed wait. The next call will be X scheduled via an at job which in turn is handled by cron. X Atrun depends on the scheduling granularity of cron so the X actual times may be later than planned. X Autopoll will check the options -f and -s (-S) as well as the name X of the site passed. All other options will be passed unchecked. X The -f and -S options will indicate to autopoll that any wait X to call a site should be ignored; if the call fails the next X call to those sites will be at the current time plus 120 secs. X When the time found plus any wait evaluates to a time that X passed already the next call will be the current time plus 60 X secs. The name of the site if given must be a valid one and not X the host itself otherwise it will be ignored. X X Call: autopoll [ options ] X X all option that apply to uucico may be given and X will be passed verbatim. See man uucico(8). X X Environment: NeXT 2.1+, Taylor UUCP-1.04+ X X I/O: stdin: unused. X stdout: used only when ALOG_DIR is defined and the file X can't be opened. It will be a single message to tell X just that and the run is aborted. X stderr: all messages go here. X If ALOG_DIR is defined (see conf.h) all messages will X be appended to a file autopoll.msglog in that X directory; the file will be created automatically if X necessary; a redirection is then no longer possible. X Needs access to .Status files (see Comments later on). X X Called Programs: sort, uniq, uucico, uuname, at X X Compile: no special options are needed. Compiled with gcc 2.3.3 -O2. X Compile with the supplied cc might produce erroneous code X for the check options switch case 's' code: the break inside X the first if (..) {... break} is ignored. X X Comments: - should run setuid UUCP or whatever userid is necessary to X access (RDONLY) the .Status files and to run the programs X named under "Called Programs" above. X - No alias expansion is done on the given names for the X check against uuname's output.. X - Invalid arguments will yield in an exit code > 0 as do X "normal" failures. It may therefore happen that a site X is called at the intervals with the same invalid arguments. X - "Wrong time to call" is not handled properly and may X call the site at the intervals until the time ban is lifted. X - human action is necessary as we can't distinguish between X "normal" failures and "errors" such as wrong password, X number to dial etc. The logs should be checked periodically. X - if CICO_DIR points to a non existent program the run may X end with signal 10: Bus Error. X - is has been observed that uucico will time out with "Dial X failed" when called via autopoll; setting chat-timeout to X value of 40 cured that problem. X - no rescheduling is done in case uucico fails and this X is not reported in the .Status file, one should check X the uucico log; this is to the fact that autopoll will X not scan the uucico log. */ X X #if !defined(lint) static char rcsid[] = "$Id: autopoll.c,v 2.8 1994/04/14 17:22:54 kdburg Rel $"; #endif /* not lint */ X /* $Log: autopoll.c,v $ X * Revision 2.8 1994/04/14 17:22:54 kdburg X * corrected misspelled AT_OPTION X * X * Revision 2.7 1994/04/11 20:15:48 kdburg X * major rework done; honor now some of the new option taht came with X * uucp-1.05 X * X * Revision 2.6 1994/03/26 17:40:30 kdburg X * added support for UNAME_DIR; cleanup of some code; adjusted code after X * obtaining sitenames via popen() X * X * Revision 2.5 1993/07/07 16:49:02 kdburg X * when used interactivly only the start msg is put into the msg-log X * so far defined (UULOG) X * X * Revision 2.4 1993/06/26 16:17:51 kdburg X * the -S option wasn't propagated to the command passed to the at pgm X * X * Revision 2.3 1993/05/25 12:05:01 kdburg X * added error check on gettimeofday; added comment in the note section; X * minor changes not affection code X * X * Revision 2.2 1993/05/17 20:47:05 kdburg X * execution of at cmd also ok always said failed... X * X * Revision 2.1 1993/05/16 21:49:13 kdburg X * changed exit() to _exit() in case the exec fails within child X * X * Revision 2.0 1993/05/16 14:12:05 kdburg X * initial revision X * */ X #define CAT 16 #define SEVERE 8 #define WARNING 4 #define OK 0 /* Boolean types */ typedef int bool; #undef TRUE #undef FALSE #define TRUE (1) #define FALSE (0) X #include "conf.h" #include #include #include #include #include #include #include #include #include #include X #ifdef ALOG_FILE X static char Msg_Log[] = ALOG_FILE; /* name of msglog filename */ #endif X #ifdef UNAME_DIR X static char subcmd[] = " | sort | uniq"; /* pipe that follows uuname */ #else /* ! UNAME_DIR */ X static char Sort[] = "uuname | sort | uniq"; /* default to obtain site names */ #endif /*UNAME_DIR */ X #ifdef AT_OPTION X static char at_opt[] = AT_OPTION; #else X static char at_opt[] = "-mc"; #endif /* AT_OPTION */ X static char at_cmd[] = "at"; static char cGrade[] = DEF_GRADE; /* grade as defined in conf.h */ static char dGrade[] = "A"; /* use this if DEF_GRADE is invalid */ static char Auto_Dir[] = AUTO_DIR; /* we live here */ static char Cico_Dir[] = CICO_DIR; /* here lives cico */ X struct Sites { X char name[MAXHOSTNAMELEN+1]; /* name of site as supplied by uuname */ X char grade[1]; /* as passed or defaulted */ X bool flag; /* TRUE: call this site only */ X bool force; /* TRUE: -S or -f option given */ X int stat_code; X int stat_retries; X long stat_lastcall; X long stat_delay; X char *stat_errtext; }; X struct Common_Stor { X int maxtab; /* high-water-mark for site tab */ X int Single_Site_Tab; /* entry into site tab for a site */ X /* passed via -s or -S option */ X bool force_any; /* TRUE: -f option without site */ X bool one_site; /* TRUE: call for a specific site */ X bool nodetach; /* TRUE: -D or --nodetach found */ X bool ifwork; /* TRUE: -C or --ifwork found */ X char *Grade; /* use this as grade for calls */ X char *Poll_Pgm; /* our name without path */ X char *called_as; /* but called by this name */ X int our_pid; /* our process-id */ X char *Uucico; /* cico's name without path */ X char This_Site[MAXHOSTNAMELEN+1]; /* our site name */ X char Single_Site[MAXHOSTNAMELEN+1]; /* name of site found as arg */ X union wait *W_Stat; X char *Usort; /* will hold uuname + subcmd */ X struct passwd *pwd; X struct timeval tp; X struct timezone tzp; X struct Sites Sitetab[SITE_MAX]; X char mon[3]; X int day, hh, mm, ss; X char oname[24]; X char jname[20]; X char tstr[20]; X char ctag[2]; X char workf[300]; X char call_args[300]; X }; X /* copied from taylor uucp "uudefs.h" X * X **/ X /* The tstatus_type enumeration holds the kinds of status information X we put in the status file. The order of entries here corresponds X to the order of entries in the azStatus array. */ enum tstatus_type { X /* Conversation complete. */ X STATUS_COMPLETE, X /* Port unavailable. */ X STATUS_PORT_FAILED, X /* Dial failed. */ X STATUS_DIAL_FAILED, X /* Login failed. */ X STATUS_LOGIN_FAILED, X /* Handshake failed. */ X STATUS_HANDSHAKE_FAILED, X /* Failed after logging in. */ X STATUS_FAILED, X /* Talking to remote system. */ X STATUS_TALKING, X /* Wrong time to call. */ X STATUS_WRONG_TIME, X /* Number of status values. */ X STATUS_VALUES }; X /* ----end-- copied from taylor uucp "uudefs.h" */ X X /* define the prototypes X * */ X int set_mlog(FILE **seclog, struct Common_Stor *); int get_sites(struct Common_Stor *); int Call_Cico(int argc, char *argv[], struct Common_Stor *); int get_args(int argc, char *argv[], struct Common_Stor *); int Housekeeping(int argc, char *argv[], struct Common_Stor *); int Chk_Status(int argc, char *argv[], X struct timeval tcc, X struct timezone tzcc, X struct Common_Stor *); int Check_Site(struct Common_Stor *); int start_at(char *name, struct Common_Stor *); void *storage(unsigned count, char *errloc, int *Rc, struct Common_Stor *); X extern int gethostname(char *name, int namelen); extern int system(char *cmd); extern int fork(); extern int unlink(char *path); extern void *malloc(size_t byteSize); extern int execve(char *name, char *argv[], char *envp[]); extern int execlp(char *name, char *arg0, ...); extern int chmod(char *path, int mode); extern int getuid(); extern int getpid(); extern int isatty(int); extern char *ttyname(int); extern void free(void *ptr); #ifdef __STRICT_ANSI__ extern FILE *popen(char *command, char *type); extern int pclose(FILE *stream); extern void _exit(int status); #endif /* __STRICT_ANSI__ */ #ifdef __STRICT_BSD__ extern int fprintf(FILE *stream, const char *format, ...); extern int fclose(FILE *stream); extern char *strerror(int errnum); extern int fflush(FILE *stream); extern void exit(int status); extern int fscanf(FILE *stream, const char *format, ...); extern int sscanf(char *s, const char *format, ...); #endif /* __STRICT_BSD__ */ X /* --------------------------------------------------------------------------*/ /* Main */ /* --------------------------------------------------------------------------*/ X int main(int argc, char *argv[]) { X X struct Common_Stor *sCom_Sto; X int Maxrc = OK; /* Max err-code encountered so far */ X int k = 0; X X if ( NULL == (sCom_Sto = malloc(sizeof(struct Common_Stor))) ) { X fprintf(stderr,"%s: (C) malloc failed (%s). Reason: %i (%s)\n", X AUTO_DIR,"Common_Stor",errno,strerror(errno)); X exit (CAT); X } X X Maxrc = Housekeeping(argc, argv, sCom_Sto); X /* If any errors popped up so far they are of such a nature that it is very X * questionable to continue; so we better bail out in this case. X */ X if (Maxrc <= WARNING) { X if ((sCom_Sto->W_Stat = (union wait *)storage (sizeof(union wait), X "W_Stat",&Maxrc,sCom_Sto)) != NULL) { X k = Call_Cico(argc, argv, sCom_Sto); X Maxrc = Maxrc >= k ? Maxrc:k; X free(sCom_Sto->W_Stat); X sCom_Sto->W_Stat = NULL; X } X } X k = gettimeofday(&sCom_Sto->tp, &sCom_Sto->tzp); X fprintf(stderr,"%s: (I) ended with rc = %i on %s\n", X sCom_Sto->called_as, X Maxrc,k!=0 ? "time unavailable":ctime(&sCom_Sto->tp.tv_sec)); X fclose(stderr); X free(sCom_Sto); X sCom_Sto = NULL; X exit (Maxrc); } X /* --------------------------------------------------------------------------*/ /* Functions */ /* --------------------------------------------------------------------------*/ X /* -------------------------------------------------------------------- X * housekeeping X */ X int Housekeeping(argc, argv, sCom_Sto) X int argc; X char *argv[]; X struct Common_Stor *sCom_Sto; { X X FILE *seclog = NULL; X int Rc = OK; X int Rci = OK; /* intermediate rc as returnd by functions */ X X sCom_Sto->our_pid = getpid(); X /* X * get our name sans path X * */ X X sCom_Sto->called_as = argv[0] + strlen(*argv); X for(;sCom_Sto->called_as >= argv[0] && *--sCom_Sto->called_as != '/';) X ; X sCom_Sto->called_as++; X /* if defined set up the name of the message log file otherwise X * stderr will be used. Setup the cmd string to obtain all known sitenames X * which will be sorted in ascending order with duplicates removed X * */ X X Rc = set_mlog(&seclog, sCom_Sto); X if (Rc > WARNING) X return (Rc); X /* put out the started message including the time and the userid. X * */ X X sCom_Sto->pwd = getpwuid(getuid()); X X if ((gettimeofday(&sCom_Sto->tp, &sCom_Sto->tzp)) != 0) { /* unacceptable error */ X fprintf(stderr,"%s: (C) gettimeofday() failed. Reason: %i (%s)\n", X sCom_Sto->called_as,errno,strerror(errno)); X return (Rc >= CAT ? Rc:CAT); X } X X if (seclog != NULL) { X fprintf(seclog,"\n%s: (I) started by `%s' (%s) on %s", X sCom_Sto->called_as, X (sCom_Sto->pwd==NULL) ? "???":sCom_Sto->pwd->pw_name, X ttyname(0), X ctime(&sCom_Sto->tp.tv_sec)); X fclose(seclog); X } X fprintf(stderr,"\n%s: (I) started by `%s' on %s", X sCom_Sto->called_as, X (sCom_Sto->pwd==NULL) ? "???":sCom_Sto->pwd->pw_name, X ctime(&sCom_Sto->tp.tv_sec)); X /* set up the default grade X * */ X X sCom_Sto->Grade = dGrade; /* set default for now */ X if (strlen(cGrade) != 1) { X fprintf(stderr,"%s: (W) grade %s invalid; default `%s' used\n", X sCom_Sto->called_as,cGrade,sCom_Sto->Grade); X Rc = Rc >= WARNING ? Rc:WARNING; X } X else X sCom_Sto->Grade = cGrade; /* Ok, take the one from conf.h */ X /* get the program to actually call the site. This is normally UUCICO. X * */ X X sCom_Sto->Uucico = Cico_Dir + strlen(Cico_Dir); X for(;sCom_Sto->Uucico >= Cico_Dir && *--sCom_Sto->Uucico != '/';) X ; X sCom_Sto->Uucico++; X /* get the path to ourself. X * */ X X sCom_Sto->Poll_Pgm = Auto_Dir + strlen(Auto_Dir); X for(;sCom_Sto->Poll_Pgm >= Auto_Dir && *--(sCom_Sto->Poll_Pgm) != '/';) X ; X sCom_Sto->Poll_Pgm++; X /* obtain our sitename X * */ X X if ((gethostname(sCom_Sto->This_Site,MAXHOSTNAMELEN+1)) != 0) { X fprintf(stderr,"%s: (W) hostname could not be obtained\n", X sCom_Sto->called_as); X Rc = (Rc >= WARNING) ? Rc:WARNING; X } X /* obtain all known sitenames X * */ X X Rci = get_sites(sCom_Sto); X Rc = Rci > Rc ? Rci:Rc; X /* check the arguments that we are called with X * */ X X Rci = get_args(argc, argv, sCom_Sto); X Rc = Rci > Rc ? Rci:Rc; X X return (Rc); } X /* -------------------------------------------------------------------- X * check all relevant arguments that have been passed to us. Those args X * that may be needed for a recall will be copied to a workfield. X * */ X int get_args(int argc, char *argv[], struct Common_Stor *sCom_Sto) { X X int j = 0; X int Rc = OK; X int Rci = OK; X X strcpy(sCom_Sto->Single_Site,""); X sCom_Sto->force_any = FALSE; X sCom_Sto->one_site = FALSE; X sCom_Sto->nodetach = FALSE; X X strcpy(sCom_Sto->call_args,AUTO_DIR); /* specify complete path to us */ X strcat(sCom_Sto->call_args," "); /* and separate by one space */ X for (j=1;jnodetach = TRUE; X strcat(sCom_Sto->call_args,"-D "); X continue; X } X if (strcmp(argv[j],"--force") == 0 || X strcmp(argv[j],"-f") == 0) { X strcat(sCom_Sto->call_args,"-f "); X sCom_Sto->force_any = TRUE; X continue; X } X if (strcmp(argv[j],"--ifwork") == 0 || X strcmp(argv[j],"-C") == 0) { X sCom_Sto->ifwork = TRUE; X continue; X } X if ( strncmp(argv[j],"-s",2) == 0 || X strncmp(argv[j],"-S",2) == 0 || X strcmp(argv[j],"--system") == 0) { X if (strncmp(argv[j],"-S",2) == 0) X sCom_Sto->force_any = TRUE; X X if (strlen(argv[j]) == 2 || strcmp(argv[j],"--system") == 0) { X j++; X if (j>=argc) { X fprintf(stderr,"%s: (E) System to call is missing\n", X sCom_Sto->called_as); X Rc = Rc >= SEVERE ? Rc:SEVERE; X break; X } X else { X strcpy(sCom_Sto->Single_Site,argv[j]); X Rci = Check_Site(sCom_Sto); X if (! Rci) { X sCom_Sto->one_site = TRUE; /* specific call */ X strcat(sCom_Sto->call_args,argv[j-1]); X strcat(sCom_Sto->call_args," "); X strcat(sCom_Sto->call_args,argv[j]); X strcat(sCom_Sto->call_args," "); X } X } X Rc = Rci <= Rc ? Rc:Rci; X } X else { X strcpy(sCom_Sto->Single_Site,argv[j]+2); X Rci = Check_Site(sCom_Sto); X if (! Rci) { X sCom_Sto->one_site = TRUE; /* specific call */ X strcat(sCom_Sto->call_args,argv[j]); X strcat(sCom_Sto->call_args," "); X } X Rc = Rci <= Rc ? Rc:Rci; X } X continue; X } X strcat(sCom_Sto->call_args,argv[j]); X strcat(sCom_Sto->call_args," "); X } /* end copy all arguments */ X X if (sCom_Sto->ifwork) { X if (sCom_Sto->one_site) { X strcat(sCom_Sto->call_args,"-C "); X } X else { X fprintf(stderr,"%s: (W) no site given, '-C' option is ignored\n", X sCom_Sto->called_as); X sCom_Sto->ifwork = FALSE; X Rc = Rc >= WARNING ? Rc:WARNING; X } X } X X if (! sCom_Sto->nodetach) { X strcat(sCom_Sto->call_args,"-D "); X } X X return (Rc); } X /* -------------------------------------------------------------------- X * call uucico or whatever programm is necessary to get connected X */ X /* Start uucico and wait for completion. In case the return code is '0' X * we're finished; otherwise we'll have to check the status files for any X * non successful calls (retry time > 0). X * Any such site will be called again at the current time plus any wait X * Note: X * If the '-D' or '--nodetach' option is missing, uucico will X * detach immediate. The return-code is 0 in this case and therefore X * we can't check whether the call is successful or not. No recall X * is scheduled for such an invocation. If we however get control X * to schedule a recall we silently add the '-D' option. To add X * the '-D' option in any case may be undesirable for a specific X * type of run. X */ X int Call_Cico(int argc, char *argv[], struct Common_Stor *sCom_Sto) { X X int W_Ret = 0; X int pid = 0; X int Rc = OK; X struct timeval tcc; X struct timezone tzcc; X X if ((gettimeofday(&tcc, &tzcc)) != 0) { /* unacceptable error */ X fprintf(stderr,"%s: (C) gettimeofday() failed. Reason: %i (%s)\n", X sCom_Sto->called_as,errno,strerror(errno)); X Rc = Rc >= CAT ? Rc:CAT; X } X X if (Rc > WARNING) { X return (Rc); X } X X fflush(stderr); X switch(pid = fork()) { X case -1: X fprintf(stderr,"%s: (C) could not fork(). Reason-code: %i (%s)\n", X sCom_Sto->called_as,errno,strerror(errno)); X return (CAT); X case 0: X if ((argv[0] = (char *)storage(strlen(sCom_Sto->Uucico)+1,"argv[0]", X &Rc,sCom_Sto)) == NULL) { X _exit (CAT); X } X strcpy(argv[0],sCom_Sto->Uucico); /* change name to be uucico */ X execve(Cico_Dir, argv, NULL); X fprintf(stderr,"%s: (C) could not start %s. Reason-code: %i (%s)\n", X sCom_Sto->called_as, X sCom_Sto->Uucico,errno,strerror(errno)); X _exit (CAT); /* child: bail out */ X default: X fprintf(stderr,"%s: (I) starting %s [%d]\n\n", X sCom_Sto->called_as,sCom_Sto->Uucico,pid); X fflush(stderr); /* maybe we come behind uucico's output */ X /* if any; it's a race condition */ X W_Ret = wait(sCom_Sto->W_Stat); X if (sCom_Sto->W_Stat->w_termsig == 0) { X if (sCom_Sto->W_Stat->w_retcode == 0) { X fprintf(stderr,"%s: (I) %s [%d] ended normally\n", X sCom_Sto->called_as,sCom_Sto->Uucico,pid); X return (OK); X } X if (sCom_Sto->W_Stat->w_retcode != CAT) { X fprintf(stderr,"%s: (I) %s's log may contain further information !\n", X sCom_Sto->called_as,sCom_Sto->Uucico); X fprintf(stderr,"\n%s: (W) %s [%d] ended with rc = %i\n", X sCom_Sto->called_as, X sCom_Sto->Uucico,pid, X sCom_Sto->W_Stat->w_retcode); X return (Chk_Status(argc, argv, X tcc, tzcc, sCom_Sto)); X } X else X return (CAT); /* we where unable to exec */ X } X else { X fprintf(stderr,"\n%s: (E) %s [%d] terminated by signal %i\n", X sCom_Sto->called_as, X sCom_Sto->Uucico, X pid, X sCom_Sto->W_Stat->w_termsig); X return (SEVERE); X } X } /* switch (pid = fork()) */ X return (OK); /* Never reached: silence the compiler */ } X X /* -------------------------------------------------------------------- X * check the status after the call has completed and the return code X * is > zero. The status is checked for all sites found via uuname or X * for one site only (option -s, -S or --system given on call) X */ X int Chk_Status(int argc, char *argv[], X struct timeval tcc, X struct timezone tzcc, X struct Common_Stor *sCom_Sto) { X /* X * For all sites found in Site_Tab their status files will be checked. X * The table scan will be bypassed for a call to a specific site. X * If the call failed the wait period is > 0. We will schedule an at-job X * to be run at the time found + the delta. In case we find an old entry X * where the time + delta is lower than the current time we'll advance X * the current time by 60 secs. and use that value instead. X * In case we are invoked to call a specific site and either the -f option or X * the site was given as -S... indicating to disregard any wait, we'll X * use the time found in the status file increased by 120 secs. */ X X FILE *infile; X long secs, retries = 0; X long add = 0; X int errind = 0; X int i = 0; X int ecnt = 0; X int recall_cnt = 0; X char curr_site[MAXHOSTNAMELEN+11] = ""; /* keyword + sitename */ X bool schedule = TRUE; /* FALSE: no more rescheduling: unspec. + force */ X int Rc = WARNING; /* uucico got rc = 1 otherwise we were not here */ X int Rs = 0; /* uucico' reason code from .Status file */ X /* X * Note X * We have to increase the sum of time and wait by at least one minute. X * That is because this time denotes the earliest point *after* which X * we may call again. X * When a site is called at the wrong time the follwing actions are X * taken: wait = 0 && ! force --> no further action (indicator: see log) X * wait = 0 && force --> (W) message generated; no further action X * wait > 0 && ! force --> normal scheduling at time + wait X * wait > 0 && force --> normal scheduling at time+120 secs X * We can't depend on the string "Wrong time to call" because the .Status X * file may not be updated due to the -c switch. This may lead to a X * situation where the site will be called over and over again while it's X * still the wrong time. (No we don't want to go fishing for a message in X * the uucp LOG!) X * In case the -s, -S or --system option was given we will only X * check that site and schedule a recall for it so far the X * conditions are met. X * In case the -C or --ifwork switch is given without naming a site a X * the option is dropped and only an unspecific call is scheduled. X * */ X X if (sCom_Sto->one_site) { X i = sCom_Sto->Single_Site_Tab; X if (strncmp(sCom_Sto->Sitetab[i].name, X sCom_Sto->Single_Site, X sizeof(sCom_Sto->Single_Site)) != 0) { X fprintf(stderr,"%s: (C) internal index-error (%d): %s found: %s\n", X sCom_Sto->called_as, X i, X sCom_Sto->Single_Site, X sCom_Sto->Sitetab[i].name); X Rc = Rc >= CAT ? Rc:CAT; X return (Rc); /* break unconditionally */ X } X } X X for (i = sCom_Sto->Single_Site_Tab; i <= sCom_Sto->maxtab; i++) { X sprintf(sCom_Sto->workf,"%s%s",STATUS_DIR,sCom_Sto->Sitetab[i].name); X if ((infile=fopen(sCom_Sto->workf,"r")) == NULL) { X ecnt++; X fprintf(stderr,"%s: (W) no access to status file for: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X Rc = Rc >= WARNING ? Rc:WARNING; X if (sCom_Sto->one_site) { X break; X } X else { X continue; X } X } X X fscanf(infile,"%d %d %ld %ld",&errind,&retries,&secs,&add); X fclose(infile); X X /* X * in case the .Status file is not updated and we have a call to X * a specific site we try to give some clues of what went wrong X * (we won't succeed in any case!) X */ X X if (sCom_Sto->Sitetab[i].stat_lastcall == secs && sCom_Sto->one_site) { X X if (errind == 0 && retries == 0 && add == 0) X break; X X if (errind > 0) { X if (tcc.tv_sec <= (secs+add) && ! sCom_Sto->Sitetab[i].force) { X fprintf(stderr,"%s: (W) retry time not reached for site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X Rc = Rc >= WARNING ? Rc:WARNING; X } X else { X fprintf(stderr,"%s: (E) maybe port/site unavailable site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X } X else { X if (sCom_Sto->one_site) { X fprintf(stderr,"%s: (E) unknown error for call to site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X } X fprintf(stderr,"%s: (W) no recall scheduled for site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X break; /* bail out completely */ X } X X if (sCom_Sto->Sitetab[i].stat_lastcall == secs) { X if (sCom_Sto->one_site) X break; X else X continue; X } X X Rs = OK; /* if Rs is > WARNING we won't schedule a recall */ X switch(errind) { X case STATUS_COMPLETE: X if (add != 0 || retries != 0) { X fprintf(stderr,"%s: (E) unknown error for call to site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X Rs = Rs >= SEVERE ? Rs:SEVERE; X } X break; X case STATUS_PORT_FAILED: X fprintf(stderr,"%s: (E) port was unavailable site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X break; X case STATUS_DIAL_FAILED: X fprintf(stderr,"%s: (E) dail failed for site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X break; X case STATUS_LOGIN_FAILED: X fprintf(stderr,"%s: (E) login for site: %s failed\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X Rs = Rs >= SEVERE ? Rs:SEVERE; X break; X case STATUS_HANDSHAKE_FAILED: X fprintf(stderr,"%s: (E) handshake failed site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X break; X case STATUS_FAILED: X fprintf(stderr,"%s: (E) invalid status after login site: %s \n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X break; X case STATUS_TALKING: X break; X case STATUS_WRONG_TIME: X fprintf(stderr,"%s: (W) it's the wrong time to call site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X Rs = Rs >= SEVERE ? Rs:SEVERE; X break; X default: X fprintf(stderr,"%s: (E) unknown error for call to site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X Rs = Rs >= SEVERE ? Rs:SEVERE; X break; X } X Rc = Rs > Rc ? Rs:Rc; X if (Rs > WARNING) { /* schedule a recall ? */ X fprintf(stderr,"%s: (W) no recall scheduled for site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X if (sCom_Sto->one_site) X break; X else X continue; X } X X if (add == 0) { X fprintf(stderr,"%s: (W) no delay found for site: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X Rc = Rc >= WARNING ? Rc:WARNING; X } X X X if (! schedule) { X recall_cnt += 1; X continue; /* scheduling already done: unspec. + force */ X } X if ((gettimeofday(&sCom_Sto->tp, &sCom_Sto->tzp)) != 0) { X fprintf(stderr,"%s: (C) gettimeofday() failed. Reason: %i (%s)\n", X sCom_Sto->called_as,errno,strerror(errno)); X fclose(infile); X Rc = Rc >= CAT ? Rc:CAT; X break; /* break unconditionally */ X } X X if (sCom_Sto->force_any || sCom_Sto->Sitetab[i].force) { X add = secs + 120; /* shorten the wait */ X } X else { /* ! force */ X /* X * check for an already scheduled recall. For we don't keep X * a file of already scheduled recalls the only way to recognize X * this, is to check the current time against that of the .Stats file. X * In case the current time is >= the .Stats-time + n-secs fuzz value X * we assume (99.99% correctness) that we have already scheduled a recall X * for this site. If this assumption is incorrect a call will be X * scheduled on the next unspecific failing call. This check can't X * be done for forced call because the .Stats will be updated. X */ X if (sCom_Sto->tp.tv_sec >= secs+2) { X fprintf(stderr,"%s: (W) Retry time not reached for site: %s\n", X sCom_Sto->called_as, X sCom_Sto->Sitetab[i].name); X Rc = Rc >= WARNING ? Rc:WARNING; X if (sCom_Sto->one_site) X break; X else X continue; X } X add += secs + 60; /* if not force then take the full wait */ X } /* force */ X X if (sCom_Sto->tp.tv_sec >= add) { X add = sCom_Sto->tp.tv_sec + 60; /* time < current time */ X } X X sscanf(ctime(&add),"%*s %s %d %d:%d:%d",sCom_Sto->mon, X &sCom_Sto->day, X &sCom_Sto->hh, X &sCom_Sto->mm, X &sCom_Sto->ss); X X sprintf(sCom_Sto->oname,"/tmp/at.%d.%02d%02d%02d",sCom_Sto->our_pid, X sCom_Sto->hh, X sCom_Sto->mm, X sCom_Sto->ss); X if (! sCom_Sto->one_site) { X strcpy(curr_site,"-s"); X strcat(curr_site,sCom_Sto->Sitetab[i].name); X } X X /* X * If 'onesite' is FALSE and 'force' is TRUE X * we will reschedule one unspecific call an let UUCICO decide X * which site should be called (is there any work?) X */ X X if ( ! sCom_Sto->one_site && sCom_Sto->force_any) { X recall_cnt += 1; X schedule = FALSE; X continue; X } X strcat(sCom_Sto->call_args,curr_site); X Rs = start_at(sCom_Sto->Sitetab[i].name, sCom_Sto); X Rc = Rs >= Rc ? Rs:Rc; X unlink(sCom_Sto->oname); X if (Rc > SEVERE || sCom_Sto->one_site) X break; X } /* for (i = Single_Site_Tab; ...) */ X X if (ecnt > sCom_Sto->maxtab) { X fprintf(stderr,"%s: (E) no access to status files; no scheduling done\n", X sCom_Sto->called_as); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X else { X if (! schedule) { X if (recall_cnt == 1) { X strcat(sCom_Sto->call_args,curr_site); X } X Rs = start_at("any site", sCom_Sto); X Rc = Rs >= Rc ? Rs:Rc; X unlink(sCom_Sto->oname); X } X } X return (Rc); } X X /* X * X */ X int start_at(char *site, struct Common_Stor *sCom_Sto) { X FILE *outfile; int W_Ret = 0; int Rc = OK; int pid = 0; X /* X * if we can't open the workfile to be passed to AT we'll abandon X * this site and set the rc accordingly X * */ X X if ((outfile=fopen(sCom_Sto->oname,"w")) == NULL) { X fprintf(stderr,"%s: (E) could not open workfile %s. No scheduling for: %s\n", X sCom_Sto->called_as, X sCom_Sto->oname, X site); X Rc = Rc >= SEVERE ? Rc:SEVERE; X fclose(outfile); X unlink(sCom_Sto->oname); X return (Rc); /* bail out here */ X } X sprintf(sCom_Sto->jname,"at.%d.%02d%02d%02d",sCom_Sto->our_pid, X sCom_Sto->hh, X sCom_Sto->mm, X sCom_Sto->ss); X fprintf(outfile,"%s \n",sCom_Sto->call_args); X sprintf(sCom_Sto->tstr,"%02d%02d",sCom_Sto->hh, X sCom_Sto->mm); X sprintf(sCom_Sto->ctag,"%d",sCom_Sto->day); X fclose(outfile); X if ((chmod(sCom_Sto->oname,00644)) != 0) { X fprintf(stderr,"%s: (W) chmod to %s failed. Reason_code: %i (%s)\n", X sCom_Sto->called_as, X sCom_Sto->oname, X errno, X strerror(errno)); X Rc = Rc >= WARNING ? Rc:WARNING; X } X X switch (pid = fork()) { X case -1: X fprintf(stderr,"%s: (C) could not fork(). Reason-code: %i (%s)\n", X sCom_Sto->called_as,errno,strerror(errno)); X return (Rc >= CAT ? Rc:CAT); X case 0: X if (*at_opt == '\0') X execlp(at_cmd, at_cmd, sCom_Sto->tstr, X sCom_Sto->mon, sCom_Sto->ctag, X sCom_Sto->oname, 0); X else X execlp(at_cmd, at_cmd, at_opt, sCom_Sto->tstr, X sCom_Sto->mon, sCom_Sto->ctag, X sCom_Sto->oname, 0); X X fprintf(stderr,"%s: (C) could not start AT-cmd. Reason-code: %i (%s)\n", X sCom_Sto->called_as, X errno,strerror(errno)); X _exit (CAT); /* child: bail out */ X default: X fprintf(stderr,"%s: (I) at [%d] started. Job name: %s\n", X sCom_Sto->called_as, X pid, X sCom_Sto->jname); X W_Ret = wait(sCom_Sto->W_Stat); X if (sCom_Sto->W_Stat->w_termsig == 0) { X if (sCom_Sto->W_Stat->w_retcode != 0) { X if (sCom_Sto->W_Stat->w_retcode != CAT) { X fprintf(stderr,"%s: (E) at-cmd failed for some reason\n", X sCom_Sto->called_as); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X else { X Rc = Rc >= CAT ? Rc:CAT; X } X X fprintf(stderr,"%s: (I) at [%d] ended with rc = %i\n", X sCom_Sto->called_as, X pid, X sCom_Sto->W_Stat->w_retcode); X /* bail out in case wait returned > SEVERE */ X if (Rc > SEVERE) { X return (Rc); X } X } X else { X fprintf(stderr,"%s: (I) at-cmd [%d] ended normally\n", X sCom_Sto->called_as, X pid); X } X } X else { X fprintf(stderr,"%s: (E) at [%d] terminated by signal %i\n", X sCom_Sto->called_as, X pid, X sCom_Sto->W_Stat->w_termsig); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X } /* switch (pid = fork()) */ X return (Rc); } /* ----------------------------------------------------------------- X * check the site passed via -s or -S option to be a valid one and X * not to be our hostname. X * */ X int Check_Site(struct Common_Stor *sCom_Sto) { X X int i,j = 0; X sCom_Sto->Single_Site_Tab = 0; X if (strcmp(sCom_Sto->Single_Site,sCom_Sto->This_Site) == 0) { X fprintf(stderr,"%s: (E) won't call *ourself* %s\n", X sCom_Sto->called_as,sCom_Sto->Single_Site); X return(SEVERE); X } X for(i=0;i<=sCom_Sto->maxtab;i++) { X if ((j=strcmp(sCom_Sto->Sitetab[i].name,sCom_Sto->Single_Site)) >= 0) { X break; X } X } X if (j != 0) { X fprintf(stderr,"%s: (E) unknown site: %s\n", X sCom_Sto->called_as,sCom_Sto->Single_Site); X return(SEVERE); X } X sCom_Sto->Single_Site_Tab = i; X sCom_Sto->Sitetab[i].flag = TRUE; X if (sCom_Sto->force_any) { X sCom_Sto->Sitetab[i].force = TRUE; X sCom_Sto->force_any = FALSE; X } X return(OK); } X X /* ------------------------------------------------------------------ X * storage - get some memory X */ X void *storage(unsigned count, X char *location, X int *Rc, X struct Common_Stor *sCom_Sto) { X void *p; X X if( NULL == (p= malloc(count)) ) { X fprintf(stderr,"%s: (C) malloc failed (%s). Reason: %i (%s)\n", X sCom_Sto->called_as,location,errno,strerror(errno)); X *Rc = *Rc >= CAT ? *Rc:CAT; X } X return p; } X /* ------------------------------------------------------------------ X * if defined open the message log file otherwise all mesages will go X * to stderr. If UNAME_DIR is defined construct the command to be X * passed to popen(); if undefined the deafult will be used X * */ X int set_mlog(FILE **seclog, struct Common_Stor *sCom_Sto) { X X int Rc = 0; X #ifdef ALOG_FILE X if (!isatty(0)) { X if ((freopen(Msg_Log,"a",stderr)) == NULL) { X fprintf(stdout,"%s: (C) Could not open msglog: %s\n", X sCom_Sto->called_as,Msg_Log); X return (Rc >= CAT ? Rc:CAT); X } X } X else { X if ((*seclog = fopen(Msg_Log,"a")) == NULL) { X fprintf(stderr,"%s: (C) Could not open msglog: %s\n", X sCom_Sto->called_as,Msg_Log); X return (Rc >= CAT ? Rc:CAT); X } X } #endif /* ALOG_FILE */ X /* set up the pipe together with the complete path to uuname */ X #ifdef UNAME_DIR X if ((sCom_Sto->Usort = (char *)storage (sizeof(UNAME_DIR)+sizeof(subcmd), X "Sort",&Rc, sCom_Sto)) != NULL) { X strncpy(sCom_Sto->Usort,UNAME_DIR,strlen(UNAME_DIR)); /* paste in the path */ X strcat(sCom_Sto->Usort,subcmd); /* chain the pipe to it */ X } #else /* ! UNAME_DIR */ X sCom_Sto->Usort = &Sort; /* set pointer to uuname + sort */ #endif /* UNAME_DIR */ X X return (Rc); } X /* ------------------------------------------------------------------ X * obtain all active sitenames X * */ X int get_sites(struct Common_Stor *sCom_Sto) { X X int i = 0; X int n; X int Rc = 0; X FILE *infile, *statsfile; X X if ((infile=popen(sCom_Sto->Usort,"r")) != NULL) { X while(fgets(sCom_Sto->Sitetab[i].name,MAXHOSTNAMELEN+1,infile)) { X if (i > SITE_MAX) { /* let'm run so that we can give */ X i++; /* the user some guidance */ X continue; /* we'll tell the user later on */ X } X n = strlen(sCom_Sto->Sitetab[i].name)-1; /* offset: next to last char */ X sCom_Sto->Sitetab[i].name[n] = '\0'; /* strip trailing newline */ X sCom_Sto->Sitetab[i].flag = FALSE; /* TRUE: poll this site only*/ X sCom_Sto->Sitetab[i].force = FALSE; /* TRUE: force call */ X strcpy(sCom_Sto->Sitetab[i].grade,sCom_Sto->Grade); X sprintf(sCom_Sto->workf,"%s%s",STATUS_DIR,sCom_Sto->Sitetab[i].name); X if ((statsfile=fopen(sCom_Sto->workf,"r")) == NULL) { X fprintf(stderr,"%s: (W) no access to status file for: %s\n", X sCom_Sto->called_as,sCom_Sto->Sitetab[i].name); X Rc = Rc >= WARNING ? Rc:WARNING; X } X else { X fscanf(statsfile,"%d %d %ld %ld %s", X &sCom_Sto->Sitetab[i].stat_code, X &sCom_Sto->Sitetab[i].stat_retries, X &sCom_Sto->Sitetab[i].stat_lastcall, X &sCom_Sto->Sitetab[i].stat_delay, X sCom_Sto->workf); X X fclose(statsfile); X if ((sCom_Sto->Sitetab[i].stat_errtext = X (char *)storage (sizeof(sCom_Sto->workf), X "stat_errtext",&Rc, sCom_Sto)) == NULL) { X Rc = Rc >= WARNING ? Rc:WARNING; X } X else X strcpy(sCom_Sto->Sitetab[i].stat_errtext,sCom_Sto->workf); X } X sCom_Sto->maxtab = i++; /* set high-water-mark */ X } X if (ferror(infile) != 0) { X fprintf(stderr,"%s: (E) fgets() for sitenames failed reason-code: %i (%s)\n", X sCom_Sto->called_as,errno,strerror(errno)); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X pclose(infile); X X /* X * check for an empty table (strange but possible) X */ X X if (sCom_Sto->maxtab == 0) { X fprintf(stderr,"%s: (E) could not obtain sitenames.\n", X sCom_Sto->called_as); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X else { X X /* in case the internal table overflows we'll now give notice and tell X * the user by which amount the table has to be increased to hold all X * site-names X */ X X if (i > SITE_MAX) { X fprintf(stderr,"%s: (E) number of sites > internal tab\n", X sCom_Sto->called_as); X fprintf(stderr,"%s: (E) increase SITE_MAX to >= %d and recompile\n", X sCom_Sto->called_as,i); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X } /* sCom_Sto->maxtab == 0 */ X X } X else /* infile == NULL */ X { X fprintf(stderr,"%s: (E) could not sort sitenames. Reason-code: %i (%s)\n", X sCom_Sto->called_as,errno,strerror(errno)); X Rc = Rc >= SEVERE ? Rc:SEVERE; X X } /* if ((infile=popen(sCom_Sto->Usort,"r")) ... */ X X return (Rc); } SHAR_EOF chmod 0444 uupoll/autopoll.c || echo 'restore of uupoll/autopoll.c failed' Wc_c="`wc -c < 'uupoll/autopoll.c'`" test 44031 -eq "$Wc_c" || echo 'uupoll/autopoll.c: original size 44031, current size' "$Wc_c" fi # ============= uupoll/conf.h ============== if test -f 'uupoll/conf.h' -a X"$1" != X"-c"; then echo 'x - skipping uupoll/conf.h (File already exists)' else echo 'x - extracting uupoll/conf.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'uupoll/conf.h' && #ifndef CONF X #define CONF X /* $Id: conf.h,v 1.9 1994/04/14 17:24:58 kdburg Rel $ */ /* $Log: conf.h,v $ X * Revision 1.9 1994/04/14 17:24:58 kdburg X * added comment to the AT_OPTION X * X * Revision 1.8 1994/03/26 17:41:06 kdburg X * the location of uuname can now be specified. This was added due to X * the fact that cron (NeXT 3.2 and +) now obeys the path that was active X * during boot (either /.path or set within /etc/rc) so autopoll/uupoll X * always got the wrong uuname when called direct fron cron. This is X * not true when called via a script that does a 'su - user -c ...' X * X * Revision 1.7 1993/06/26 16:21:47 kdburg X * default location for logfiles changed X * X * Revision 1.6 1993/05/14 22:32:05 kdburg X * change to HAVE_SPOOLDIR_TAYLOR X * X * Revision 1.5 1993/05/09 13:16:53 kdburg X * make have-autopoll the default X * X * Revision 1.4 1993/05/08 23:17:34 kdburg X * cleanup and to reflect changes made to autopoll/uupoll X * X * Revision 1.3 1993/04/29 10:46:34 kdburg X * added def for STATUS_DIR X * X * Revision 1.2 1993/04/27 15:31:47 kdburg X * rearranged the defs; changed LOG_DIR to ALOG_DIR in case uupoll X * will have one too; we need then eventually 2 different dirs. X * X * Revision 1.1 1993/04/26 21:20:12 kdburg X * Initial revision X * */ X /* --------- combined config file for uupoll and autopoll */ /* --------- change the following defines to meet your needs */ X /* define the default grade to be inserted into the pollfile name */ #define DEF_GRADE "A" X /* Define the complete path to the uuname program. X * If undefined we'll use just the name 'uuname' to call it X * */ #define UNAME_DIR "/usr/local/bin/uuname" X /* define the path to the directory which does contain uucico */ #define CICO_DIR "/usr/local/lib/uucp/uucico" X /* define the path to the directory which holds all the uucp files. X * We'll place the poll file in one of it's subdirectories X * */ #define SPOOL_DIR "/usr/spool/uucp" X /* at least one of the follwing must be defined To use the second or X * third set of definitions, change the ``#if 1'' to ``#if 0'' X * and change the appropriate ``#if 0'' to ``#if 1''. X * */ #if 0 #define HAVE_SPOOLDIR_BSD #endif #if 0 #define HAVE_SPOOLDIR_HDB #endif #if 1 #define HAVE_SPOOLDIR_TAYLOR #endif X /* define the maximum number of sites in your config or L.sys */ #define SITE_MAX 100 X /* define the path to the directory which is to contain the X * message log created by uupoll and the file name itself. X * change the ``#if 1'' to ``#if 0'' to have the messages on stderr X * */ #if 1 #define ULOG_FILE "/Logfiles/poll.log" #endif X /* change if to 0 if you don't have autopoll installed. */ #if 1 #define AUTO_POLL #endif X /* The following defs are irrelevant if you don't have autopoll */ X /* define the options to be given to the at cmd (-s -c -m). The default X * is shown (use csh and send mail after execution) if AT_OPTION is X * undefined X * */ #define AT_OPTION "-mc" X /* Define the complete path to the autopoll program. X * This will assure that we get the one we want X * The path must be the same as given in Makefile (lbindir) X * */ #define AUTO_DIR "/usr/local/lib/uucp/autopoll" X /* define the path to the directory which is to contain the X * message log created by autopoll and the file name itself. X * change the ``#if 1'' to ``#if 0'' to have the messages on stderr X * */ #if 1 #define ALOG_FILE "/Logfiles/poll.log" #endif X /* define the full path to the directory which holds the status files X * The name should be given *except* the sitename. A trailing `/' if any X * must be given. X * Example: /usr/spool/uucp/.Status/sys.sitename X * then specify STATUS_DIR as X * "/usr/spool/uucp/.Status/sys." X * */ #define STATUS_DIR "/usr/spool/uucp/.Status/" #endif SHAR_EOF chmod 0444 uupoll/conf.h || echo 'restore of uupoll/conf.h failed' Wc_c="`wc -c < 'uupoll/conf.h'`" test 3884 -eq "$Wc_c" || echo 'uupoll/conf.h: original size 3884, current size' "$Wc_c" fi # ============= uupoll/uupoll.8c ============== if test -f 'uupoll/uupoll.8c' -a X"$1" != X"-c"; then echo 'x - skipping uupoll/uupoll.8c (File already exists)' else echo 'x - extracting uupoll/uupoll.8c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'uupoll/uupoll.8c' && .\" Copyright (c) 1986 Regents of the University of California. .\" All rights reserved. The Berkeley software License Agreement .\" specifies the terms and conditions for redistribution. .\" .\" @(#)uupoll.8c 6.1 (Berkeley) 4/24/86 .\" @(#)uupoll.8c 1.11 (incoahe) 5/09/1993 .\" .TH UUPOLL 8C "Mai 09, 1993" .UC 6 .SH NAME uupoll \- poll a remote \s-1UUCP\s+1 site .SH SYNOPSIS .B uupoll [ .BI \-g grade ] [ .B \-n ] [ .B \-x ] .I system ... ... .SH SUMMARY This version of .IR uupoll can be used to fully replace the vendor supplied .IR uupoll that comes with the NeXTStep OS. The original version (up to 3.1) had a X bug in that X an unknown site given as argument would yield in a `Bus error' condition. Using any other type of UUCP like Taylor-UUCP with the option of having a different file structure as well as a different L.sys will therefore make it necessary to do maintenance to the (unused) L.sys as well to keep .IR uupoll going. This one has been programmed from scratch due to the fact that no source code was available. Some enhancements have been incorporated into this version: .PP \- the default grade may now be compiled different from `A'. .PP \- the options may now be given in any order and the \-g option may be given more than once. Any option will be used immediately when encountered and will stay in effect unless reset; this does not apply to the \-x and \-n option which can't be reset. The processing of options is guaranteed to be from left to right so that some grouping may be achieved (see below). .PP \- .IR uupoll may be used to call any program instead of .IR uucico namely .IR autopoll to ease the task of rescheduling a failed call. .SH DESCRIPTION .I Uupoll is used to force a poll of a remote system. It queues a null job for the remote system, unless the \-x option has been given, and then invokes either .IR uucico (8C) or .IR autopoll (8C) or any other program depending on how .IR uupoll is customized. If used in conjunction with .IR autopoll the latter will then invoke .IR uucico. .SH OPTIONS The following options are available: .TP 8 .BI \-g grade Only send jobs of grade .I grade or higher on this call. The .I grade stays in effect until it is changed by a different \-g option. .TP 8 .B \-n Queue the null job, but do not invoke the program that actually calls the named site(s). The \-n option once given will apply to all sites following to the .IR right of it. .TP 8 .B \-x Do not place a null job for all following sites. This option must be given before the \-n option. The \-n option will nullify this. Any grade in effect will not be honored because .I uucico (Taylor) does not carry the \-g option at the moment. .PP .I Uupoll is usually run by .IR cron (5) or by a user who wants to hurry a job along. A typical entry in .I crontab could be: .PP .nf X 0 0,8,16 * * * uucp /usr/bin/uupoll ihnp4 X 0 4,12,20 * * * uucp /usr/bin/uupoll ucbvax .fi This will poll .B ihnp4 at midnight, 0800, and 1600, and .B ucbvax at 0400, noon, and 2000. .PP If the local machine is already running .I uucico every hour and has a limited number of outgoing modems, a better approach might be: .PP .nf X 0 0,8,16 * * * uucp /usr/bin/uupoll -n ihnp4 X 0 4,12,20 * * * uucp /usr/bin/uupoll -n ucbvax X 5 * * * * uucp /usr/lib/uucp/uucico -r1 -D -C .fi This will queue null jobs for the remote sites at the top of the hour; they will be processed by .I uucico when it runs five minutes later (the -C option apply to Taylor uucp-1.05 only, the -D option applies to Talor uucp-1.04 and up) .SH EXTENDED options An example of the options and how they interact is given below. The working order while processing the options is left to right: .nf X uupoll -gC site1 -gB site2 -x site3 -n -gA site4 site5 .fi .PP this poll will: .PP - call immediate site1 with grade C or higher and will place a null job .PP - call immediate site2 with grade B or higher and will place a null job .PP - call immediate site3 with grade B or higher without placing a null job .PP - just placing a null job for site4 and site5 with grade A or higher. These sites will be called at the next regular schedule. .SH BUGS When more than one site is given on the command line and no \-n option is given there will be an immediate invocation of .IR uucico or .IR autopoll for .IR all sites given. That may lead to a `No port available' condition. .SH FILES .ta \w'/usr/spool/uucp/ 'u .nf /etc/uucp/ UUCP internal files/utilities /usr/spool/uucp/ Spool directory /tmp/poll.log This file is present only if uupoll has been X compiled to place the messages into a file. X Otherwise all messages will go to stderr. X The directory as well as the name may be X different. The name may be defined at compile time. .fi .SH SEE ALSO uucp(1C), uux(1C), uucico(8C), autopoll(8C) SHAR_EOF chmod 0444 uupoll/uupoll.8c || echo 'restore of uupoll/uupoll.8c failed' Wc_c="`wc -c < 'uupoll/uupoll.8c'`" test 4787 -eq "$Wc_c" || echo 'uupoll/uupoll.8c: original size 4787, current size' "$Wc_c" fi # ============= uupoll/uupoll.c ============== if test -f 'uupoll/uupoll.c' -a X"$1" != X"-c"; then echo 'x - skipping uupoll/uupoll.c (File already exists)' else echo 'x - extracting uupoll/uupoll.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'uupoll/uupoll.c' && /* ---------------------------------------------------------------------------* X X Name: uupoll X X Author: Klaus Dahlenburg X X Status: Public domain X X Copyright: none X X Funktion: The main intention behind this program was to get a full X replacement of the uupoll supplied by NeXT when using an X UUCP or a file structure that is different from that hardwired X config in NeXT's uupoll. The lack of source made it impossible X to modify the supplied uupoll. X X Call: uupoll [-n] [-x] [-g[A | 0-9,A-Z,a-z]] site ... X X -n just place a poll file but do not call uucico; X This option can be given only once. X -x meaningful only for sites not affected by the -n X option. It prevents the creation of a poll file; X the default is to place one. In case the poll fails X there will be no attempt to poll those sites on X the next general (unspecific) poll. If using X autopoll the site will be called at the next + 1 X run of autopoll. X -g any grade may be given to meet the criteria for X a successful poll. The default being specified X in conf.h (A). X This option may be given individually for each X site to call. X site the name of the site to be called. As many sites X as necessary may be specified separated by at least X one blank. X Note: any site will be called with the options currently in X effect. The working order is left to right. Example: X uupoll -gQ site1 site2 -gZ site3 -n site4 X site1 and site2 will be called immediate with grade Q X site3 will be called immediate with grade Z. Site4 will X have a poll file created with grade Z. X X Environment: NeXT 2.1 X X Called Programs: sort, uniq, uucico (or autopoll), uuname X X Compile: no special options are needed X X Comments: - should run setuid UUCP or whatever userid is necessary to X write to the spool directory with the proper ownership of X the files and to run uucico. X - No alias expansion is done on the given names. */ X #if !defined(lint) static char rcsid[] = "$Id: uupoll.c,v 2.7 1994/04/14 17:22:04 kdburg Rel $"; #endif /* not lint */ X /* $Log: uupoll.c,v $ X * Revision 2.7 1994/04/14 17:22:04 kdburg X * major rework done X * X * Revision 2.6 1994/03/26 17:38:41 kdburg X * added support for UNAME_DIR; cleanup of some code; adjusted code after X * obtaining sitenames via popen() X * X * Revision 2.5 1994/03/24 19:01:24 kdburg X * some minor changes; some calls had their rc not checked X * X * Revision 2.4 1993/07/08 07:56:26 kdburg X * befor invoking autopoll stdin is now closed to avoid blocking of X * terminal X * X * Revision 2.3 1993/07/05 19:43:00 kdburg X * when used interactivly only the start msg is put into the msg-log X * so far defined (UULOG) X * X * Revision 2.2 1993/05/20 18:50:52 kdburg X * no execute permission to the poll-pgm (uucico/autopoll) was not X * reflected in the log; when to start message was not given when -x X * option was present X * X * Revision 2.1 1993/05/16 21:48:15 kdburg X * changed exit() to _exit() in case the exec fails within child X * X * Revision 2.0 1993/05/16 14:11:04 kdburg X * initial revision X * */ X #define CAT 16 #define SEVERE 8 #define WARNING 4 #define OK 0 #define P_MODE 00647 /* file-mode for poll-file */ /* Boolean types */ typedef int bool; #undef TRUE #undef FALSE #define TRUE (1) #define FALSE (0) X #include "conf.h" #include #include #include #include #include #include #include #include X #define X_OK 1 /* access: executable ? */ X #ifdef ALOG_FILE X static char Msg_Log[] = ALOG_FILE; /* name of msglog filename */ #endif X #ifdef UNAME_DIR X static char subcmd[] = " | sort | uniq"; /* pipe that follows uuname */ #else /* ! UNAME_DIR */ X static char Sort[] = "uuname | sort | uniq"; /* default to obtain site names */ #endif /*UNAME_DIR */ X static char cGrade[] = DEF_GRADE; /* grade as defined in conf.h */ static char dGrade[] = "A"; /* use this if DEF_GRADE is invalid */ #ifdef AUTO_POLL X static char Auto_Dir[] = AUTO_DIR; /* autopoll lives here */ #else X static char Cico_Dir[] = CICO_DIR; /* and here lives cico */ #endif /* AUTO_POLL */ X struct Sites { X char name[MAXHOSTNAMELEN+1]; /* name of site as supplied by uuname */ X char grade[1]; /* as passed or defaulted */ X bool flag; /* TRUE this site should be polled */ X int asap; /* 1 without -n; 2 with -x option */ }; X struct Common_Stor { X int maxtab; /* high-water-mark for site tab */ X char *Grade; /* use this as grade for calls */ X char *Poll_Pgm; /* our name without path */ X char *called_as; /* but called by this name */ X int our_pid; /* our process-id */ X char *Uucico; /* cico's name without path */ X char This_Site[MAXHOSTNAMELEN+1]; /* our site name */ X char System[MAXHOSTNAMELEN+1]; /* intermediate to hold sitename */ X char *Usort; /* will hold uuname + subcmd */ X struct passwd *pwd; X struct timeval tp; X struct timezone tzp; X struct Sites Sitetab[SITE_MAX]; X char workf[300]; X }; X /* define the prototypes X * */ X int set_mlog(FILE **seclog, struct Common_Stor *); int get_sites(struct Common_Stor *); int Check_Args(int argc, char *argv[], struct Common_Stor *); int Housekeeping(int argc, char *argv[], struct Common_Stor *); int Call_Site(struct Common_Stor *); void *storage(unsigned count, char *errloc, int *Rc, struct Common_Stor *); X extern int getpid(); extern void free(void *ptr); extern int access(char *path, int mode); extern int gethostname(char *name, int namelen); extern int system(char *cmd); extern int fork(); extern int execlp(char *name, char *arg0, ...); extern void *malloc(size_t byteSize); extern int getuid(); extern int isatty(int); extern char *ttyname(int); extern int open(char *path, int flags, int mode); extern int close(int fd); #ifdef __STRICT_ANSI__ extern FILE *popen(char *command, char *type); extern int pclose(FILE *stream); extern void _exit(int status); #endif /* __STRICT_ANSI__ */ #ifdef __STRICT_BSD__ extern int fprintf(FILE *stream, const char *format, ...); extern int fclose(FILE *stream); extern char *strerror(int errnum); extern int fflush(FILE *stream); extern void exit(int status); #endif /* __STRICT_BSD__ */ X /* --------------------------------------------------------------------------*/ /* Main */ /* --------------------------------------------------------------------------*/ X int main(int argc, char *argv[]) { X X struct Common_Stor *sCom_Sto; X int Maxrc = OK; /* Max err-code encountered so far */ X int k = 0; X X if ( NULL == (sCom_Sto = malloc(sizeof(struct Common_Stor))) ) { X fprintf(stderr,"%s: (C) malloc failed (%s). Reason: %i (%s)\n", X argv[0],"Common_Stor",errno,strerror(errno)); X exit (CAT); X } X X Maxrc = Housekeeping(argc, argv, sCom_Sto); X /* If any errors popped up so far they are of such a nature that it is very X * questionable to continue; so we better bail out in this case. X */ X if (Maxrc <= WARNING) { X k = Call_Site(sCom_Sto); X Maxrc = Maxrc >= k ? Maxrc:k; X } X k = gettimeofday(&sCom_Sto->tp, &sCom_Sto->tzp); X fprintf(stderr,"%s: (I) ended with rc = %i on %s\n", X sCom_Sto->called_as, X Maxrc,k!=0 ? "time unavailable":ctime(&sCom_Sto->tp.tv_sec)); X fclose(stderr); X free(sCom_Sto); X sCom_Sto = NULL; X exit (Maxrc); } X /* --------------------------------------------------------------------------*/ /* Functions */ /* --------------------------------------------------------------------------*/ X /* -------------------------------------------------------------------- X * housekeeping X */ X int Housekeeping(argc, argv, sCom_Sto) X int argc; X char *argv[]; X struct Common_Stor *sCom_Sto; { X X FILE *seclog = NULL; X int Rc = OK; X int Rci = OK; /* intermediate rc as returnd by functions */ X X sCom_Sto->our_pid = getpid(); X /* X * get our name sans path X * */ X X sCom_Sto->called_as = argv[0] + strlen(*argv); X for(;sCom_Sto->called_as >= argv[0] && *--sCom_Sto->called_as != '/';) X ; X sCom_Sto->called_as++; X /* if defined set up the name of the message log file otherwise X * stderr will be used. Setup the cmd string to obtain all known sitenames X * which will be sorted in ascending order with duplicates removed X * */ X X Rc = set_mlog(&seclog, sCom_Sto); X if (Rc > WARNING) X return (Rc); X /* put out the started message including the time and the userid. X * */ X X sCom_Sto->pwd = getpwuid(getuid()); X X if ((gettimeofday(&sCom_Sto->tp, &sCom_Sto->tzp)) != 0) { /* unacceptable error */ X fprintf(stderr,"%s: (C) gettimeofday() failed. Reason: %i (%s)\n", X sCom_Sto->called_as,errno,strerror(errno)); X return (Rc >= CAT ? Rc:CAT); X } X X if (seclog != NULL) { X fprintf(seclog,"\n%s: (I) started by `%s' (%s) on %s", X sCom_Sto->called_as, X (sCom_Sto->pwd==NULL) ? "???":sCom_Sto->pwd->pw_name, X ttyname(0), X ctime(&sCom_Sto->tp.tv_sec)); X fclose(seclog); X } X fprintf(stderr,"\n%s: (I) started by `%s' on %s", X sCom_Sto->called_as, X (sCom_Sto->pwd==NULL) ? "???":sCom_Sto->pwd->pw_name, X ctime(&sCom_Sto->tp.tv_sec)); X /* set up the default grade X * */ X X sCom_Sto->Grade = dGrade; /* set default for now */ X if (strlen(cGrade) != 1) { X fprintf(stderr,"%s: (W) grade %s invalid; default `%s' used\n", X sCom_Sto->called_as,cGrade,sCom_Sto->Grade); X Rc = Rc >= WARNING ? Rc:WARNING; X } X else X sCom_Sto->Grade = cGrade; /* Ok, take the one from conf.h */ X /* get the program to actually call the site. This is either UUCICO X * or AUTOPOLL or something completely different. X * */ X #ifdef AUTO_POLL X sCom_Sto->Uucico = Auto_Dir + strlen(Auto_Dir); X for(;sCom_Sto->Uucico >= Auto_Dir && *--sCom_Sto->Uucico != '/';) X ; X sCom_Sto->Uucico++; #else /* ! AUTO_POLL */ X sCom_Sto->Uucico = Cico_Dir + strlen(Cico_Dir); X for(;sCom_Sto->Uucico >= Cico_Dir && *--sCom_Sto->Uucico != '/';) X ; X sCom_Sto->Uucico++; #endif /* AUTO_POLL */ X /* get the path to ourself. X * */ X X sCom_Sto->Poll_Pgm = argv[0] + strlen(argv[0]); X for(;sCom_Sto->Poll_Pgm >= argv[0] && *--(sCom_Sto->Poll_Pgm) != '/';) X ; X sCom_Sto->Poll_Pgm++; X /* obtain our sitename X * */ X X if ((gethostname(sCom_Sto->This_Site,MAXHOSTNAMELEN+1)) != 0) { X fprintf(stderr,"%s: (W) hostname could not be obtained\n", X sCom_Sto->called_as); X Rc = (Rc >= WARNING) ? Rc:WARNING; X } X /* obtain all known sitenames X * */ X X Rci = get_sites(sCom_Sto); X Rc = Rci > Rc ? Rci:Rc; X /* check the arguments that we are called with X * */ X X Rci = Check_Args(argc, argv, sCom_Sto); X Rc = Rci > Rc ? Rci:Rc; X X return (Rc); } X /* -------------------------------------------------------------------- X * check all relevant arguments that have been passed to us. Those args X * that may be needed for a recall will be copied to a workfield. X * */ X int Check_Args(int argc, char *argv[], struct Common_Stor *sCom_Sto) { X int i,s,k,n = 0; X int Rc = OK; X int One_Site = 0; /* TRUE: found at least one valid site to call */ X int poll_file = 1; /* FALSE: after -x option given */ X int def_flag = 0; /* TRUE: when option -n was encountered */ X X /* --------------------------------------------------------------*/ X /* check the arguments passed to us */ X /* */ X /* These are: -n -> place a POLL file but do not start uucico */ X /* -x -> do not place a poll file (immed. poll only) */ X /* -g? -> specify a grade with the POLL file. The ? */ X /* may be: 0-9, A-Z, a-z */ X /* (validity not checked!) */ X /* site name of the site to call. There many be as */ X /* many as necessary separated by at least one */ X /* blank */ X /* Note: all options will stay in effect as long as they are'nt */ X /* changed by a new setting. The options -n and -x can't */ X /* be negated once given; that means place all sites */ X /* that should be immediately polled to the left of the */ X /* -n option; the same applies to the -x option which must */ X /* be left of the -n option to come into effect! */ X /* The working order is left to right! */ X /* --------------------------------------------------------------*/ X X for (i = 1, s = 0; i < argc; i++) { X k = strlen(argv[i]); X switch (*argv[i]) { X X /* ----> handle the options */ X X case '-': X n = 1; X switch (*(argv[i]+n)) { X case 'n': X if (k > 2) { X fprintf(stderr,"%s: (E) invalid specification %s\n", X sCom_Sto->called_as,argv[i]); X Rc = Rc >= SEVERE ? Rc:SEVERE; X break; X } X def_flag = 1; X break; X case 'x': X if (k > 2) { X fprintf(stderr,"%s: (E) invalid specification %s\n", X sCom_Sto->called_as,argv[i]); X Rc = Rc >= SEVERE ? Rc:SEVERE; X break; X } X if (def_flag) { X fprintf(stderr,"%s: (W) -x after -n has no effect\n", X sCom_Sto->called_as); X Rc = Rc >= WARNING ? Rc:WARNING; X } X else { X poll_file = 0; X } X break; X case 'g': X if (k > 3) { X fprintf(stderr,"%s: (E) invalid specification %s\n", X sCom_Sto->called_as,argv[i]); X Rc = Rc >= SEVERE ? Rc:SEVERE; X break; X } X if (*(argv[i]+n+1) == '\0') { X fprintf(stderr,"%s: (E) missing grade\n", X sCom_Sto->called_as); X Rc = Rc >= SEVERE ? Rc:SEVERE; X break; X } X if (isalnum(*(argv[i]+n+1)) == 0) { X fprintf(stderr,"%s: (E) invalid grade %s\n", X sCom_Sto->called_as,argv[i]); X Rc = Rc >= SEVERE ? Rc:SEVERE; X break; X } X strcpy(sCom_Sto->Grade,(argv[i]+n+1)); X break; X default: X fprintf(stderr,"%s: (W) missing/unknown option `-%s' ignored\n", X sCom_Sto->called_as,argv[i]+n); X Rc = Rc >= WARNING ? Rc:WARNING; X break; X } /* end of switch (*(argv[i]+n)) */ X break; X X /* ----> handle the sitenames */ X X default: X if (strcmp(argv[i],sCom_Sto->This_Site) == 0) { X fprintf(stderr,"%s: (W) ignoring to call *ourself* %s\n", X sCom_Sto->called_as,argv[i]); X Rc = Rc >= WARNING ? Rc:WARNING; X break; X } X strcpy(sCom_Sto->System,argv[i]); X for(s=0;s<=sCom_Sto->maxtab;s++) { X if ((n=strcmp(sCom_Sto->Sitetab[s].name,sCom_Sto->System)) >= 0) { X break; X } X } X if (n != 0) { X fprintf(stderr,"%s: (W) unknown site (ignored): %s\n", X sCom_Sto->called_as,sCom_Sto->System); X Rc = Rc >= WARNING ? Rc:WARNING; X break; X } X X /* ----> if there was no error we arrive here to save the data */ X X strcpy(sCom_Sto->Sitetab[s].grade,sCom_Sto->Grade); X One_Site = sCom_Sto->Sitetab[s].flag = 1; /* poll this site */ X if (def_flag) X sCom_Sto->Sitetab[s].asap = 0; /* poll on next schedule */ X else { X sCom_Sto->Sitetab[s].asap = 1; /* poll immediately */ X if (! poll_file) X sCom_Sto->Sitetab[s].asap++; /* and do not place a poll file */ X } X s++; X break; X } /* end of switch (*argv[i]) */ X } /* end of for(...) */ X /* now let's check what we've gotten so far. If no valid data has been */ /* entered we will indicate that to prevent further processing */ X X if (! One_Site) { X fprintf(stderr,"%s: (E) found no site to call\n", X sCom_Sto->called_as); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X return (Rc); } X int Call_Site(struct Common_Stor *sCom_Sto) { X /* For all sites that carry the -n flag we will place */ /* a poll file into the appropriate directory. For all others there will */ /* be an immediate call to uucico (or autopoll) */ /* Those sites that have been named on the command have the corresponding */ /* flag byte set to one. */ X X int fdpoll; /* fildes for the poll file */ X int mode = P_MODE; /* mode for poll file */ X int i = 0; X int Rc = OK; X int pid = 0; /* process-id after fork() */ X X for(i=0;(i<=sCom_Sto->maxtab);i++) { X if (sCom_Sto->Sitetab[i].flag == 0) /* should we trigger this one ? */ X continue; /* nope */ X /* processing done for delayed polls only */ X X if (sCom_Sto->Sitetab[i].asap <= 1) { /* do not place a poll file */ X /* for sites that will be polled */ X /* immediate and carry the -x option */ #ifdef HAVE_SPOOLDIR_TAYLOR X sprintf(sCom_Sto->workf,"%s/%s/C./C.%sPOLL", X SPOOL_DIR, X sCom_Sto->Sitetab[i].name, X sCom_Sto->Sitetab[i].grade); #endif #ifdef HAVE_SPOOLDIR_HDB X sprintf(sCom_Sto->workf,"%s/%s/C.%s%sPOLL", X SPOOL_DIR, X sCom_Sto->Sitetab[i].name, X sCom_Sto->Sitetab[i].name, X sCom_Sto->Sitetab[i].grade); #endif #ifdef HAVE_SPOOLDIR_BSD X sprintf(sCom_Sto->workf,"%s/C./C.%s%sPOLL", X SPOOL_DIR, X sCom_Sto->Sitetab[i].name, X sCom_Sto->Sitetab[i].grade); #endif X X fflush(stderr); X if ((fdpoll=open(sCom_Sto->workf,O_CREAT,mode)) <= 0) { X fprintf(stderr,"%s: (E) couldn't place poll file for system: %s. Reason: %s\n", X sCom_Sto->called_as, X sCom_Sto->Sitetab[i].name, X strerror(errno)); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X else { X if (close(fdpoll) != 0) { X fprintf(stderr,"%s: (W) close failed for poll file; system: %s. Reason: %s\n", X sCom_Sto->called_as, X sCom_Sto->Sitetab[i].name, X strerror(errno)); X Rc = Rc >= WARNING ? Rc:WARNING; X } X } X } X /* the following processing is done for immediate polls only X * there is no wait for the completion of the called program that actually X * calls the site X * */ X X fflush(stderr); X if (Rc <= WARNING) { X fprintf(stderr,"%s: (I) site %s will be called %s\n", X sCom_Sto->called_as, X sCom_Sto->Sitetab[i].name, X sCom_Sto->Sitetab[i].asap == 0 ? X "upon next poll":"immediately"); X if (sCom_Sto->Sitetab[i].asap >= 1) X { #ifdef AUTO_DIR X if ( access(Auto_Dir,X_OK) != 0) /* do we have xecute permission ? */ #else /* ! AUTO_DIR */ X if ( access(Cico_Dir,X_OK) != 0) /* do we have xecute permission ? */ #endif /* AUTO_DIR */ X { X fprintf(stderr,"%s: (C) could not start %s. Reason-code: %i (%s)\n", X sCom_Sto->called_as, X sCom_Sto->Uucico, X errno,strerror(errno)); X return (Rc >= CAT ? Rc:CAT); /* abandon the run */ X } X switch (pid = fork()) X { X case -1: X fprintf(stderr,"%s: (C) could not fork() Reason-code: %i (%s)\n", X sCom_Sto->called_as, X errno,strerror(errno)); X return (Rc >= CAT ? Rc:CAT); X case 0: X if (isatty(0)) X close(0); /* don't block the terminal by autopoll */ #ifdef AUTO_DIR X execlp(Auto_Dir, #else /* ! AUTO_DIR */ X execlp(Cico_Dir, #endif /*AUTO_DIR */ X sCom_Sto->Uucico, X "-D", "-r1", "-s", X sCom_Sto->Sitetab[i].name,0); X fprintf(stderr,"%s: (C) could not start %s. Reason-code: %i (%s)\n", X sCom_Sto->called_as, X sCom_Sto->Uucico, X errno,strerror(errno)); X _exit (CAT); /* child: bail out */ X default: X fflush(stderr); X fprintf(stderr,"%s: (I) %s [%d] started; site: %s\n", X sCom_Sto->called_as, X sCom_Sto->Uucico, X pid, X sCom_Sto->Sitetab[i].name); X } /* switch (pid = fork()) */ X } /* if (Sitetab ...) */ X } /* if (Rc ...) */ X } /* for(i=0;(i<= ...)) */ X return (Rc); } X X /* ------------------------------------------------------------------ X * storage - get some memory X */ X void *storage(unsigned count, X char *location, X int *Rc, X struct Common_Stor *sCom_Sto) { X void *p; X X if( NULL == (p= malloc(count)) ) { X fprintf(stderr,"%s: (C) malloc failed (%s). Reason: %i (%s)\n", X sCom_Sto->called_as,location,errno,strerror(errno)); X *Rc = *Rc >= CAT ? *Rc:CAT; X } X return p; } X /* ------------------------------------------------------------------ X * if defined open the message log file otherwise all mesages will go X * to stderr. If UNAME_DIR is defined construct the command to be X * passed to popen(); if undefined the default will be used X * */ X int set_mlog(FILE **seclog, struct Common_Stor *sCom_Sto) { X X int Rc = 0; X #ifdef ALOG_FILE X if (!isatty(0)) { X if ((freopen(Msg_Log,"a",stderr)) == NULL) { X fprintf(stdout,"%s: (C) Could not open msglog: %s\n", X sCom_Sto->called_as,Msg_Log); X return (Rc >= CAT ? Rc:CAT); X } X } X else { X if ((*seclog = fopen(Msg_Log,"a")) == NULL) { X fprintf(stderr,"%s: (C) Could not open msglog: %s\n", X sCom_Sto->called_as,Msg_Log); X return (Rc >= CAT ? Rc:CAT); X } X } #endif /* ALOG_FILE */ X /* set up the pipe together with the complete path to uuname */ X #ifdef UNAME_DIR X if ((sCom_Sto->Usort = (char *)storage (sizeof(UNAME_DIR)+sizeof(subcmd), X "Sort",&Rc, sCom_Sto)) != NULL) { X strncpy(sCom_Sto->Usort,UNAME_DIR,strlen(UNAME_DIR)); /* paste in the path */ X strcat(sCom_Sto->Usort,subcmd); /* chain the pipe to it */ X } #else /* ! UNAME_DIR */ X sCom_Sto->Usort = &Sort; /* set pointer to uuname + sort */ #endif /* UNAME_DIR */ X X return (Rc); } X /* ------------------------------------------------------------------ X * obtain all active sitenames X * */ X int get_sites(struct Common_Stor *sCom_Sto) { X X int i = 0; X int n; X int Rc = 0; X FILE *infile; X X if ((infile=popen(sCom_Sto->Usort,"r")) != NULL) { X while(fgets(sCom_Sto->Sitetab[i].name,MAXHOSTNAMELEN+1,infile)) { X if (i > SITE_MAX) { /* let'm run so that we can give */ X i++; /* the user some guidance */ X continue; /* we'll tell the user later on */ X } X n = strlen(sCom_Sto->Sitetab[i].name)-1; /* offset: next to last char */ X sCom_Sto->Sitetab[i].name[n] = '\0'; /* strip trailing newline */ X sCom_Sto->Sitetab[i].flag = FALSE; /* TRUE: poll this site */ X sCom_Sto->Sitetab[i].asap = FALSE; /* TRUE: immediate poll */ X strcpy(sCom_Sto->Sitetab[i].grade,sCom_Sto->Grade); X sprintf(sCom_Sto->workf,"%s%s",STATUS_DIR,sCom_Sto->Sitetab[i].name); X sCom_Sto->maxtab = i++; /* set high-water-mark */ X } X if (ferror(infile) != 0) { X fprintf(stderr,"%s: (E) fgets() for sitenames failed reason-code: %i (%s)\n", X sCom_Sto->called_as,errno,strerror(errno)); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X pclose(infile); X X /* X * check for an empty table (strange but possible) X */ X X if (sCom_Sto->maxtab == 0) { X fprintf(stderr,"%s: (E) could not obtain sitenames.\n", X sCom_Sto->called_as); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X else { X X /* in case the internal table overflows we'll now give notice and tell X * the user by which amount the table has to be increased to hold all X * site-names X */ X X if (i > SITE_MAX) { X fprintf(stderr,"%s: (E) number of sites > internal tab\n", X sCom_Sto->called_as); X fprintf(stderr,"%s: (E) increase SITE_MAX to >= %d and recompile\n", X sCom_Sto->called_as,i); X Rc = Rc >= SEVERE ? Rc:SEVERE; X } X } /* sCom_Sto->maxtab == 0 */ X X } X else /* infile == NULL */ X { X fprintf(stderr,"%s: (E) could not sort sitenames. Reason-code: %i (%s)\n", X sCom_Sto->called_as,errno,strerror(errno)); X Rc = Rc >= SEVERE ? Rc:SEVERE; X X } /* if ((infile=popen(sCom_Sto->Usort,"r")) ... */ X X return (Rc); } SHAR_EOF chmod 0444 uupoll/uupoll.c || echo 'restore of uupoll/uupoll.c failed' Wc_c="`wc -c < 'uupoll/uupoll.c'`" test 27587 -eq "$Wc_c" || echo 'uupoll/uupoll.c: original size 27587, current size' "$Wc_c" fi exit 0 uucp-1.07/contrib/uuq.sh0000775000076400007640000000602007665321760010733 #!/bin/sh # # uuq - a script to examine and display the Taylor spool directory contents. # note - uses the uuname script or similar functionality. # Zacharias Beckman SPOOLDIR="/usr/spool/uucp" SYSTEMS=`uuname` TMPFILE="/tmp/uuq.tmp" FORSYSTEM="" DELETE="" LONG=0 SINGLE=0 while [ "$1" != "" ] do case $1 in -l) LONG=1 shift ;; -s) shift SYSTEMS=$argv[1] SINGLE=1 shift ;; -d) shift DELETE=$argv[1] shift ;; -h) echo "uuq: usage uuq [options]" echo " -l long listing (may take a while)" echo " -s n run uuq only for system n" echo " -d n delete item n from the queue (required -s)" exit 1 ;; *) echo "uuq: invalid option" exit 1 ;; esac done if [ "${DELETE}" != "" ] && [ ${SINGLE} != 1 ] ; then echo "uuq: you must specify a system to delete the job from:" echo " uuq -s wizard -d D.0004" exit 1 fi cd ${SPOOLDIR} # if we are deleting a job, then do that first and exit without showing # any other queue information if [ "${DELETE}" != "" ] ; then if [ -d ${SYSTEMS}/D. ] ; then cd ${SYSTEMS}/C. PACKET=${DELETE} if [ -f ${PACKET} ] ; then EXFILE=../D.X/`awk '{if (NR == 2) print $2}' ${PACKET}` DFILE=../D./`awk '{if (NR == 1) print $2}' ${PACKET}` echo "deleting job ${PACKET}" rm ${PACKET} rm ${EXFILE} rm ${DFILE} else echo "uuq: job ${PACKET} not found" exit 1 fi else echo "uuq: system ${SYSTEMS} not found" fi exit 1 fi # use the 'uuname' script to obtain a list of systems for the 'sys' file, # then step through each directory looking for appropriate information. if [ ${LONG} -gt 0 ] ; then echo "system" echo -n "job# act size command" fi for DESTSYSTEM in ${SYSTEMS} ; do # if there is an existing directory for the named system, cd into it and # "do the right thing." if [ -d ${DESTSYSTEM} ] ; then cd ${DESTSYSTEM}/C. PACKET=`ls` if [ "${PACKET}" != "" ] ; then # if a long listing has been required, extra information is printed echo "" echo "${DESTSYSTEM}:" # now each packet must be examined and appropriate information is # printed for this system if [ ${LONG} -gt 0 ] ; then for PACKET in * ; do EXFILE=../D.X/`awk '{if (NR == 2) print $2}' ${PACKET}` DFILE=../D./`awk '{if (NR == 1) print $2}' ${PACKET}` echo -n "${PACKET} " > ${TMPFILE} gawk '{if (NR == 2) printf(" %s ", $1);}' ${PACKET} >> ${TMPFILE} ls -l ${DFILE}|awk '{printf("%-10d ", $4)}' >> ${TMPFILE} if [ -f ${EXFILE} ] ; then gawk '/U / {printf("(%s)", $2);}\ /C / {print substr($0,2,length($0));}' ${EXFILE} >> ${TMPFILE} else echo "---" >> ${TMPFILE} fi cat ${TMPFILE} done cat ${SPOOLDIR}/.Status/${DESTSYSTEM} else ls fi fi fi cd ${SPOOLDIR} done uucp-1.07/contrib/uurate.c0000664000076400007640000017256507665321760011255 /* * @(#)uurate.c 1.2 - Thu Sep 3 18:32:46 1992 * * This program digests log and stats files in the "Taylor" format * and outputs various statistical data to standard out. * * Author: * Bob Denny (denny@alisa.com) * Fri Feb 7 13:38:36 1992 * * Original author: * Mark Pizzolato mark@infopiz.UUCP * * Edits: * Bob Denny - Fri Feb 7 15:04:54 1992 * Heavy rework for Taylor UUCP. This was the (very old) uurate from * DECUS UUCP, which had a single logfile for activity and stats. * Personally, I would have done things differently, with tables * and case statements, but in the interest of time, I preserved * Mark Pizzolato's techniques and style. * * Bob Denny - Sun Aug 30 14:18:50 1992 * Changes to report format suggested by Francois Pinard and others. * Add summary report, format from uutraf.pl (perl script), again * thanks to Francois. Integrate and checkout with 1.03 of Taylor UUCP. * * Stephan Niemz - Fri Apr 9 1993 * - Print totals in summary report, * - show all commands in execution report, * - count incoming calls correctly, * - suppress empty tables, * - don't divide by zero in efficiency report, * - limit the efficiency to 100% (could be more with the i-protocol), * - suppress some zeros to improve readability, * - check for failure of calloc, * - -h option changed to -s for consistency with all other uucp commands * (but -h was left in for comptibility). * * Scott Boyd - Thu Aug 26 13:21:34 PDT 1993 * - Changed hosts linked-list insertion routine so that hosts * are always listed in alphabetical order on reports. * * Klaus Dahlenburg - Fri Jun 18 1993 (1.2.2) * - redesigned the printed layout (sticked to those 80 column tubes). * - 'Retry time not ...' and ' ERROR: All matching ports ...' will now be * counted as calls and will raise the failed-call counter. * - times now shown as hh:mm:ss; the fields may hold up to 999 hrs * (a month equals 744 hrs at max). Printing will be as follows: * * hrs > 0 hh:mm:ss * min > 0 mm:ss * sec > 0 ss * leading zeroes are suppressed. * * - the bytes xfered will be given in thousands only (we're counting * so 1K is 1000 bytes!). Sums up to 9,999,999.9 thousand can be shown. * - dropped the fractions of a byte in columns: bytes/second (avg cps). * - File statistic changed to display in/out in one row instead of 2 * separate reports. * - eliminated the goto in command report and tightened the code; also * the 'goto usage' has been replaced by a call to void usage() with no * return (exit 1). * - a totaling is done for all reports now; the total values are held * within the structure; after finishing read there will be an alloc * for a site named 'Total' so that the totals line will be printed * more or less automatically. * - option -t implemented: that is every possible report will be given. * - the start and end date/time of the input files are printed; can be * dropped by the -q option at run time. * - it is now possible to use Log/Stats files from V2_LOGGING configs. * They must however not be mixed together (with each other). * - the Log/Stats files are taken from config which is passed via * Makefile at compile time. Variable to set is: newconfigdir. If the * default config can't be read the default values are used * (the config is optional!). * Note: keyword/filename must be on the same line (no continuation). * - -I option implemented to run with a different config file. In case * the file can't be opened the run is aborted! * - -q option implemented to run without environment report (default is * FALSE: print the report). * - -p option added to print protocol statistics: one for the packets * and one for the errors encountered * - reapplied patch by Scott Boyd that I did not * get knowledge of * * Ed Doolittle - Sun Aug 8 1999 * - shrunk compact summary to fit in 78 characters (time strings only * require 8 characters, not 9) */ /* $Log: uurate.c,v $ * Revision 1.15 1994/04/07 21:47:11 kdburg * printed 'no data avail' while there was data; layout chnaged * (cosmetic only) * * Revision 1.14 1994/04/07 21:16:32 kdburg * the layout of the protocol-used line within the LOGFILE changed * from 1.04 to 1.05; both formats may be used together; output * changed for packet report (columns adjusted) * * Revision 1.13 1994/04/04 10:04:35 kdburg * cosmetic change to the packet-report (separator lines) * * Revision 1.12 1994/03/30 19:52:04 kdburg * incorporated patch by Scott Boyd which was missing from this version * of uurate.c. Left the comment in cronological order. * * Revision 1.11 1994/03/28 18:53:22 kdburg * config not checked properly for 'logfile/statsfile' overwrites, bail-out * possible; wrong file name written to log for statsfile when found * * Revision 1.10 1993/09/28 16:46:51 kdburg * transmission failures denoted by: failed after ... in stats file * have not been counted at all. * * Revision 1.9 1993/08/17 23:38:36 kdburg * sometimes a line(site) was missing from the protocol stats due * to a missing +; added option -d and -v reassing option -h to print * the help; a zero was returned instead of a null-pointer by * prot_sum * * Revision 1.8 1993/07/03 06:58:55 kdburg * empty input not handled properly; assigned some buffer to input; msg * not displayed when no protocol data was available * * Revision 1.7 1993/06/27 10:31:53 kdburg * rindex was replaced by strchr must be strrchr * * Revision 1.6 1993/06/26 06:59:18 kdburg * switch hdr_done not reset at beginning of protocol report * * Revision 1.5 1993/06/25 22:22:30 kdburg * changed rindex to strchr; if there is no NEWCONFIG defined take * appropriate action * * Revision 1.4 1993/06/25 20:04:07 kdburg * added comment about -p option; inserted proto for rindex * * Revision 1.3 1993/06/25 19:31:14 kdburg * major rework done; added protocol reports (normal/errors) * * Revision 1.2 1993/06/21 19:53:54 kdburg * init * */ char version[] = "@(#) Taylor UUCP Log File Summary Filter, Version 1.2.2"; static char rcsid[] = "$Id: uurate.c,v 1.15 1994/04/07 21:47:11 kdburg Rel $"; #include /* Character Classification */ #include #include "uucp.h" /* uucp.h includes string.h or strings.h, no include here. */ #if HAVE_SYS_PARAM_H #include #endif #define _DEBUG_ 0 /* * Direction of Calling and Data Transmission */ #define IN 0 /* Inbound */ #define OUT 1 /* Outbound */ /* * define some limits */ #define MAXCOLS 8 /* report has this # of columns incl. 'name' */ #define MAXREP 6 /* number of reports available */ #define MAXFNAME 64 /* max input file name length incl. path*/ #define MAXDNAME 8 /* max display (Hostname) name length */ /* * Data structures used to collect information */ struct File_Stats { int files; /* Files Transferred */ unsigned long bytes; /* Data Size Transferred*/ double time; /* Transmission Time */ }; struct Phone_Call { int calls; /* Call Count */ int succs; /* Successful calls */ double connect_time; /* Connect Time Spent */ struct File_Stats flow[2]; /* Rcvd & Sent Data */ }; struct Execution_Command { struct Execution_Command *next; char Commandname[64]; int count; }; struct Protocol_Summary { struct Protocol_Summary *next; char type[3]; long int pr_cnt; long int pr_psent; long int pr_present; long int pr_preceived; long int pr_eheader; long int pr_echksum; long int pr_eorder; long int pr_ereject; long int pr_pwinmin; long int pr_pwinmax; long int pr_psizemin; long int pr_psizemax; }; struct Host_entry { struct Host_entry *next; char Hostname[32]; struct Execution_Command *cmds; /* Local Activities */ struct Phone_Call call[2]; /* In & Out Activities */ struct Protocol_Summary *proto; }; struct Host_entry *hosts = NULL; struct Host_entry *tot = NULL; struct Host_entry *cur = NULL; struct Execution_Command *cmd, *t_cmds = NULL; struct Protocol_Summary *prot, *t_prot, *s_prot, *ss_prot = NULL; /* * Stuff for getopt() */ extern int optind; /* GETOPT : Option Index */ extern char *optarg; /* GETOPT : Option Value */ extern int getopt(); #if ! HAVE_STDLIB_H extern pointer *calloc(); #endif /* HAVE_STDLIB_H */ /* * Default files to read. Taken from Taylor compile-time configuration. * def_logs must look like an argvec, hence the dummy argv[0]. * Maybe later modified by scanning the config */ static char *def_logs[3] = { NULL, NULL, NULL}; char *I_conf = NULL; /* points to config lib given by -I option */ char *D_conf = NULL; /* points to config lib from makefile */ char *Tlog = NULL; /* points to Log-file */ char *Tstat = NULL; /* points to Stats-file */ char Pgm_name[64]; /* our pgm-name */ char logline[BUFSIZ+1]; /* input area */ char noConf[] = "- not defined -"; char buff[16*BUFSIZ]; char sbuff[2*BUFSIZ]; /* * Boolean switches for various decisions */ int p_done = FALSE; /* TRUE: start date/time of file printed */ int hdr_done = FALSE; /* TRUE: report header printed */ int show_files = FALSE; /* TRUE: -f option given */ int show_calls = FALSE; /* TRUE: -c option given */ int show_commands = FALSE; /* TRUE: -x option given */ int show_efficiency = FALSE; /* TRUE: -e option given */ int show_all = FALSE; /* TRUE: -t option given */ int show_proto = FALSE; /* TRUE: -p option given */ int use_stdin = FALSE; /* TRUE: -i option given */ int be_quiet = FALSE; /* TRUE: -q option given */ int have_files[2]; /* TRUE: [IN] or [OUT] files found */ int have_calls = FALSE; /* TRUE: in/out calls found */ int have_commands = FALSE; /* TRUE: found uuxqt records */ int have_proto = FALSE; /* TRUE: protocol data found */ int no_records = TRUE; /* FALSE: got one record from file */ /* * protos */ static pointer *getmem(unsigned n); static void inc_cmd(struct Execution_Command **, char *name); static void fmtime(double sec, char *buf); static void fmbytes(unsigned long n, char *buf); static void usage(); static int chk_config(char *conf, int n, int type); static void hdrprt(char c, int bot); struct Protocol_Summary *prot_sum(struct Protocol_Summary **, char *, int); /* * BEGIN EXECUTION */ int main(argc, argv) int argc; char *argv[]; { FILE *Log = NULL; int c; char *p, *s, *stt, *flq = NULL; char Hostname[MAXHOSTNAMELEN]; /* def taken from */ char Filename[15]; /* filename to be printed */ char in_date[14]; /* holds the date info of record read*/ char in_time[14]; /* holds the time info of record read */ char dt_info[31]; /* holds the date info from the last record read */ char *logmsg; int sent, called = IN; int report = 0; /* if <= 0 give msg that no report was avail. */ int junk; /* -------------------------------------------------------------------- * P r o l o g * -------------------------------------------------------------------- */ Hostname[0] = '\0'; have_files[IN]= have_files[OUT]= FALSE; setvbuf(stdout,sbuff,_IOFBF,sizeof(sbuff)); /* * get how we've been called isolate the name from the path */ if ((stt = strrchr(argv[0],'/')) != NULL) strcpy(Pgm_name,++stt); else strcpy(Pgm_name,argv[0]); def_logs[0] = Pgm_name; /* * I wish the compiler had the #error directive! */ #if !HAVE_TAYLOR_LOGGING && !HAVE_V2_LOGGING fprintf(stderr,"\a%s: (E) %s\n",Pgm_name,"Your config of Taylor UUCP is not yet supported."); fprintf(stderr,"%s: (E) %s\n",Pgm_name,"Current support is for V2 or TAYLOR logging only."); puts(" Run aborted due to errors\n") exit(1); #endif /* * get some mem to store the default config name (def's are in * policy.h ) */ if (sizeof(NEWCONFIGLIB) > 1) /* defined at compile time */ { D_conf = (char *)getmem((sizeof(NEWCONFIGLIB) + sizeof("/config"))); strcpy(D_conf,NEWCONFIGLIB); /* passed by makefile */ strcat(D_conf,"/config"); } Tlog = (char *)getmem(sizeof(LOGFILE)); Tstat = (char *)getmem(sizeof(STATFILE)); Tlog = LOGFILE; Tstat = STATFILE; /* * Process the command line arguments */ while((c = getopt(argc, argv, "I:s:cfdexaitphv")) != EOF) { switch(c) { case 'h': (void) usage(); case 's': strcpy(Hostname, optarg); break; case 'c': show_calls = TRUE; ++report; break; case 'd': printf("%s: (I) config-file default: %s\n",Pgm_name,D_conf); exit (0); break; case 'f': show_files = TRUE; ++report; break; case 'x': show_commands = TRUE; ++report; break; case 'e': show_efficiency = TRUE; ++report; break; case 'a': show_calls = show_files = show_commands = show_efficiency = TRUE; report = 4; break; case 'i': use_stdin = TRUE; break; case 't': show_all = TRUE; report = MAXREP; break; case 'p': show_proto = TRUE; ++report; break; case 'I': I_conf = (char *)getmem(sizeof(optarg)); I_conf = optarg; break; case 'q': be_quiet = TRUE; break; case 'v': printf("%s\n",rcsid); exit (0); default : (void) usage(); } } if (report == 0) /* no options given */ ++report; /* at least summary can be printed */ if (! be_quiet) hdrprt('i',0); /* print header for environment info */ /* * Adjust argv and argc to account for the args processed above. */ argc -= (optind - 1); argv += (optind - 1); /* * If further args present, Assume rest are logfiles for us to process * which should be given in pairs (log plus stat) otherwise the results may * not come out as expected! If no further args are present take input from * Log and Stat files provided in the compilation environment of Taylor UUCP. * If -i was given, Log already points to stdin and no file args are accepted. */ if (use_stdin) /* If -i, read from stdin */ { if (argc != 1) /* No file arguments allowed */ { fprintf(stderr,"\a%s: (E) %s\n",Pgm_name, "it's not posssible to give file args with '-i'"); (void) usage(); } else { argc = 2; Log = stdin; if (! be_quiet) puts(" Input from stdin; no other files will be used\n"); } } else { if (argc != 1) /* file arguments are present */ { if (! be_quiet) puts(" No defaults used; will use passed file arguments\n"); } else /* Read from current logs */ { def_logs[1] = Tlog; /* prime the */ def_logs[2] = Tstat; /* file names */ if (! be_quiet) printf(" Config for this run: "); if (I_conf != NULL) { junk = 0; if (! be_quiet) printf("%s\n",I_conf); if (0 != (chk_config(I_conf,be_quiet,junk))) return (8); } else { if (D_conf != NULL) { junk = 1; /* indicate default (compiled) config */ if (! be_quiet) printf("%s\n",D_conf); chk_config(D_conf,be_quiet,junk); } else if (! be_quiet) printf("%s\n",noConf); } def_logs[1] = Tlog; /* final setting of */ def_logs[2] = Tstat; /* file names */ argv = def_logs; /* Bash argvec to log/stat files */ argc = sizeof(def_logs) / sizeof(def_logs[0]); } } /* -------------------------------------------------------------------- * MAIN LOGFILE PROCESSING LOOP * -------------------------------------------------------------------- */ if (!use_stdin) { if (argc < 3 && ! be_quiet) { puts(" (W) there is only one input file!"); puts(" (W) some reports may not be printed"); } if (! be_quiet) hdrprt('d',0); /* give subheaderline */ } while (argc > 1) { if (!use_stdin && (Log = fopen(argv[1], "r")) == NULL) { perror(argv[1]); exit (8); } setvbuf(Log,buff,_IOFBF,sizeof(buff)); if ((flq = strrchr(argv[1], '/')) == NULL) strncpy(Filename,argv[1],sizeof(Filename)-1); else strncpy(Filename,++flq,sizeof(Filename)-1); strcpy(in_date," n/a"); strcpy(in_time," n/a"); p_done = FALSE; /* no info printed yet */ no_records = TRUE; /* not read any record yet */ /* * Read each line of the logfile and collect information */ while (fgets(logline, sizeof(logline), Log)) { /* * The host name of the other end of the connection is * always the second field of the log line, whether we * are reading a Log file or a Stats file. Set 'p' to * point to the second field, null-terminated. Skip * the line if something is funny. V2 and Taylor ar identical * up to this part. Put out the start/end date of the files read; */ if (NULL == (p = strchr(logline, ' '))) continue; no_records = FALSE; /* got one (usable) record at least */ ++p; if (NULL != (stt = strchr(p, '('))) { if (! p_done && ! use_stdin && ! be_quiet) { #if HAVE_TAYLOR_LOGGING sscanf(++stt,"%s%*c%[^.]",in_date,in_time); #endif /* HAVE_TAYLOR_LOGGING */ #if HAVE_V2_LOGGING sscanf(++stt,"%[^-]%*c%[1234567890:]",in_date,in_time); #endif /* HAVE_V2_LOGGING */ printf(" %-14s %10s %8s",Filename, in_date, in_time); strcpy(in_date," n/a"); /* reset to default */ strcpy(in_time," n/a"); p_done = TRUE; } else { if (! use_stdin && ! be_quiet) /* save for last time stamp prt. */ strncpy(dt_info,++stt,sizeof(dt_info)-1); } } if (NULL != (s = strchr(p, ' '))) *s = '\0'; for (s = p; *s; ++s) if (isupper(*s)) *s = tolower(*s); /* * Skip this line if we got -s and * this line does not contain that host name. * Don't skip the `incoming call' line with the system name `-'. */ if (Hostname[0] != '\0') if ( (p[0] != '-' || p[1] != '\0') && 0 != strcmp(p, Hostname) ) continue; /* * We are within a call block now. If this line is a file * transfer record, determine the direction. If not then * skip the line if it is not interesting. */ if ((s = strchr(++s, ')')) == NULL) continue; #if ! HAVE_TAYLOR_LOGGING #if HAVE_V2_LOGGING if ((strncmp(s,") (",3)) == 0) /* are we in stats file ?) */ if ((s = strchr(++s, ')')) == NULL) continue; /* yes but strange layout */ #endif /* HAVE_V2_LOGGING */ #endif /* ! HAVE_TAYLOR_LOGGING */ logmsg = s + 2; /* Message is 2 characters after ')' */ if ((0 != strncmp(logmsg, "Call complete", 13)) && (0 != strncmp(logmsg, "Calling system", 14)) && (0 != strncmp(logmsg, "Incoming call", 13)) && (0 != strncmp(logmsg, "Handshake successful", 20)) && (0 != strncmp(logmsg, "Retry time not", 14)) && (0 != strncmp(logmsg, "ERROR: All matching ports", 25)) && (0 != strncmp(logmsg, "Executing", 9)) && (0 != strncmp(logmsg, "Protocol ", 9)) && (0 != strncmp(logmsg, "sent ", 5)) && (0 != strncmp(logmsg, "received ", 9)) && (0 != strncmp(logmsg, "failed after ", 13)) && (0 != strncmp(logmsg, "Errors: ", 8))) continue; /* * Find the Host_entry for this host, or create a new * one and link it on to the list. */ if ((cur == NULL) || (0 != strcmp(p, cur->Hostname))) { struct Host_entry *e, *last; for (e= cur= hosts; cur != NULL ; e= cur, cur= cur->next) if (0 == strcmp(cur->Hostname, p)) break; if (cur == NULL) { cur= (struct Host_entry *)getmem(sizeof(*hosts)); strcpy(cur->Hostname, p); if (hosts == NULL) e= hosts= cur; else { e = hosts; last = NULL; while (e != NULL) { if (strcmp(e->Hostname, cur->Hostname) <= 0) { if (e->next == NULL) { e->next = cur; break; } last = e; e = e->next; } else { cur->next = e; if (last == NULL) hosts = cur; else last->next = cur; break; } } /* while (e != NULL) */ } /* hosts == NULL */ } /* cur == NULL */ } /* * OK, if this is a uuxqt record, find the Execution_Command * structure for the command being executed, or create a new * one. Then count an execution of this command. * (Log file only) */ if (0 == strncmp(logmsg, "Executing", 9)) { if (NULL == (p = strchr(logmsg, '('))) continue; if ((s = strpbrk(++p, " )")) == NULL) continue; *s = '\0'; inc_cmd(&cur->cmds, p); inc_cmd(&t_cmds, p); have_commands = TRUE; continue; } /* * Count start of outgoing call. */ if ((0 == strncmp(logmsg, "Calling system", 14)) || (0 == strncmp(logmsg, "Retry time not", 14)) || (0 == strncmp(logmsg, "ERROR: All matching ports", 25))) { called = OUT; cur->call[OUT].calls++; have_calls = TRUE; s_prot = NULL; /* destroy pointer to protocol */ continue; } /* * Count start of incoming call. */ if (0 == strncmp(logmsg, "Incoming call", 13)) { called = IN; s_prot = NULL; /* destroy pointer to protocol */ continue; } /* * On an incoming call, get system name from the second line. * Get protocol type and size/window too */ if (0 == strncmp(logmsg, "Handshake successful", 20)) { if ( called==IN ) cur->call[IN].calls++; have_calls = TRUE; s_prot = NULL; /* destroy pointer to protocol */ if (NULL == (p = strchr(logmsg, '('))) continue; if (0 == strncmp(p, "(protocol ", 10)) { if (NULL == (p = strchr(p, '\''))) continue; ss_prot = prot_sum(&cur->proto, ++p, 1); s_prot = prot_sum(&t_prot, p, 1); continue; } } /* * check protocol type and get stats * */ if (0 == strncmp(logmsg, "Protocol ", 9)) { s_prot = NULL; /* destroy pointer to protocol */ if (NULL == (p = strchr(logmsg, '\''))) continue; ss_prot = prot_sum(&cur->proto, ++p, 2); s_prot = prot_sum(&t_prot, p, 2); continue; } /* * check protocol errors. Unfortunately the line does not contain * the used protocol, so if any previous line did contain that * information and we did process that line we will save the pointer * to that particular segment into s_prot. If this pointer is not set * the error info is lost for we don't know where to store. * */ if ((0 == strncmp(logmsg, "Errors: header", 14)) && s_prot != NULL) { int i1,i2,i3,i4 = 0; sscanf(logmsg,"%*s %*s %d%*c%*s %d%*c%*s %d%*c%*s %*s%*c %d",&i1,&i2,&i3,&i4); ss_prot->pr_eheader += i1; ss_prot->pr_echksum += i2; ss_prot->pr_eorder += i3; ss_prot->pr_ereject += i4; s_prot->pr_eheader += i1; s_prot->pr_echksum += i2; s_prot->pr_eorder += i3; s_prot->pr_ereject += i4; s_prot = NULL; continue; } /* * Handle end of call. Pick up the connect time. * position is on the closing paren of date/time info * i.e: ) text.... */ if (0 == strncmp(logmsg, "Call complete", 13)) { cur->call[called].succs++; s_prot = NULL; /* destroy pointer to protocol */ if (NULL == (s = strchr(logmsg, '('))) continue; cur->call[called].connect_time += atof(s+1); continue; } /* * We are definitely in a Stats file now. * If we reached here, this must have been a file transfer * record. Count it in the field corresponding to the * direction of the transfer. Count bytes transferred and * the time to transfer as well. * Position within the record is at the word 'received' or 'sent' * depending on the direction. */ sent = IN; /* give it an initial value */ if (0 == strncmp(logmsg, "failed after ",13)) logmsg += 13; /* the transmission failed for any reason */ /* so advance pointer */ if (0 == strncmp(logmsg, "sent", 4)) sent = OUT; else if (0 == strncmp(logmsg, "received", 8)) sent = IN; have_files[sent] = TRUE; cur->call[called].flow[sent].files++; if (NULL == (s = strchr(logmsg, ' '))) /* point past keyword */ continue; /* nothing follows */ /* we should be at the bytes column now*/ #if HAVE_TAYLOR_LOGGING cur->call[called].flow[sent].bytes += atol(++s); #endif /* HAVE_TAYLOR_LOGGING */ #if HAVE_V2_LOGGING if (NULL == (s = strpbrk(s, "0123456789"))) /* point to # bytes */ continue; cur->call[called].flow[sent].bytes += atol(s); #endif /* HAVE_V2_LOGGING */ if (NULL == (s = strchr(s, ' '))) /* point past # of bytes */ continue; if (NULL == (s = strpbrk(s, "0123456789"))) /* point to # of seconds */ continue; cur->call[called].flow[sent].time += atof(s); } /* end of while (fgets(logline...)) */ if (stt != NULL && ! use_stdin && ! be_quiet && ! no_records) { #if HAVE_TAYLOR_LOGGING sscanf(dt_info,"%s%*c%[^.]",in_date,in_time); #endif /* HAVE_TAYLOR_LOGGING */ #if HAVE_V2_LOGGING sscanf(dt_info,"%[^-]%*c%[1234567890:]",in_date,in_time); #endif /* HAVE_V2_LOGGING */ printf(" %10s %8s\n",in_date, in_time); p_done = FALSE; } if (Log != stdin) { if (0 != ferror(Log)) { if (! be_quiet) printf(" %-14s data is incomplete; read error"," "); else fprintf(stderr,"%s (W) data is incomplete; read error on %s\n", Pgm_name,argv[1]); } else { if (! be_quiet && no_records) printf(" %-14s %10s\n",Filename, " is empty "); } } fclose(Log); argc--; argv++; } /* end of while (for (argv ....) */ /* * do we have *any* data ? */ if (cur == NULL) { puts("\n(I) Sorry! No data is available for any requested report\n"); exit(0); } /* * truncate hostname, alloc the structure holding the totals and * collect the totals data */ for (cur = hosts; cur != NULL;cur = cur->next) { cur->Hostname[MAXDNAME] = '\0'; if (cur->next == NULL) /* last so will have to alloc totals */ { cur->next = (struct Host_entry *)getmem(sizeof(*hosts)); strcpy(cur->next->Hostname,"Totals"); tot = cur->next; for (cur = hosts; cur != NULL; cur = cur->next) { if (cur->next != NULL) /* don't count totals to totals */ { tot->call[IN].flow[IN].bytes += cur->call[IN].flow[IN].bytes; tot->call[OUT].flow[IN].bytes += cur->call[OUT].flow[IN].bytes; tot->call[IN].flow[OUT].bytes += cur->call[IN].flow[OUT].bytes; tot->call[OUT].flow[OUT].bytes += cur->call[OUT].flow[OUT].bytes; tot->call[IN].flow[IN].time += cur->call[IN].flow[IN].time; tot->call[OUT].flow[IN].time += cur->call[OUT].flow[IN].time; tot->call[IN].flow[OUT].time += cur->call[IN].flow[OUT].time; tot->call[OUT].flow[OUT].time += cur->call[OUT].flow[OUT].time; tot->call[IN].flow[IN].files += cur->call[IN].flow[IN].files; tot->call[OUT].flow[IN].files += cur->call[OUT].flow[IN].files; tot->call[IN].flow[OUT].files += cur->call[IN].flow[OUT].files; tot->call[OUT].flow[OUT].files += cur->call[OUT].flow[OUT].files; tot->call[OUT].succs += cur->call[OUT].succs; tot->call[OUT].calls += cur->call[OUT].calls; tot->call[OUT].connect_time += cur->call[OUT].connect_time; tot->call[IN].succs += cur->call[IN].succs; tot->call[IN].calls += cur->call[IN].calls; tot->call[IN].connect_time += cur->call[IN].connect_time; } } break; /* totals is last in Host_Entry */ } } /* * *********** * * REPORTS * * *********** */ #if _DEBUG_ putchar('\n'); #endif /* ------------------------------------------------------------------ * * Summary report only when no other report except option -t is given * * I know, this code could be tightened (rbd)... * ------------------------------------------------------------------ */ if ( !(show_calls || show_files || show_efficiency || show_commands || show_proto) || show_all) { if (have_calls || have_files[IN] || have_files[OUT]) { char t1[32], t2[32], t3[32], t4[32], t5[32]; long ib, ob, b, rf, sf; double it, ot, ir, or; hdr_done = FALSE; for (cur = hosts; cur != NULL; cur = cur->next) { ib = (cur->call[IN].flow[IN].bytes + cur->call[OUT].flow[IN].bytes); fmbytes(ib, t1); ob = (cur->call[IN].flow[OUT].bytes + cur->call[OUT].flow[OUT].bytes); fmbytes(ob, t2); /* Don't print null-lines. */ if (( b= ib+ob ) == 0 ) continue; /* Don't print the header twice. */ if (! hdr_done) { hdrprt('s',0); /* print the header line(s) */ hdr_done = TRUE; } fmbytes(b, t3); it = cur->call[IN].flow[IN].time + cur->call[OUT].flow[IN].time; fmtime(it, t4); ot = cur->call[IN].flow[OUT].time + cur->call[OUT].flow[OUT].time; fmtime(ot, t5); rf = cur->call[IN].flow[IN].files + cur->call[OUT].flow[IN].files; sf = cur->call[IN].flow[OUT].files + cur->call[OUT].flow[OUT].files; ir = (it == 0.0) ? 0.0 : (ib / it); or = (ot == 0.0) ? 0.0 : (ob / ot); if (cur->next == NULL) /* totals line reached ? */ hdrprt('s',1); /* print the separator line */ printf("%-8s %4ld %4ld %9s %9s %9s %8s %8s %5.0f %5.0f\n", cur->Hostname, rf, sf, t1, t2, t3, t4, t5, ir, or); } if (! hdr_done) { puts("\n(I) No data found to print Compact summary report"); } } else { puts("\n(I) No data available for Compact summary report"); --report; } } /* ------------------------------------------------------------------ * Protocol statistics report * ------------------------------------------------------------------ */ if (show_proto || show_all) { if (have_proto) { /* --------------------- */ /* protocol packet report */ /* --------------------- */ char *type = NULL; hdr_done = FALSE; for (cur = hosts; cur != NULL; cur = cur->next) { type = cur->Hostname; if (cur->next == NULL) { if (hdr_done) puts("-------------------------------------------------------------------"); cur->proto = t_prot; } for (prot = cur->proto; prot != NULL; prot = prot->next) { if (! hdr_done) { hdrprt('p',0); /* print the header line(s) */ hdr_done = TRUE; } printf("%-8s %3s %4ld %4ld %5ld %4ld %10ld %7ld %10ld\n", type == NULL ? " ":cur->Hostname, prot->type, prot->pr_psizemin, prot->pr_psizemax, prot->pr_pwinmin, prot->pr_pwinmax, prot->pr_psent, prot->pr_present, prot->pr_preceived); type = NULL; } } if (! hdr_done) puts("\n(I) No data found to print Protocol packet report"); /* --------------------- */ /* protocol error report */ /* --------------------- */ type = NULL; hdr_done = FALSE; if (t_prot != NULL) { for (cur = hosts; cur != NULL; cur = cur->next) { type = cur->Hostname; if (cur->next == NULL) { if (hdr_done) puts("--------------------------------------------------------------"); cur->proto = t_prot; } for (prot = cur->proto; prot != NULL; prot = prot->next) { if ((prot->pr_eheader + prot->pr_echksum + prot->pr_eorder + prot->pr_ereject) != 0) { if (! hdr_done) { hdrprt('p',1); /* print the header line(s) */ hdr_done = TRUE; } printf("%-8s %3s %11ld %11ld %11ld %11ld\n", type == NULL ? " ":cur->Hostname, prot->type, prot->pr_eheader, prot->pr_echksum, prot->pr_eorder, prot->pr_ereject); type = NULL; } } } } if (! hdr_done) puts("\n(I) No data found to print Protocol error report"); } else { puts("\n(I) No data available for Protocol reports"); --report; } } /* ------------------------------------------------------------------ * Call statistics report * ------------------------------------------------------------------ */ if (show_calls || show_all) { if (have_calls) { char t1[32], t2[32]; hdr_done = FALSE; for (cur = hosts; cur != NULL; cur = cur->next) { if (cur->next == NULL) { if (hdr_done) hdrprt('c',1); /* print the separator line */ } else { /* Don't print null-lines on deatail lines */ if ( cur->call[OUT].calls + cur->call[IN].calls == 0 ) continue; /* Don't print the header twice. */ if (! hdr_done) { hdrprt('c',0); /* print the header line(s) */ hdr_done = TRUE; } } if ( cur->call[OUT].calls > 0 || cur->next == NULL) { fmtime(cur->call[OUT].connect_time, t1); printf( " %-8s %7d %7d %7d %9s", cur->Hostname, cur->call[OUT].succs, cur->call[OUT].calls - cur->call[OUT].succs, cur->call[OUT].calls, t1 ); } else { printf( " %-42s", cur->Hostname ); } if ( cur->call[IN].calls > 0 || cur->next == NULL ) { fmtime(cur->call[IN].connect_time, t2); printf( " %7d %7d %7d %9s", cur->call[IN].succs, cur->call[IN].calls - cur->call[IN].succs, cur->call[IN].calls, t2 ); } putchar('\n'); } if (! hdr_done) { puts("\n(I) No data found to print Call statistics report"); } } else { puts("\n(I) No data available for Call statistics report"); --report; } } /* ------------------------------------------------------------------ * File statistics report * ------------------------------------------------------------------ */ if (show_files || show_all) { if (have_files[IN] || have_files[OUT]) { char t1[32], t2[32]; double rate = 0, time = 0; int b = 0; int lineOut = 0; hdr_done = FALSE; for (cur = hosts; cur != NULL; cur = cur->next) { lineOut = 0; for (sent= IN; sent <= OUT; ++sent) { b = cur->call[IN].flow[sent].bytes + cur->call[OUT].flow[sent].bytes; time = cur->call[IN].flow[sent].time + cur->call[OUT].flow[sent].time; /* Don't print null-lines on detail lines. */ if ( (b != 0 && time != 0.0) || cur->next == NULL) { /* Don't print the header twice. */ if (! hdr_done) { hdrprt('f',0); /* print the header line(s) */ hdr_done = TRUE; } fmbytes(b, t1); rate = (cur->call[IN].flow[sent].bytes + cur->call[OUT].flow[sent].bytes) / time; fmtime((cur->call[IN].flow[sent].time + cur->call[OUT].flow[sent].time), t2); if (lineOut == 0) /* first half not printed yet ? */ { if (cur->next == NULL) /* totals line ? */ hdrprt('f',1); /* print the separator line */ printf(" %-8s", cur->Hostname); if (sent == OUT) /* can't happen whith totals line */ printf("%34s", " "); } printf(" %5d %11s %9s %5.0f", cur->call[IN].flow[sent].files + cur->call[OUT].flow[sent].files, t1, t2, rate); lineOut = 1; } } /* end: for (sent ... ) */ if (lineOut) printf("\n"); } /* end: for (cur= ... ) */ if (! hdr_done) { puts("\n(I) No data found to print File statistics report"); } } else { puts("\n(I) No data available for File statistics report"); --report; } } /* ------------------------------------------------------------------ * Efficiency report * ------------------------------------------------------------------ */ if (show_efficiency || show_all) { if (have_files[IN] || have_files[OUT]) { char t1[32], t2[32], t3[32]; double total, flow; hdr_done = FALSE; for (cur = hosts; cur != NULL; cur = cur->next) { /* Don't print null-lines. */ if ( 0 == cur->call[IN].flow[IN].files + cur->call[IN].flow[OUT].files + cur->call[OUT].flow[IN].files + cur->call[OUT].flow[OUT].files || 0.0 == (total= cur->call[IN].connect_time + cur->call[OUT].connect_time)) { continue; } if (! hdr_done) { hdrprt('e',0); /* print the header line(s) */ hdr_done = TRUE; } flow = cur->call[IN].flow[IN].time + cur->call[IN].flow[OUT].time + cur->call[OUT].flow[IN].time + cur->call[OUT].flow[OUT].time; fmtime(total, t1); fmtime(flow, t2); fmtime(total-flow, t3); if (cur->next == NULL) hdrprt('e',1); /* print the separator line */ printf(" %-8s %10s %10s %10s %7.2f\n", cur->Hostname, t1, t2, t3, flow >= total ? 100.0: flow*100.0/total); } /* end: for (cur= .. */ if (! hdr_done) { puts("\n(I) No data found to print Efficiency report"); } } else { puts("\n(I) No data available for Efficiency report"); --report; } } /* ------------------------------------------------------------------ * Command execution report * ------------------------------------------------------------------ */ if (show_commands || show_all) { if (have_commands) { int ncmds, i, match; /* * layout the header line. The column's header is the command name */ hdr_done = FALSE; for (ncmds= 0, cmd= t_cmds; cmd != NULL && ncmds <= MAXCOLS-1; ncmds++, cmd= cmd->next) { if (! hdr_done) { puts("\nCommand executions:"); puts("-------------------"); puts(" Name of "); fputs(" site ", stdout); hdr_done = TRUE; } printf(" %7s", cmd->Commandname); } if (! hdr_done) { puts("\n(I) No data found to print Command execution report"); } else { fputs("\n --------", stdout); for (i= 0; inext) { if (cur->next == NULL) break; /* Don't print null-lines. */ if (cur->cmds == NULL) continue; printf(" %-8s", cur->Hostname); for (cmd= t_cmds; cmd != NULL; cmd= cmd->next) { struct Execution_Command *ec; match = FALSE; for(ec= cur->cmds; ec != NULL; ec= ec->next) { if ( 0 == strcmp(cmd->Commandname, ec->Commandname) ) { printf(" %7d", ec->count); match = TRUE; break; } } if (! match) printf("%8s"," "); /* blank out column */ } putchar('\n'); } /* * print the totals line */ fputs(" --------", stdout); for (i= 0; iHostname); for (cmd= t_cmds; cmd != NULL; cmd= cmd->next) { printf(" %7d", cmd->count); } putchar('\n'); } } else { puts("\n(I) No data available for Command execution report"); --report; } } if (report <= 0 ) /* any reports ? */ { puts("\n(I) Sorry! No data is available for any requested report\n"); exit(1); } puts("\n(I) End of reports\n"); exit (0); } /* end of main */ /* ------------------------------------------------------------------ * * Functions * * ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ * display the help * ------------------------------------------------------------------ */ void usage() { fprintf(stderr,"Usage uurate [-acdefhiptvx] [-s hostname] [-I config file] [logfile(s) ... logfile(s)]\n"); fprintf(stderr,"where:\t-a\tPrint reports c,e,f,x\n"); fprintf(stderr,"\t-c\tReport call statistics\n"); fprintf(stderr,"\t-d\tPrint the name of the default config file\n"); fprintf(stderr,"\t-e\tReport efficiency statistics\n"); fprintf(stderr,"\t-f\tReport file transfer statistics\n"); fprintf(stderr,"\t-h\tPrint this help\n"); fprintf(stderr,"\t-i\tRead log info from standard input\n"); fprintf(stderr,"\t-p\tReport protocol statistics\n"); fprintf(stderr,"\t-t\tAll available reports plus compact summary report\n"); fprintf(stderr,"\t-v\tPrint version number\n"); fprintf(stderr,"\t-x\tReport command execution statistics\n"); fprintf(stderr,"\t-s host\tReport activities involving HOST only\n"); fprintf(stderr,"\t-I config Use config instead of standard config file\n"); fprintf(stderr,"If no report options given, a compact summary report is printed.\n"); fprintf(stderr,"log files should be given as pairs that is Log/Stats ... .\n"); fprintf(stderr,"If neither -i nor logfiles given, those names found in config will be used\n"); exit (1); } /* ------------------------------------------------------------------ * getmem - get some memory * ------------------------------------------------------------------ */ static pointer *getmem(n) unsigned n; { pointer *p; if( NULL== (p= calloc(1, n)) ) { fprintf(stderr,"\a%s (C) %s\n",Pgm_name, "out of memory\n"); exit (8); } return p; } /* ------------------------------------------------------------------ * inc_cmd - increment command count * ------------------------------------------------------------------ */ static void inc_cmd(cmds, name) struct Execution_Command **cmds; char *name; { int cnt = 0; struct Execution_Command *cmd, *ec; for (ec = cmd = *cmds; cmd != NULL; ec= cmd, cmd= cmd->next, cnt++) if ( (0 == strcmp(cmd->Commandname, name)) || (0 == strcmp(cmd->Commandname, "Misc.")) ) break; if (cmd == NULL) { cmd= (struct Execution_Command *)getmem(sizeof(*cmd)); if (cnt <= MAXCOLS-1) /* first col prints site name therefore < max-1 */ { strcpy(cmd->Commandname, name); if (*cmds == NULL) ec = *cmds = cmd; else ec->next= cmd; } else { strcpy(ec->Commandname, "Misc."); /* reached high-water-mark */ cmd = ec; /* backtrack */ } } cmd->count++; } /* ------------------------------------------------------------------ * prot_sum - collect protocol data * ------------------------------------------------------------------ */ struct Protocol_Summary * prot_sum(proto, ptype, ind) struct Protocol_Summary **proto; char *ptype; int ind; { int cnt = 0; int i1, i2, i3 = 0; struct Protocol_Summary *cur, *first; for (first = cur = *proto; cur != NULL; first= cur, cur= cur->next, cnt++) { if ( (0 == strncmp(cur->type, ptype,strlen(cur->type)))) break; } if (cur == NULL) { cur= (struct Protocol_Summary *)getmem(sizeof(*cur)); sscanf(ptype,"%[^\' ]3",cur->type); if (*proto == NULL) first = *proto = cur; else first->next= cur; } if (NULL == (ptype = strchr(ptype, ' '))) return (NULL); cur->pr_cnt++; have_proto = TRUE; ++ptype; switch(ind) { case 1: /* used protocol line */ /* * uucp-1.04 format: .... packet size ssss window ww) * uucp-1.05 format: .... remote packet/window ssss/ww local ssss/ww) * (the remote packet/window will be used!) */ i1 = i2 = 0; /* reset */ if (NULL == (strchr(ptype, '/'))) sscanf(ptype,"%*s %*s %d %*s %d",&i1,&i2); else sscanf(ptype,"%*s %*s %d/%d",&i1,&i2); if (i1 > cur->pr_psizemax) cur->pr_psizemax = i1; if (i1 < cur->pr_psizemin || cur->pr_psizemin == 0) cur->pr_psizemin = i1; if (i2 > cur->pr_pwinmax) cur->pr_pwinmax = i2; if (i2 < cur->pr_pwinmin || cur->pr_pwinmin == 0) cur->pr_pwinmin = i2; break; case 2: /* protocol statistics line */ i1 = i2 = i3 = 0; /* reset */ sscanf(ptype,"%*s %*s %d%*c %*s %d%*c %*s %d",&i1,&i2,&i3); cur->pr_psent += i1; cur->pr_present += i2; cur->pr_preceived += i3; break; default: break; } return (cur); } /* ------------------------------------------------------------------ * fmtime() - Format time in hours & minutes & seconds; * ------------------------------------------------------------------ */ static void fmtime(dsec, buf) double dsec; char *buf; { long hrs, min, lsec; if( dsec <= 0 ) { strcpy(buf, "0" ); return; } lsec = fmod(dsec+0.5, 60L); /* round to the next full second */ hrs = dsec / 3600L; min = ((long)dsec / 60L) % 60L; if (hrs == 0) if (min == 0) sprintf(buf,"%6s%2ld"," ",lsec); else sprintf(buf,"%3s%2ld:%02ld"," ",min,lsec); else sprintf(buf,"%2ld:%02ld:%02ld",hrs,min,lsec); } /* ------------------------------------------------------------------ * fmbytes - Format size in bytes * ------------------------------------------------------------------ */ static void fmbytes(n, buf) unsigned long n; char *buf; { if ( n == 0 ) { strcpy( buf, "0.0" ); return; } sprintf(buf, "%.1f", (double)n / 1000.0); /* Display in Kilobytes */ } /* ------------------------------------------------------------------ * chk_config - Read the config file * check on keywords: logfile and statfile. When found override * the corresponding default * ------------------------------------------------------------------ */ int chk_config(char *T_conf,int be_quiet, int type) { FILE *Conf; char keywrd[9]; char name[MAXPATHLEN+1]; char *pos1, *pos2; int i = 0; int logf = FALSE; int statf = FALSE; if ((Conf = fopen(T_conf, "r")) == NULL) { if (! be_quiet) { puts(" Could not open config"); if (type == 0) { puts(" The run will be aborted\n"); return (8); } } else { fprintf(stderr,"%s (E) %s %s \n",Pgm_name, "could not open config:", T_conf); if (type != 0) fprintf(stderr,"%s (W) defaults used for all files\n", Pgm_name); else { fprintf(stderr,"%s (C) ended due to errors\n", Pgm_name); return (8); } } } else { while (fgets(logline, sizeof(logline), Conf)) { if (logline[0] == '#') continue; sscanf(logline,"%8s %s",keywrd,name); if (0 == strncmp(keywrd,"logfile",7)) { pos1 = pos2 = name; for (i=0;(i<=MAXPATHLEN && *pos1 != '\0');pos1++,pos2++,i++) { if (*pos1 == '#') /* name immed followed by comment */ break; if (*pos1 == '\\') /* quoted comment (filename has #) */ { ++pos1; /* skip escape char */ if (*pos1 != '#') /* continuation ? */ { puts(" Config error:"); puts(" Found filename continuation; bailing out\n"); exit (8); } } *pos2 = *pos1; /* move char */ } *pos2 = '\0'; /* terminate string */ Tlog = (char *)getmem(strlen(name)+1); strcpy(Tlog,name); if (! be_quiet) printf(" logfile used: %s\n",Tlog); logf = TRUE; if (statf) /* statsfile still to come ? */ break; /* no finished */ continue; } if (0 == strncmp(keywrd,"statfile",8)) { pos1 = pos2 = name; for (i=0;(i<=MAXPATHLEN && *pos1 != '\0');pos1++,pos2++,i++) { if (*pos1 == '#') /* name immed followed by comment */ break; if (*pos1 == '\\') /* quoted comment (filename has #) */ { ++pos1; /* skip escape char */ if (*pos1 != '#') /* continuation ? */ { puts(" Config error:"); puts(" Found filename continuation; bailing out\n"); exit (8); } } *pos2 = *pos1; /* move char */ } *pos2 = '\0'; /* terminate string */ Tstat = (char *)getmem(strlen(name)+1); strcpy(Tstat,name); if (! be_quiet) printf(" statfile used: %s\n",Tstat); statf = TRUE; if (logf) /* logfile still to come ? */ break; /* no finished */ continue; } } fclose(Conf); } if (! be_quiet) { if (! logf) puts(" logfile used: - default -"); if (! statf) puts(" statfile used: - default -"); } return 0; } /* ------------------------------------------------------------------ * hdrprt - Print Header/Trailer lines (constant data) * ------------------------------------------------------------------ */ static void hdrprt(char head, int bot) { switch(head) { case('s'): /* standard summary report */ if (bot == 0) { puts("\nCompact summary:"); puts("----------------"); puts("\ Name of + Files + +------- Bytes/1000 --------+ +----- Time ----+ + Avg CPS +\n\ site in out inbound outbound total inbound outbound in out\n\ -------- ---- ---- --------- --------- --------- -------- -------- ----- -----"); } else puts("\ ------------------------------------------------------------------------------"); break; case('f'): /* file statistic report */ if (bot == 0) { puts("\nFile statistics:"); puts("----------------"); puts(" Name of +----------- Inbound -----------+ +---------- Outbound -----------+"); puts(" site files Bytes/1000 xfr time B/sec files Bytes/1000 xfr time B/sec"); puts(" -------- ----- ----------- --------- ----- ----- ----------- --------- -----"); } else puts("\ ----------------------------------------------------------------------------"); break; case('c'): /* calls statistic report */ if (bot == 0) { puts("\nCall statistics:"); puts("----------------"); puts(" Name of +------- Outbound Calls -------+ +-------- Inbound Calls ------+"); puts(" site succ. failed total time succ. failed total time"); puts(" -------- ------ ------ ------ --------- ------ ------ ------ ---------"); } else puts("\ ----------------------------------------------------------------------------"); break; case('e'): /* efficiency statistic report */ if (bot == 0) { puts("\nEfficiency:"); puts("-----------"); puts(" Name of +------ Times inbound/outbound -------+"); puts(" site connected xfr time overhead eff. %"); puts(" -------- --------- --------- --------- ------"); } else puts(" -------------------------------------------------"); break; case('i'): /* Environment information */ if (bot == 0) { puts("\nEnvironment Information:"); puts("------------------------"); printf(" Default config: %s\n",D_conf == NULL ? noConf:D_conf); printf(" Default logfile: %s\n",Tlog); printf(" Default statfile: %s\n\n",Tstat); } break; case('d'): /* Date/time coverage */ if (bot == 0) { puts("\n Date coverage of input files:"); puts(" Name of +----- Start -----+ +------ End ------+"); puts(" file date time date time"); puts(" -------- ---------- -------- ---------- --------"); } break; case('p'): /* Protocol stats */ if (bot == 0) { puts("\nProtocol packet report:"); puts("-----------------------"); puts(" +------- protocol -----+ +--------- Packets ----------+"); puts("Name of packet window "); puts("site typ min max min max sent resent received"); puts("-------- --- ---- ---- ---- ---- ----------- ------- ----------"); } else { puts("\nProtocol error report:"); puts("----------------------"); puts("Name of +----------------- Error Types --------------------+"); puts("site typ header checksum order rem-reject"); puts("-------- --- ----------- ---------- ----------- ----------"); } break; default: if (bot == 0) { puts("\nNo header for this report defined:"); } else puts(" "); break; } } uucp-1.07/contrib/uurate.man0000664000076400007640000001723607665321760011577 ''' $Id: uurate.man,v 1.4 1993/09/28 17:38:31 kdburg Rel $ .TH uurate 1 .SH NAME uurate \- Report Taylor UUCP statistics .SH SYNOPSIS .BR uurate " [ " "\-acdefhipqtvx" " ] [ " "\-s " .I host .RI " ] [ " "\-I " .I config .RI " ][ " "logfile..." " ] " .PP or simply, .PP .B uurate .PP for a traffic summary report. .SH DESCRIPTION The .I uurate command provides tabular summary reports on the operation of the Taylor UUCP system. Data is taken from the currently active log files, standard input, or from a list of log files given on the command line. Output is in the form of tabular reports summarizing call, file transfer, and command execution .RI "(" "uuxqt" ")" activity. .PP The log and stats files given to .I uurate must be in the ``Taylor'' or ``V2'' format. Also, note that call and file transfer activities are logged in separate files, nominally called .I Log and .I Stats, respectively. For reports to be meaningful, the .I Log and .I Stats files should be given to .I uurate together, and cover the same time period. .PP If neither the .B \-i or .B \-I option nor any .I logfile options are given, .I uurate defaults to taking its input from the current Taylor .I Log and .I Stats files. The names are either as defined at compilation time, in case there is no config file, or taken from the arguments of the keywords .I logfile and .I statfile when encountered in the config file. This is the normal mode of operation. .PP The reporting options described below can be used to select the set of reports desired. If no options are given, a summary report is displayed. If there is no relevant data for a particular report or host, that report or host will be suppressed. .SH OPTIONS The following options may be given to .I uurate: .TP 5 .B \-a All reports. Identical to .B \-cfexp. .TP 5 .B \-c Report on call statistics. Requires data from a .I Log file. .TP 5 .B \-d will print the default config file to be used. .TP 5 .B \-e Report on efficiency (total connect time versus time spent transferring files). Requires data from both a .I Log and a .I Stats file, and they must span the same time period. .TP 5 .B \-f Report on file transfer statistics. Requires data from a .I Stats file. .TP 5 .B \-h will print a short help information. .TP 5 .B \-i tells uurate to read any logfile information from standard input. .TP 5 .B \-p report on protocol errors and packets sent/received .TP 5 .B \-q do not print the Environment information, .TP 5 .B \-t All reports. Identical to .B \-cfexp. plus the .B Compact summary. .TP 5 .B \-v will print the version id string .TP 5 .B \-x Report on remote execution requests (e.g., .IR rmail ")." Requires data from a .I Log file. .TP 5 .BI "\-s " "host" Restrict report output to .I host. .TP 5 .BI "\-I " "config file" an alternate config file may be passed by this option. .SH "DESCRIPTION OF REPORTS" There are four reports available: the call, file transfer, efficiency, and remote execution reports. Each may be selected by a command line option. All reports may be selected via the options .B \-a or .B \-t. If no report selection options are given, .I uurate displays a compact traffic summary report (see below). .SS "Summary report" If no report options are given, .I uurate displays a traffic summary report. This is particularly useful in daily .I cron jobs which report on errors and the like. Traffic statistics for each active system is reported on a single line. If more than one system was active, a 'totals' line is included at the end of the report. .SS "Protocol packet report" The protocol report gives statistics on min/max packet and window sizes used during transmission. Further on data is collected for packets transferred. The data is collected for each host/protocol type. The fields are described below: .PP .br .nf .in +.3i .ta 1.0i .BR "site " "UUCP node name of neighbor host system," .BR "typ " "Type of protocol used" .BR "Min " "minimum packet/window size" .BR "Max " "maximum packet/window size" .BR "sent " "packets sent" .BR "resent " "packets resent" .BR "received " "packets received" .in -.3 .SS "Protocol error report" The protocol report gives statistics on packet errors during transmission. The data is collected for each host/protocol type. The fields are described below: .PP .br .nf .in +.3i .ta 1.5i .BR "site " "UUCP node name of neighbor host system," .BR "typ " "Type of protocol used" .BR "header " "number of errors in header" .BR "checksum " "number of checksum errors" .BR "order " "number of order errors" .BR "resent " "number packets resent" .BR "rem-reject " "packets that the remote site rejected" .in -.3 .SS "Call report" The call report gives statistics on inbound and outbound calls for each active host system. The fields are described below: .PP .br .nf .in +.3i .ta 1.0i .BR "site " "UUCP node name of neighbor host system," .BR "succ. " "Successful calls attempted to/by that system," .BR "failed " "Failed calls to/by that system," .BR "total " "Total calls to/by that system," .BR "time " "Collected connect time (hh:mm:ss) for all calls," .in -.3 .SS "File transfer reports" The file transfer reports give statistics on inbound and outbound file transfers (regardless of which end initiated the transfer) for each active host system. There are two reports, one for files sent to the remote system and one for files received from the remote system. The fields in each report are described below: .PP .br .nf .in +.3i .ta 1.0i .BR "site " "UUCP node name of neighbor host system" .BR "files " "Number of files transferred" .BR "Bytes/1000 " "Total size of files transferred given in thousands" .BR "xfr time " "Total time (hh:mm:ss) spent on transfer the files," .BR "B/sec " "Average transfer rate (bytes/sec)." .in -.3 .SS "Efficiency report" The efficiency report describes the utilization of the links to each active remote system, giving the ratio of total connect time to the time spent actually transferring files. The fields are described below: .PP .br .nf .in +.3i .ta 1.0i .BR "site " "UUCP node name of neighbor host system" .BR "connected " "Total connect time for that system (turn-around)" .BR "xfr time " "Total file transfer time for that system" .BR "overhead " "Connect time not used to transfer files," .BR "eff. % " "Ratio of connect time to transfer time (xfer*100/conn)" .in -.3 .SS "Command executions report" The remote execution report describes remotely requested command executions from each active host system, like .I rmail and .IR rnews "." Up to eight command names are displayed. If there are more, the rest will be put together in an `Misc.' column. The fields are described below: .PP .br .nf .in +.3i .ta 1.0i .BR "site " "UUCP node name of neighbor host system," .BR "(command) " "Number of requests of this command," .BR "Misc. " "Number of other requests, if more than eight." .in -.3i .SS FILES The file names below may be changed at compilation time or by the configuration file, so these are only approximations. .br .nf .in +.3in .ta 2.2i .IR "/usr/spool/uucp/Log " "V2/Taylor format call/execution log," .IR "/usr/spool/uucp/Stats " "V2/Taylor format file transfer log." .SS "SEE ALSO" .IR uucico "(8)" .SS BUGS Does not understand other than V2/TAYLOR logging formats. Anyone care to volunteer to add the not mentioned? .PP Scanning the arguments of logfile and statfile keywords in config should handle lines continued with the backslash as well. .PP The .B failfm field in the call statistics table is always zero, unless something really serious happens, e.g. uucico got SIGQUIT or the whole system crashed. .SS AUTHOR Robert B. Denny (denny@alisa.com). .br Loosely based on the DECUS UUCP program .I uurate by Mark Pizzolato. .br Modified by Stephan Niemz (stephan@sunlab.ka.sub.org). .br Modified by Klaus Dahlenburg (kdburg@incoahe.hanse.de). uucp-1.07/contrib/uureroute.perl0000775000076400007640000000416407665321760012517 #!/usr/bin/perl eval ' exec /usr/bin/perl $0 "$@" ' if $running_under_some_shell; # From a script by # Newsgroups: comp.sources.misc # Subject: v28i073: uureroute - Reroute HDB queued mail, Part01/01 # Date: 26 Feb 92 02:28:37 GMT # # This is a Honey DanBer specific routine written in perl to reroute all # mail queued up for a specific host. It needs to be run as "root" since # uucp will not allow itself to remove others requests. # # Revision *** 92/21/09: Francois Pinard # 1. adapted for Taylor UUCP # # Revision 1.3 91/10/08 09:01:21 src # 1. Rewritten in perl # 2. Add -v option for debugging. # # Revision 1.2 91/10/07 23:57:42 root # 1. Fix mail program path. # 2. Truncate directory name to 7 characters ($progname = $0) =~ s!.*/!!; # save this very early $USAGE = " # Reroute uucp mail # # Usage: $progname [-v] host [host...] # # Options Argument Description # -v Verbose (doesn't execute /bin/sh) # "; $UUSTAT = "uustat"; $SHELL = "/bin/sh"; $SMAIL = "/bin/smail"; sub usage { die join ("\n", @_) . "\n$USAGE\n"; } do "getopts.pl"; &usage ("Invalid Option") unless do Getopts ("vV"); $verbose = ($opt_v ? '-v' : ()); $suffix = ($verbose ? '' : $$); &usage ("No system specified") if $#ARGV < 0; if (!$verbose) { open (SHELL, "| $SHELL"); select SHELL; } while ($system = shift) { $sysprefix = substr ($system, 0, 7); $directory = "/usr/spool/uucp/$sysprefix"; open (UUSTAT, "$UUSTAT -s $system -c rmail |"); print "set -ex\n"; while () { ($jobid, ) = split; ($cfile) = substr ($jobid, length ($jobid) - 5); $cfilename = "$directory/C./C.$cfile"; open (CFILE, $cfilename) || die "Cannot open $cfilename\n"; $_ = ; close CFILE; if (/^E D\.(....) [^ ]+ [^ ]+ -CR D\.\1 0666 [^ ]+ 0 rmail (.*)/) { $datafile = "$directory/D./D.$1"; $address = $2; } else { print STDERR; die "Cannot parse previous line from $cfilename\n"; } print "$SMAIL -R $system!$address < $datafile && $UUSTAT -k $jobid\n"; } close UUSTAT; } close SHELL unless $verbose; exit 0; uucp-1.07/contrib/uusnap.c0000664000076400007640000001704307665321760011250 /* uusnap.c (c) 1992 Heiko W.Rupp hwr@pilhuhn.ka.sub.org uusnap is a tool to display the activities of the connected systems. Put a file uusnap.systems in NEWCONFIGDIR (see Makefile), in which the systems, you want to monitor are listed, one on a single line. The sequence of the files there determine the sequence of the listing. At the moment it only works with taylor config and taylor dirs compile it form the Makefile or: cc -c -g -pipe -O -I. -I. -DNEWCONFIGLIB=\"/usr/local/lib/uucp\" uusnap.c cc -o uusnap uusnap.o For this, uusnap.[ch] must be in the same directory as uucp.h and so. uusnap must have read access to SPOOLDIR/.Status in order to work. */ #define MAXSYS 30 /* maximum number of systems */ #define WAIT_NORMAL 10 /* wait period if noone is talking */ #define WAIT_TALKING 2 /* refresh display every second if */ /* someone is talking with us */ #include "uucp.h" #if USE_RCS_ID char uusnap_rcsid[] = "$Id: uusnap.c,v 1.9 92/05/05 22:51:50 hwr Exp Locker: hwr $"; #endif #include #include #include #include extern char *ctime(time_t*); struct sysInfo { char sysname[10]; /* name of the system to watch */ char *statfile; /* name of its status file */ char *spooldir; /* root of its spooldir */ int in; /* number of unprocessed in-files */ int out; /* number of files to send them */ time_t last; /* last poll time */ time_t next; /* time of next poll */ time_t lastidir; /* time of last in-spooldir access */ time_t lastodir; /* time of last outgoing spd acc */ time_t laststat; /* time of last status file access */ int status; /* status of the system */ int num_retries; /* number of retries */ }; struct sysInfo Systems[MAXSYS]; /* I have extend the system status. If time for the specified system is Never, I say so. To get this to work, one also should extend uucico.c. It is not important to do this. With the normal uucico, one only gets no status. */ const char *azStatus[] = /* Status codes as defined by uucico */ { /* listing them here instead of */ "Conversation complete", /* including the appropriate file */ "Port unavailable", /* reduces the size of the executable */ "Dial failed", "Login failed", "Handshake failed", "Call failed", "Talking", "Wrong time to call", "Time to call = Never !" }; main() { int i; i=get_systems(); display_info(i); exit(0); } int get_systems() { char filename[1024]; char fn[1024]; char line[80]; FILE *fp; int i=0; int j; struct stat stbuf; struct sysInfo sys; strcpy(filename,NEWCONFIGLIB); strcat(filename,"/uusnap.systems"); if ((fp=fopen(filename,"r"))!=NULL) { while (fgets(line,80,fp)!=NULL) { *(rindex(line,'\n'))='\0'; strcpy(sys.sysname,line); /* get the name of the system */ strcpy(fn,SPOOLDIR); /* get the name of the statusfile */ strcat(fn,"/.Status/"); strcat(fn,line); sys.statfile=malloc(strlen(fn)+1); strcpy(sys.statfile,fn); strcpy(fn,SPOOLDIR); /* get the name of the spooldir */ strcat(fn,"/"); strcat(fn,line); sys.spooldir=malloc(strlen(fn)+1); strcpy(sys.spooldir,fn); sys.laststat=0; sys.lastidir=sys.lastodir=0; Systems[i]=sys; /* get_stat_for_system needs it */ get_stat_for_system(i); /* now get the system status */ get_inq_num(i,TRUE); /* number of unprocessed files */ get_outq_num(i,TRUE); /* number of files to send */ i++; } fclose(fp); } else { fprintf(stderr,"Can't open %s \n",filename); exit(1); } return i; } display_info(int numSys) { char *filename; int sysnum; FILE *fp; char contentline[80]; char isTalking=FALSE; struct stat stbuf; struct sysInfo sys; time_t time; filename = (char*)malloc(1024); if (filename == NULL) { fprintf(stderr, "Can't malloc 1024 bytes"); exit(1); } while(TRUE) { display_headline(); for (sysnum=0;sysnum sys.laststat) { get_stat_for_system(sysnum); } if(display_status_line(sysnum)==1) isTalking=TRUE; } if (isTalking) { sleep(WAIT_TALKING); isTalking = FALSE; } else sleep(WAIT_NORMAL); /* wait a bit */ } return 0; } int display_status_line(int sn) { char *time_s; int sys_stat,num_retries,wait; int i; time_t last_time; time_t next_time; struct sysInfo sys; sys = Systems[sn]; printf("%10s ",sys.sysname); get_inq_num(sn); if (sys.in==0) printf(" "); else printf("%3d ",sys.in); get_outq_num(sn); if (sys.out==0) printf(" "); else printf("%3d ",sys.out); time_s = ctime(&sys.last); time_s = time_s + 11; *(time_s+8)='\0'; printf("%8s ",time_s); /* time of last poll */ time_s = ctime(&sys.next); time_s = time_s + 11; *(time_s+8)='\0'; if (sys.last == sys.next) printf(" "); else printf("%8s ",time_s); /* time of next poll */ if (sys.num_retries==0) printf(" "); else printf("%2d ",sys.num_retries); if (sys_stat==6) /* system is talking */ printf("\E[7m"); /* reverse video on */ printf("%s",azStatus[sys.status]); if (sys.status==6) { printf("\E[m\n"); /* reverse video off */ return 1; } else { printf("\n"); return 0; } } display_headline() { printf("\E[;H\E[2J"); /* clear screen */ printf("\E[7muusnap (press CTRL-C to escape)\E[m \n\n"); printf(" System #in #out last next #ret Status\n"); return 0; } get_inq_num(int num,char firstTime) { int i=0; char filename[1024]; struct stat stbuf; DIR *dirp; strcpy(filename,Systems[num].spooldir); strcat(filename,"/X./."); stat(filename,&stbuf); if ((stbuf.st_mtime > Systems[num].lastidir) || (firstTime)) { if ((dirp=opendir(filename))!=NULL) { while(readdir(dirp)) i++; closedir(dirp); stat(filename,&stbuf); Systems[num].lastidir=stbuf.st_mtime; } else { fprintf(stderr,"Can't open %s \n",filename); exit(1); } if (i>=2) i-=2; /* correct . and .. */ Systems[num].in=i; } return 0; } get_outq_num(int sys,char firstTime) { int i=0; char filename[1024]; struct stat stbuf; DIR *dirp; strcpy(filename,Systems[sys].spooldir); strcat(filename,"/C./."); stat(filename,&stbuf); if ((stbuf.st_mtime > Systems[sys].lastodir) || (firstTime)) { if ((dirp=opendir(filename))!=NULL) { while(readdir(dirp)) i++; closedir(dirp); stat(filename,&stbuf); Systems[sys].lastodir=stbuf.st_mtime; } else { fprintf(stderr,"Can't open %s \n",filename); exit(1); } if (i>=2) i-=2; /* correct . and .. */ Systems[sys].out=i; } return 0; } get_stat_for_system(int i) { char fn[80]; struct sysInfo sys; struct stat stbuf; FILE *fp; time_t wait; sys = Systems[i]; stat(sys.statfile,&stbuf); if (stbuf.st_atime > sys.laststat) { if ((fp=fopen(sys.statfile,"r"))!=NULL) { fgets(fn,80,fp); fclose(fp); sscanf(fn,"%d %d %ld %d", &sys.status, &sys.num_retries, &sys.last, &wait); sys.next=sys.last+wait; } else { sys.status=0; sys.num_retries=0; sys.last=0; sys.next=0; } stat(sys.statfile,&stbuf); sys.laststat=stbuf.st_atime; } Systems[i] = sys; return 0; } uucp-1.07/contrib/uutraf0000664000076400007640000001307707665321760011025 #!/usr/bin/perl # uutraf.pl -- UUCP Traffic Analyzer # SCCS Status : @(#)@ uutraf 1.8 # Author : Johan Vromans # Created On : *** # Last Modified By: Johan Vromans # Last Modified On: Mon Aug 30 15:02:22 1993 # Update Count : 6 # Status : OK # Requires: : Perl V4 or later # Reads UUCP syslog, and generates a report from it. # # Created by Johan Vromans # Loosely based on an idea by Greg Hackney (hack@texbell.swbt.com) # Usage: uutraf [-taylor|-hdb|-bnu|-bsd] [syslog] # Logfile formats: # # BSD: # # jv mhres (2/23-5:18) (698818735) received 135 b 2 secs # root mhres (2/23-5:19) (698818742) sent 2365 b 3 secs, Pk: 38, Rxmt: 0 # # HDB: # # uunet!uucp M (12/10-09:04:22) (C,16390,1) [ttyXX] <- 2371 / 5.000 secs, \ # 474 bytes/sec # # Taylor: # # jv mhres (1992-02-24 20:49:04.06) sent 16234 bytes in 148.780 seconds \ # (109 bytes/sec) # jv mhres (1992-02-24 21:04:05.76) received 449 bytes in 6.550 seconds \ # (68 bytes/sec) $uucp_type = "gnu"; %hosts = (); # hosts seen %bytes_in = (); # of bytes received from host %bytes_out = (); # of bytes sent to host %secs_in = (); # of seconds connect for recving %secs_out = (); # of seconds connect for sending %files_in = (); # of input requests %files_out = (); # of output requests # read info, break the lines and tally if ( $ARGV[0] =~ /^-/ ) { ($uucp_type = substr (shift (@ARGV), 1)) =~ tr/A-Z/a-z/; } if ( $uucp_type eq "taylor" || $uucp_type eq "gnu" ) { @ARGV = ("/usr/local/spool/uucp/Stats") unless $#ARGV >= 0; $pat = "^[^ ]+ ([^ ]+) \\(([-0-9:\\/ .]+)\\) " . "(sent|received) (\\d+) bytes in (\\d+)\\.(\\d+) seconds"; $uucp_type = 0; $recv = "received"; } elsif ( $uucp_type eq "hdb" || $uucp_type eq "bnu" ) { @ARGV = ("/usr/spool/uucp/.Admin/xferstats") unless $#ARGV >= 0; $pat = "^([^!]+)![^(]+\\(([-0-9:\\/]+)\\).+([<>])-? " . "(\\d+) \\/ (\\d+)\\.(\\d+) secs"; $uucp_type = 1; $recv = "<"; } elsif ( $uucp_type eq "bsd" || $uucp_type eq "v7" ) { @ARGV = ("/usr/spool/uucp/SYSLOG") unless $#ARGV >= 0; $pat = "^[^ ]+ ([^ ]+) \\(([-0-9:\\/]+)\\) \\([^)]+\\) " . "(sent|received) (\\d+) b (\\d+) secs"; $uucp_type = 2; $recv = "received"; } else { die ("FATAL: Unknown UUCP type: $uucp_type\n"); } $garbage = 0; while ( <> ) { unless ( /$pat/o ) { print STDERR "$_"; next if /failed/; if ( $garbage++ > 10 ) { die ("FATAL: Too much garbage; wrong UUCP type?\n"); } next; } # gather timestamps $last_date = $2; $first_date = $last_date unless defined $first_date; # initialize new hosts unless ( defined $hosts{$1} ) { $hosts{$1} = $files_in{$1} = $files_out{$1} = $bytes_in{$1} = $bytes_out{$1} = $secs_in{$1} = $secs_out{$1} = 0; } # Taylor and HDB have milliseconds, BSD has not. $secs = ($uucp_type == 2) ? ($5 + ($5 == 0 ? 0.5 : 0)) : ($5 + $6/1000); # tally if ( $3 eq $recv ) { # recv $bytes_in{$1} += $4; $files_in{$1}++; $secs_in{$1} += $secs; } else { # xmit $bytes_out{$1} += $4; $files_out{$1}++; $secs_out{$1} += $secs; } $garbage = 0; } @hosts = keys (%hosts); die ("No info found, stopped\n") if $#hosts < 0; ################ report section ################ $thishost = &gethostname(); $thishost = (defined $thishost) ? "on node $thishost" : "report"; if ( $uucp_type eq 0 ) { # Taylor UUCP substr ($first_date, 16) = ""; substr ($last_date, 16) = ""; } format std_head = @||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| "UUCP traffic $thishost from $first_date to $last_date" Remote -----------K-Bytes----------- ----Hours---- --Avg CPS-- --Files-- Host Recv Sent Total Recv Sent Recv Sent Recv Sent . format std_out = @<<<<<<< @>>>>>>>> @>>>>>>>> @>>>>>>>> @>>>>> @>>>>> @>>>> @>>>> @>>> @>>> $Zhost, $Zi_bytes, $Zo_bytes, $Zt_bytes, $Zi_hrs, $Zo_hrs, $Zi_acps, $Zo_acps, $Zi_count, $Zo_count . $^ = "std_head"; $~ = "std_out"; &print_dashes (); reset "T"; # reset totals foreach $host (@hosts) { &print_line ($host, $bytes_in{$host}, $bytes_out{$host}, $secs_in{$host}, $secs_out{$host}, $files_in{$host}, $files_out{$host}); } &print_dashes (); &print_line ("Total", $Ti_bytes, $To_bytes, $Ti_secs, $To_secs, $Ti_count, $To_count); ################ that's it ################ sub print_line { reset "Z"; # reset print fields local ($Zhost, $Zi_bytes, $Zo_bytes, $Zi_secs, $Zo_secs, $Zi_count, $Zo_count) = @_; $Ti_bytes += $Zi_bytes; $To_bytes += $Zo_bytes; $Zt_bytes = $Zi_bytes + $Zo_bytes; $Tt_bytes += $Zt_bytes; $Zi_acps = ($Zi_secs > 0) ? sprintf ("%.0f", $Zi_bytes/$Zi_secs) : "0"; $Zo_acps = ($Zo_secs > 0) ? sprintf ("%.0f", $Zo_bytes/$Zo_secs) : "0"; $Zi_bytes = sprintf ("%.1f", $Zi_bytes/1000); $Zo_bytes = sprintf ("%.1f", $Zo_bytes/1000); $Zt_bytes = sprintf ("%.1f", $Zt_bytes/1000); $Zi_hrs = sprintf ("%.1f", $Zi_secs/3600); $Zo_hrs = sprintf ("%.1f", $Zo_secs/3600); $Ti_secs += $Zi_secs; $To_secs += $Zo_secs; $Ti_count += $Zi_count; $To_count += $Zo_count; write; } sub print_dashes { $Zhost = $Zi_bytes = $Zo_bytes = $Zt_bytes = $Zi_hrs = $Zo_hrs = $Zi_acps = $Zo_acps = $Zi_count = $Zo_count = "------------"; write; # easy, isn't it? } ################ missing ################ sub gethostname { $ENV{"SHELL"} = "/bin/sh"; $try = `(hostname) 2>/dev/null`; chop $try; return $+ if $try =~ /^[-.\w]+$/; $try = `uname -n 2>/dev/null`; chop $try; return $+ if $try =~ /^[-.\w]+$/; $try = `uuname -l 2>/dev/null`; chop $try; return $+ if $try =~ /^[-.\w]+$/; return undef; } uucp-1.07/contrib/uutry0000664000076400007640000000170307665321760010700 #!/bin/sh # # This script was hacked together by Marc Evans (marc@Synergytics.Com) # I claim no copyright to it and don't really care what people do # with it, hence, it is public domain. I take no responsibility for # for happens if you use this script, providing no warentee. This # section of the comments may be removed if you so desire. # # Usage: # uutry [-x#] systemname # where '-x#' has the value [0-9], higher values providing more detail # # The following variables should be gropped from the configuration # files rather then being hard coded here. # Spool=/usr/spool/uucp Lib=/usr/local/lib/uucp Status=$Spool/.Status Debug=$Spool/Debug Uucico=$Lib/uucico # # Default option values # x="-x5" s="" for i in $* ; do case $i in -x*) x="$i" ;; *) s="$i" ;; esac done if [ x$s != x ]; then rm -f $Status/$s $Uucico -r1 $x -s$s & >$Debug tail -f $Debug else echo "Usage: uutry systemname" exit 1 fi uucp-1.07/contrib/uuxconv0000775000076400007640000000256707665321760011233 #!/bin/sh # # uuxconv # # After converting to Taylor from SVR4.03 UUCP, I still had a lot of # jobs queued up to go out. # # This script is a one-shot to mass-requeue a site's spool. # # It doesn't go and do your whole spool, nor even all grades. # you need to go into each grade directory for each site and # execute this. # # i.e.: You have a site named 'foo' # cd /var/spool/uucp/foo/Z # uuxconv foo # # it does delete the 'D' & 'X' after requeing them, but doesn't remove # the 'C' files. # # I foolishly went and ran this script on all my queued jobs, without # adding the improvements to recursively go through the entire UUCP spool, # so now I'm out of files to test with. I don't want to add the code # to do that since I can't test it, and this worked. # # I hereby give this (trivial :-)) program to the GNU Project/FSF # and Ian Taylor in it's entirety, so that it can be placed in # the contrib directory of taylor-uucp, and save others the pain # of rewriting it. # # Richard Nickle (rick@trystro.uucp, rnickle@gnu.ai.mit.edu) # May 27, 1993 # if [ $# -eq 0 ] then echo "Usage: $0 sitename" exit 1 fi exit 0 site=$1 tsite=`echo $site | cut -c1-5` find . -name "D.$tsite*" -print | while read file do control=`egrep "^C" $file | cut -c3-` input=`egrep "^I" $file | cut -c3-` (uux - -r -z $site!$control < $input) && (rm $file $input) echo "$site!$control < $input" done exit 0 uucp-1.07/contrib/xc-conf.h-dist0000664000076400007640000000230707665321760012235 /* * ************* * * XC-CONF.H * * ************* * * Configuration file for xchat 1.1. Edit this file prior to make-ing * xchat. * * History: * Bob Denny - Tue Sep 1 11:42:54 1992 */ /* * Edit this to reflect the relative location of xchat sources to * the main Taylor UUCP source directory. As distributed, xchat * is in the ./contrib sub-directory under the main Taylor UUCP * directory. Therefore, Taylor's conf.h is in our parent directory. */ #include "../config.h" /* * The following definition establishes the default path to the * scripts used by xchat. You may lleave this blank (""), but * the command line given to xchat (e.g., in the 'sys' file entry) * must specify a full (absolute) path name to the script to be * executed. Normally, this is the same place you put your config * and system files for UUCP. */ #define SCRIPT_DIR "/usr/local/conf/uucp/" /* MUST HAVE TRAILING "/" */ /* * The following definition establishes the default path to the * log files that are produced by the 'dbgfile' statement. Normally * this is the same location you configured Taylor UUCP to put its * log files. */ #define LOG_DIR "/usr/spool/uucp/" /* MUST HAVE TRAILING "/" */ uucp-1.07/contrib/xchat.c0000664000076400007640000010346307665321760011046 /* * *********** * * XCHAT.C * * *********** * * Extended chat processor for Taylor UUCP. See accompanying documentation. * * Written by: * Bob Denny (denny@alisa.com) * Based on code in DECUS UUCP (for VAX/VMS) * * Small modification by: * Daniel Hagerty (hag@eddie.mit.edu) * * History: * Version 1.0 shipped with Taylor 1.03. No configuration info inside. * * Bob Denny - Sun Aug 30 18:41:30 1992 * V1.1 - long overdue changes for other systems. Rip out interval * timer code, use timer code from Taylor UUCP, use select() * for timed reads. Use Taylor UUCP "conf.h" file to set * configuration for this program. Add defaulting of script * and log file paths. * * Daniel Hagerty - Mon Nov 22 18:17:38 1993 * V1.2 - Added a new opcode to xchat. "expectstr" is a cross between * sendstr and expect, looking for a parameter supplied string. * Useful where a prompt could change for different dial in * lines and such. * * Bugs: * Does not support BSD terminal I/O. Anyone care to add it? */ #include #include #include #include #include #include #include #include #include "xc-conf.h" /* * Pick a timing routine to use, as done in Taylor UUCP. */ #if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS || HAVE_POLL #define USE_SELECT_TIMER 0 #else #define USE_SELECT_TIMER HAVE_SELECT #if USE_SELECT_TIMER #include #endif #endif #if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS #undef HAVE_POLL #define HAVE_POLL 0 #endif #if HAVE_USLEEP || HAVE_NAP #undef HAVE_NAPMS #define HAVE_NAPMS 0 #endif #if HAVE_USLEEP #undef HAVE_NAP #define HAVE_NAP 0 #endif static int ttblind(); static int ttcd(); /* script entry -- "compiled" form of dial, hangup, or login script */ struct script { struct script *next; /* pointer to next entry, or null */ int opcode; /* numeric opcode */ char *strprm; /* pointer to string param */ long intprm; /* integer parameter */ char *newstate; /* new state name */ }; /* opcode definition array element -- one for each possible opcode */ struct script_opdef { char *opname; int opcode; /* numeric opcode -- same as array index */ int prmtype; /* one of SC_NONE, SC_STR, SC_XSTR, SC_INT */ int newstate; /* one of SC_NONE, SC_NWST */ }; /* values for opcode */ #define SC_LABEL 0 /* "label" (state name) */ #define SC_CDLY 1 /* set char output delay in msec */ #define SC_PCHR 2 /* pause char for dial string (from P in input) */ #define SC_PTIM 3 /* seconds to allow for pause char */ #define SC_WCHR 4 /* wait char for dial string (from W in input) */ #define SC_WTIM 5 /* seconds to allow for wait char */ #define SC_ZERO 6 /* zero counter */ #define SC_INCR 7 /* increment counter */ #define SC_IFGT 8 /* change state if counter > int param */ #define SC_WAIT 9 /* wait for int param seconds */ #define SC_GOTO 10 /* unconditional change to new state */ #define SC_SEND 11 /* send strparam (after sprintf substitutions) */ #define SC_BRK 12 /* send a break */ #define SC_HANG 13 /* drop DTR */ #define SC_DIAL 14 /* send telno string (after subst PCHR & WCHR) */ #define SC_DTIM 15 /* time in msec per digit (for timeout calculations) */ /* default = 100 (one tenth second) */ #define SC_CTIM 16 /* additional time (in seconds) to wait for carrier */ /* default = 45 seconds */ #define SC_EXIT 17 /* script done, success */ #define SC_FAIL 18 /* script done, failure */ #define SC_LOG 19 /* write strparam to uucp.log */ #define SC_LOGE 20 /* write strparam to uucp.log w/error ind */ #define SC_DBG 21 /* write strparam to debug log if debug lvl = LGI */ #define SC_DBGE 22 /* write strparam to debug log if debug lvl = LGIE */ #define SC_DBST 23 /* 'or' intparam into debug mask */ #define SC_DBCL 24 /* 'bicl' intparam into debug mask */ #define SC_TIMO 25 /* newstate if no match in intparam secs */ /* (uses calculated dial time if intparam is 0) */ #define SC_XPCT 26 /* wait for strparam, goto _newstate if found */ #define SC_CARR 27 /* goto _newstate if carrier detected */ #define SC_FLSH 28 /* flush typeahead buffer */ #define SC_IFBL 29 /* change state if controller is blind w/o CD */ #define SC_IFBG 30 /* chg state if ctlr is blind and counter > intprm */ #define SC_SNDP 31 /* send parameter n */ #define SC_IF1P 32 /* if parameter n present */ #define SC_IF0P 33 /* if parameter n absent */ #define SC_DBOF 34 /* open debugging file */ #define SC_TELN 35 /* Set telno from parameter n */ #define SC_7BIT 36 /* Set port to 7-bit stripping */ #define SC_8BIT 37 /* Set port for 8-bit characters */ #define SC_PNON 38 /* Set port for 8-bit, no parity */ #define SC_PEVN 39 /* Set port for 7-bit, even parity */ #define SC_PODD 40 /* Set port for 7-bit, odd parity */ #define SC_HUPS 41 /* Change state on HUP signal */ #define SC_XPST 42 /* Expect a param string */ #define SC_END 43 /* end of array */ /* values for prmtype, prm2type */ #define SC_NONE 0 /* no parameter */ #define SC_STR 1 /* simple string */ #define SC_INT 2 /* integer */ #define SC_NWST 3 /* new state name */ #define SC_XSTR 4 /* translated string */ /* opcode definition table for dial/login/hangup scripts */ static struct script_opdef sc_opdef[] = { {"label", SC_LABEL, SC_NONE, SC_NONE}, {"chrdly", SC_CDLY, SC_INT, SC_NONE}, {"pchar", SC_PCHR, SC_STR, SC_NONE}, {"ptime", SC_PTIM, SC_INT, SC_NONE}, {"wchar", SC_WCHR, SC_STR, SC_NONE}, {"wtime", SC_WTIM, SC_INT, SC_NONE}, {"zero", SC_ZERO, SC_NONE, SC_NONE}, {"count", SC_INCR, SC_NONE, SC_NONE}, {"ifgtr", SC_IFGT, SC_INT, SC_NWST}, {"sleep", SC_WAIT, SC_INT, SC_NONE}, {"goto", SC_GOTO, SC_NONE, SC_NWST}, {"send", SC_SEND, SC_XSTR, SC_NONE}, {"break", SC_BRK, SC_NONE, SC_NONE}, {"hangup", SC_HANG, SC_NONE, SC_NONE}, {"7bit", SC_7BIT, SC_NONE, SC_NONE}, {"8bit", SC_8BIT, SC_NONE, SC_NONE}, {"nopar", SC_PNON, SC_NONE, SC_NONE}, {"evenpar", SC_PEVN, SC_NONE, SC_NONE}, {"oddpar", SC_PODD, SC_NONE, SC_NONE}, {"telno", SC_TELN, SC_INT, SC_NONE}, {"dial", SC_DIAL, SC_NONE, SC_NONE}, {"dgttime", SC_DTIM, SC_INT, SC_NONE}, {"ctime", SC_CTIM, SC_INT, SC_NONE}, {"success", SC_EXIT, SC_NONE, SC_NONE}, {"failed", SC_FAIL, SC_NONE, SC_NONE}, {"log", SC_LOG, SC_XSTR, SC_NONE}, {"logerr", SC_LOGE, SC_XSTR, SC_NONE}, {"debug", SC_DBG, SC_XSTR, SC_NONE}, {"debuge", SC_DBGE, SC_XSTR, SC_NONE}, {"dbgset", SC_DBST, SC_INT, SC_NONE}, {"dbgclr", SC_DBCL, SC_INT, SC_NONE}, {"dbgfile", SC_DBOF, SC_XSTR, SC_NONE}, {"timeout", SC_TIMO, SC_INT, SC_NWST}, {"expect", SC_XPCT, SC_XSTR, SC_NWST}, {"ifcarr", SC_CARR, SC_NONE, SC_NWST}, {"ifhang", SC_HUPS, SC_NONE, SC_NWST}, {"flush", SC_FLSH, SC_NONE, SC_NONE}, {"ifblind", SC_IFBL, SC_NONE, SC_NWST}, {"ifblgtr", SC_IFBG, SC_INT, SC_NWST}, {"sendstr", SC_SNDP, SC_INT, SC_NONE}, {"ifstr", SC_IF1P, SC_INT, SC_NWST}, {"ifnstr", SC_IF0P, SC_INT, SC_NWST}, {"expectstr", SC_XPST, SC_INT, SC_NWST}, {"table end", SC_END, SC_NONE, SC_NONE} }; #define SUCCESS 0 #define FAIL 1 #define ERROR -1 #define MAX_SCLINE 255 /* max length of a line in a script file */ #define MAX_EXPCT 127 /* max length of an expect string */ #define CTL_DELIM " \t\n\r" /* Delimiters for tokens */ #define SAME 0 /* if (strcmp(a,b) == SAME) ... */ #define SLOP 10 /* Slop space on arrays */ #define MAX_STRING 200 /* Max length string to send/expect */ #define DEBUG_LEVEL(level) \ (Debug & (1 << level)) #define DB_LOG 0 /* error messages and a copy of the LOGFILE output */ #define DB_LGIE 1 /* dial,login,init trace -- errors only */ #define DB_LGI 2 /* dial,login,init trace -- nonerrors (incl chr I/O) */ #define DB_LGII 3 /* script processing internals */ #define TRUE 1 #define FALSE 0 #define NONE 0 #define EVEN 1 #define ODD 2 #define logit(m, p1) fprintf(stderr, "%s %s\n", m, p1) static char **paramv; /* Parameter vector */ static int paramc; /* Parameter count */ static char telno[64]; /* Telephone number w/meta-chars */ static int Debug; static int fShangup = FALSE; /* TRUE if HUP signal received */ static FILE *dbf = NULL; static struct termio old, new; extern int usignal(); extern int uhup(); static struct siglist { int signal; int (*o_catcher) (); int (*n_catcher) (); } sigtbl[] = { { SIGHUP, NULL, uhup }, { SIGINT, NULL, usignal }, { SIGIOT, NULL, usignal }, { SIGQUIT, NULL, usignal }, { SIGTERM, NULL, usignal }, { SIGALRM, NULL, usignal }, { 0, NULL, NULL } /* Table end */ }; extern struct script *read_script(); extern void msleep(); extern char xgetc(); extern void charlog(); extern void setup_tty(); extern void restore_tty(); extern void ttoslow(); extern void ttflui(); extern void tthang(); extern void ttbreak(); extern void tt7bit(); extern void ttpar(); extern void DEBUG(); extern void *malloc(); /* * ********************************** * * BEGIN EXECUTION - MAIN PROGRAM * * ********************************** * * This program is called by Taylor UUCP with a list of * arguments in argc/argv, and stdin/stdout mapped to the * tty device, and stderr mapped to the Taylor logfile, where * anything written to stdout will be logged as an error. * */ int main(argc, argv) int argc; char *argv[]; { int i, stat; FILE *sf; char sfname[256]; struct script *script; struct siglist *sigs; /* * The following is needed because my cpp does not have the * #error directive... */ #if ! HAVE_SELECT no_select_sorry(); /* Sad way to fail make */ #endif paramv = &argv[2]; /* Parameters start at 2nd arg */ paramc = argc - 2; /* Number of live parameters */ telno[0] = '\0'; if (argc < 2) { fprintf(stderr, "%s: no script file supplied\n", argv[0]); exit(FAIL); } /* * If the script file argument begins with '/', then we assume * it is an absolute pathname, otherwise, we prepend the * SCRIPT_DIR path. */ *sfname = '\0'; /* Empty name string */ if(argv[1][0] != '/') /* If relative path */ strcat(sfname, SCRIPT_DIR); /* Prepend the default dir. */ strcat(sfname, argv[1]); /* Add the script file name */ /* * Now open the script file. */ if ((sf = fopen(sfname, "r")) == NULL) { fprintf(stderr, "%s: Failed to open script %s\n", argv[0], sfname); perror(" "); exit(FAIL); } /* * COMPILE SCRIPT */ if ((script = read_script(sf)) == NULL) { fprintf(stderr, "%s: script error in \"%s\"\n", argv[0], argv[1]); exit(FAIL); } /* * Set up a signal catcher so the line can be returned to * it's current state if something nasty happens. */ sigs = &sigtbl[0]; while(sigs->signal) { sigs->o_catcher = (int (*) ())signal(sigs->signal, sigs->n_catcher); sigs += 1; } /* * Save current tty settings, then set up raw, single * character input processing, with 7-bit stripping. */ setup_tty(); /* * EXECUTE SCRIPT */ if ((stat = do_script(script)) != SUCCESS) fprintf(stderr, "%s: script %s failed.\n", argv[0], argv[1]); /* * Clean up and exit. */ restore_tty(); #ifdef FIXSIGS sigs = &sigtbl[0]; while(sigs->signal) if(sigs->o_catcher != -1) signal(sigs->signal, sigs->o_catcher); #endif exit(stat); } /* * deal_script - deallocate a script and all strings it points to */ int deal_script(loc) struct script *loc; { /* * If pointer is null, just exit */ if (loc == (struct script *)NULL) return SUCCESS; /* * Deallocate the rest of the script */ deal_script(loc->next); /* * Deallocate the string parameter, if any */ if (loc->strprm != (char *)NULL) free(loc->strprm); /* * Deallocate the new state name parameter, if any */ if (loc->newstate != (char *)NULL) free(loc->newstate); /* * Deallocate this entry */ free(loc); return SUCCESS; } /* * read_script * * Read & compile a script, return pointer to first entry, or null if bad */ struct script *read_script(fd) FILE *fd; { struct script *this = NULL; struct script *prev = NULL; struct script *first = NULL; long len, i; char inpline[MAX_SCLINE]; char inpcopy[MAX_SCLINE]; char *c, *cln, *opc, *cp; /* * MAIN COMPILATION LOOP */ while ((c = fgets(inpline, (sizeof inpline - 1), fd)) != (char *)NULL) { /* * Skip comments and blank lines */ if (*c == '#' || *c == '\n') continue; /* * Get rid of the trailing newline, and copy the string */ inpline[strlen(inpline)-1] = '\0'; strcpy(inpcopy, inpline); /* * Look for text starting in the first col (a label) */ if ((!isspace(inpline[0])) && (cln = strchr (inpline, ':')) != (char *)NULL) { this = (struct script *)malloc (sizeof (struct script)); if (prev != (struct script *)NULL) prev->next = this; prev = this; if (first == (struct script *)NULL) first = this; this->next = (struct script *)NULL; this->opcode = SC_LABEL; len = cln - c; this->strprm = (char *)malloc(len+1); strncpy(this->strprm, c, len); (this->strprm)[len] = '\0'; this->intprm = 0; this->newstate = (char *)NULL; c = cln + 1; } /* * Now handle the opcode. Fold it to lower case. */ opc = strtok(c, CTL_DELIM); if (opc == (char *)NULL) /* If no opcode... */ continue; /* ...read the next line */ cp = opc; while(*cp) tolower(*cp++); /* * If we have an opcode but we haven't seen anything * else (like a label) yet, i.e., this is the first * entry, and there was no label. We need to * cobble up a label so that read_script is happy */ if (first == (struct script *)NULL) { this = (struct script *)malloc (sizeof (struct script)); prev = this; first = this; this->next = (struct script *)NULL; this->opcode = SC_LABEL; this->strprm = (char *)malloc(2); strcpy(this->strprm, ":"); this->intprm = 0; this->newstate = (char *)NULL; } /* * Find opcode - ndex through the opcode definition table */ for (i=1; sc_opdef[i].opcode != SC_END; i++) if (strcmp(opc, sc_opdef[i].opname) == SAME) break; if ((sc_opdef[i].opcode) == SC_END) { logit ("Bad opcode in script", opc); deal_script(first); return (struct script *)NULL; } /* * Found opcode. Allocate a new command node and initialize */ this = (struct script *)malloc(sizeof (struct script)); prev->next = this; prev = this; this->next = (struct script *)NULL; this->opcode = sc_opdef[i].opcode; this->strprm = (char *)NULL; this->intprm = 0; this->newstate = (char *)NULL; /* * Pick up new state parameter, if any */ if (sc_opdef[i].newstate == SC_NWST) { c = strtok((char *)NULL, CTL_DELIM); if (c == (char *)NULL) { logit("Missing new state", opc); deal_script(first); return (struct script *)NULL; } else { this->newstate = (char *)malloc(strlen(c)+1); strcpy(this->newstate, c); } } /* * Pick up the string or integer parameter. Handle missing * parameter gracefully. */ switch (sc_opdef[i].prmtype) { /* * INT parameter - convert and store in node */ case SC_INT: c = strtok((char *)NULL, CTL_DELIM); if (c == (char *)NULL) { logit("Missing script param", opc); deal_script(first); return (struct script *)NULL; } /* * If this is the parameter to DBST or DBCL, force * base-10 conversion, else convert per parameter. */ if (sc_opdef[i].opcode == SC_DBST || sc_opdef[i].opcode == SC_DBCL) this->intprm = strtol(c, (char **)NULL, 0); else this->intprm = strtol(c, (char **)NULL, 10); break; /* * STR/XSTR strings. */ case SC_STR: case SC_XSTR: c = strtok((char *)NULL, CTL_DELIM); if (c == (char *)NULL) { logit("Missing script param", opc); deal_script(first); return (struct script *)NULL; } /* * For XSTR opcode, use c to find out where * the string param begins in the copy of the * input line, and pick up all that's left of * the line (to allow imbedded blanks, etc.). */ if (sc_opdef[i].prmtype == SC_XSTR) c = &inpcopy[0] + (c - &inpline[0]); /* * Allocate a buffer for the string parameter */ this->strprm = (char *)malloc(strlen(c)+1); /* * For XSTR, Translate the string and store its * length. Note that, after escape sequences are * compressed, the resulting string may well be a * few bytes shorter than the input string (whose * length was the basis for the malloc above), * but it will never be longer. */ if (sc_opdef[i].prmtype == SC_XSTR) { this->intprm = xlat_str(this->strprm, c); this->strprm[this->intprm] = '\0'; } else strcpy(this->strprm, c); break; } } /* * EOF */ return first; } /* * xlat_str * * Translate embedded escape characters in a "send" or "expect" string. * * Called by read_script(), above. * * Returns the actual length of the resulting string. Note that imbedded * nulls (specified by \000 in the input) ARE allowed in the result. */ xlat_str(out, in) char *out, *in; { register int i = 0, j = 0; int byte, k; while (in[i]) { if (in[i] != '\\') { out[j++] = in[i++]; } else { switch (in[++i]) { case 'd': /* EOT */ out[j++] = 0x04; break; case 'N': /* null */ out[j++] = 0x00; break; case 'n': /* line feed */ out[j++] = 0x0a; break; case 'r': /* carriage return */ out[j++] = 0x0d; break; case 's': /* space */ out[j++] = ' '; break; case 't': /* tab */ out[j++] = '\t'; break; case '-': /* hyphen */ out[j++] = '-'; break; case '\\': /* back slash */ out[j++] = '\\'; break; case '0': /* '\nnn' format */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': byte = in[i] - '0'; k = 0; while (3 > ++k) if ((in[i+1] < '0') || (in[i+1] > '7')) break; else { byte = (byte<<3) + in[i+1] - '0'; ++i; } out[j++] = byte; break; default: /* don't know so skip it */ break; } ++i; } } return j; } /* find a state within a script */ struct script * find_state(begin, newstate) struct script *begin; char *newstate; { struct script *here; for (here=begin; here != (struct script *)NULL; here=here->next) { if (here->opcode == SC_LABEL && strcmp(here->strprm, newstate) == SAME) return here; } return (struct script *)NULL; } /* * do_script() - execute a script */ int do_script(begin) struct script *begin; { struct script *curstate, *newstate, *curscr; int dbgsave; char tempstr[MAX_SCLINE]; char dfname[256]; char *c, chr; int prmlen; int dbfd; time_t sc_carrtime = 45000; /* time to wf carr after dial */ time_t sc_chrdly = 100; /* delay time for ttoslow */ time_t sc_ptime = 2000; /* time to allow for pause char */ time_t sc_wtime = 10000; /* time to allow for wait char */ time_t sc_dtime = 100; /* time to allow for each digit */ time_t sc_dtmo; /* total time to dial number */ int sc_counter; /* random counter */ char sc_pchar = ','; /* modem pause character */ char sc_wchar = 'W'; /* modem wait-for-dialtone character */ time_t sc_begwait; /* time at beg of wait */ time_t sc_secs; /* timeout period */ int expcnt; int expin; static char expbuf[MAX_EXPCT]; dbgsave = Debug; curstate = begin; if (curstate == (struct script *)NULL) return SUCCESS; _newstate: /* * do all of curstate's actions. Enter with curstate pointing * to a label entry */ expin = 0; for (curscr = curstate->next; /* point to 1st scr after label */ (curscr != (struct script *)NULL) && /* do until end of scr */ (curscr->opcode != SC_LABEL); /* or next label */ curscr = curscr->next) { expcnt = 0; switch (curscr->opcode) { case SC_LABEL: logit("Script proc err", curstate->strprm); return FAIL; case SC_FLSH: DEBUG(DB_LGII, "Flushing typeahead buffer\n", 0); ttflui(); break; case SC_CDLY: sc_chrdly = curscr->intprm; DEBUG(DB_LGII, "Set chrdly to %d\n", sc_chrdly); break; case SC_PCHR: sc_pchar = *(curscr->strprm); DEBUG(DB_LGII, "Set pause char to %c\n", sc_pchar); break; case SC_PTIM: sc_ptime = curscr->intprm; DEBUG(DB_LGII, "Set pause time to %d\n", sc_ptime); break; case SC_WCHR: sc_wchar = *(curscr->strprm); DEBUG(DB_LGII, "Set wait char to %c\n", sc_wchar); break; case SC_WTIM: sc_wtime = curscr->intprm; DEBUG(DB_LGII, "Set wait time to %d\n", sc_wtime); break; case SC_ZERO: sc_counter = 0; DEBUG(DB_LGII, "Set counter to %d\n", sc_counter); break; case SC_INCR: sc_counter++; DEBUG(DB_LGII, "Incr counter to %d\n", sc_counter); break; case SC_WAIT: DEBUG(DB_LGII, "Sleeping %d tenth-secs\n", curscr->intprm); msleep(curscr->intprm); break; case SC_DTIM: sc_dtime = curscr->intprm; DEBUG(DB_LGII, "Digit time is %d\n", sc_dtime); break; case SC_CTIM: sc_carrtime = curscr->intprm; DEBUG(DB_LGII, "Carrier time is %d\n", sc_carrtime); break; case SC_EXIT: Debug = dbgsave; DEBUG(DB_LGI, "Script ended successfully\n", 0); return SUCCESS; case SC_FAIL: Debug = dbgsave; if (DEBUG_LEVEL(DB_LGI) && dbf != NULL) fprintf(dbf, "Script failed\n"); else if (expin) charlog(expbuf, expin, DB_LOG, "Script failed. Last received data"); return FAIL; case SC_LOG: logit(curscr->strprm, ""); break; case SC_LOGE: logit("ERROR: ", curscr->strprm); break; case SC_DBOF: /* * If the debug file name does not begin with "/", then * we prepend the LOG_DIR to the string. Then CREATE the * file. This WIPES OUT previous logs. */ *dfname = '\0'; /* Zero name string */ if(curscr->strprm[0] != '/') strcat(dfname, LOG_DIR); /* Prepend default directory */ strcat(dfname, curscr->strprm); /* Add given string */ DEBUG(DB_LGII, "Open debug file %s\n", dfname); if ((dbfd = creat (dfname, 0600)) <= 0) { logit("Failed to create debug log %s", dfname); perror(""); return FAIL; } if ((dbf = fdopen(dbfd, "w")) == NULL) { logit("Failed to open debug log fildes.", ""); perror(""); return FAIL; } break; case SC_DBG: DEBUG(DB_LGI, "<%s>\n", curscr->strprm); break; case SC_DBGE: DEBUG(DB_LGIE, "ERROR: <%s>\n", curscr->strprm); break; case SC_DBST: Debug |= curscr->intprm; DEBUG(DB_LGII, "Debug mask set to %04o (octal)\n", Debug); break; case SC_DBCL: Debug &= ~(curscr->intprm); DEBUG(DB_LGII, "Debug mask set to %04o (octal)\n", Debug); break; case SC_BRK: DEBUG(DB_LGI, "Sending break\n", 0); ttbreak(); break; case SC_HANG: DEBUG(DB_LGI, "Dropping DTR\n", 0); tthang(); break; case SC_7BIT: DEBUG(DB_LGI, "Enabling 7-bit stripping\n", 0); tt7bit(TRUE); break; case SC_8BIT: DEBUG(DB_LGI, "Disabling 7-bit stripping\n", 0); tt7bit(FALSE); break; case SC_PNON: DEBUG(DB_LGI, "Setting 8-bit, no parity\n", 0); ttpar(NONE); break; case SC_PEVN: DEBUG(DB_LGI, "Setting 7-bit, even parity\n", 0); ttpar(EVEN); break; case SC_PODD: DEBUG(DB_LGI, "Setting 7-bit, odd parity\n", 0); ttpar(ODD); break; case SC_IFBL: if (ttblind()) { DEBUG(DB_LGI, "Blind mux,\n", 0); goto _chgstate; } break; case SC_IFBG: if (ttblind() && sc_counter > curscr->intprm) { DEBUG(DB_LGI, "Blind mux & ctr > %d\n", curscr->intprm); goto _chgstate; } break; case SC_IFGT: if (sc_counter > curscr->intprm) { DEBUG(DB_LGI, "Counter > %d\n", curscr->intprm); goto _chgstate; } break; case SC_GOTO: _chgstate: DEBUG(DB_LGI, "Changing to state %s\n", curscr->newstate); curstate = find_state(begin, curscr->newstate); if (curstate == NULL) { logit("New state not found", curscr->newstate); return FAIL; } goto _newstate; case SC_SEND: ttoslow(curscr->strprm, curscr->intprm, sc_chrdly); break; case SC_TELN: if (curscr->intprm > paramc - 1) { sprintf(tempstr, "telno - param #%d", curscr->intprm); logit(tempstr, " not present"); return FAIL; } strcpy(telno, paramv[curscr->intprm]); DEBUG(DB_LGII, "telno set to %s\n", telno); break; case SC_SNDP: if (curscr->intprm > paramc - 1) { sprintf(tempstr, "sendstr - param #%d", curscr->intprm); logit(tempstr, " not present"); return FAIL; } prmlen = xlat_str(tempstr, paramv[curscr->intprm]); ttoslow(tempstr, prmlen, sc_chrdly); break; case SC_IF1P: if (curscr->intprm < paramc) goto _chgstate; break; case SC_IF0P: if (curscr->intprm >= paramc) goto _chgstate; break; case SC_DIAL: if(telno[0] == '\0') { logit("telno not set", ""); return(FAIL); } /* * Compute and set a default timeout for the 'timeout' * command. Some parameters in this computation may be * changed by the script. See the man page xchat(8) for * details. */ sc_dtmo = (sc_dtime+sc_chrdly)*strlen(telno) + sc_carrtime; c=strcpy(tempstr, telno); for (; *c!='\0'; c++) { if (*c == 'W') { *c = sc_wchar; sc_dtmo += sc_wtime; } else if (*c == 'P') { *c = sc_pchar; sc_dtmo += sc_ptime; } } DEBUG(DB_LGI, "Dialing, default timeout is %d millisecs\n", sc_dtmo); ttoslow(tempstr, 0, sc_chrdly); break; case SC_TIMO: /* these are "expects", don't bother */ case SC_XPCT: /* with them yet, other than noting that */ case SC_CARR: /* they exist */ case SC_XPST: expcnt++; break; } } /* we've done the current state's actions, now do its expects, if any */ if (expcnt == 0) { if (curscr != (struct script *)NULL && (curscr->opcode == SC_LABEL)) { curstate = curscr; DEBUG(DB_LGI, "Fell through to state %s\n", curstate->strprm); goto _newstate; } else { logit("No way out of state", curstate->strprm); return FAIL; } } time(&sc_begwait); /* log time at beg of expect */ DEBUG(DB_LGI, "Doing expects for state %s\n", curstate->strprm); charlog((char *)NULL, 0, DB_LGI, "Received"); while (1) { chr = xgetc(1); /* Returns upon char input or 1 sec. tmo */ charlog(&chr, 1, DB_LGI, (char *)NULL); if (chr != EOF) { if (expin < MAX_EXPCT) { expbuf[expin++] = chr & 0x7f; } else { strncpy(expbuf, &expbuf[1], MAX_EXPCT-1); expbuf[MAX_EXPCT-1] = chr & 0x7f; } } /* for each entry in the current state... */ for (curscr = curstate->next; (curscr != (struct script *)NULL) && (curscr->opcode != SC_LABEL); curscr = curscr->next) { switch (curscr->opcode) { case SC_TIMO: sc_secs = curscr->intprm; if (sc_secs == 0) sc_secs = sc_dtmo; sc_secs /= 1000; if (time(NULL)-sc_begwait > sc_secs) { DEBUG(DB_LGI, "\nTimed out (%d secs)\n", sc_secs); goto _chgstate; } break; case SC_CARR: if (ttcd()) { DEBUG(DB_LGI, "\nGot carrier\n", 0); goto _chgstate; } break; case SC_HUPS: if (fShangup) { DEBUG(DB_LGI, "\nGot data set hangup\n", 0); goto _chgstate; } break; case SC_XPCT: if ((expin >= curscr->intprm) && (strncmp(curscr->strprm, &expbuf[expin - curscr->intprm], curscr->intprm) == SAME)) { charlog(curscr->strprm, curscr->intprm, DB_LGI, "Matched"); goto _chgstate; } break; /* New opcode added by hag@eddie.mit.edu for expecting a parameter supplied string */ case SC_XPST: if(curscr->intprm >paramc-1) { sprintf(tempstr,"expectstr - param#%d",curscr->intprm); logit(tempstr, " not present"); return(FAIL); } prmlen=xlat_str(tempstr,paramv[curscr->intprm]); if((expin >= prmlen) && (strncmp(tempstr,&expbuf[expin-prmlen], prmlen) == SAME)) { charlog(tempstr,prmlen,DB_LGI, "Matched"); goto _chgstate; } break; } } } } /* * SIGNAL HANDLERS */ /* * usignal - generic signal catcher */ static int usignal(isig) int isig; { DEBUG(DB_LOG, "Caught signal %d. Exiting...\n", isig); restore_tty(); exit(FAIL); } /* * uhup - HUP catcher */ static int uhup(isig) int isig; { DEBUG(DB_LOG, "Data set hangup.\n"); fShangup = TRUE; } /* * TERMINAL I/O ROUTINES */ /* * xgetc - get a character with timeout * * Assumes that stdin is opened on a terminal or TCP socket * with O_NONBLOCK. */ static char xgetc(tmo) int tmo; /* Timeout, seconds */ { char c; struct timeval s; int f = 1; /* Select on stdin */ int result; if(read(0, &c, 1) <= 0) /* If no data available */ { s.tv_sec = (long)tmo; s.tv_usec = 0L; if(select (1, &f, (int *) NULL, &f, &s) == 1) read(0, &c, 1); else c = '\377'; } return(c); } /* * Pause for an interval in milliseconds */ void msleep(msec) long msec; { #if HAVE_USLEEP if(msec == 0) /* Skip all of this if delay = 0 */ return; usleep (msec * (long)1000); #endif /* HAVE_USLEEP */ #if HAVE_NAPMS if(msec == 0) /* Skip all of this if delay = 0 */ return; napms (msec); #endif /* HAVE_NAPMS */ #if HAVE_NAP if(msec == 0) /* Skip all of this if delay = 0 */ return; nap (msec); #endif /* HAVE_NAP */ #if HAVE_POLL struct pollfd sdummy; if(msec == 0) return; /* * We need to pass an unused pollfd structure because poll checks * the address before checking the number of elements. */ poll (&sdummy, 0, msec); #endif /* HAVE_POLL */ #if USE_SELECT_TIMER struct timeval s; if(msec == 0) return; s.tv_sec = msec / 1000L; s.tv_usec = (msec % 1000L) * 1000L; select (0, (int *) NULL, (int *) NULL, (int *) NULL, &s); #endif /* USE_SELECT_TIMER */ #if ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP && \ ! HAVE_POLL && ! USE_SELECT_TIMER if(msec == 0) return; sleep (1); /* Sleep for a whole second (UGH!) */ #endif /* HAVE_ and USE_ nothing */ } /* * Debugging output */ static void DEBUG(level, msg1, msg2) int level; char *msg1, *msg2; { if ((dbf != NULL) && DEBUG_LEVEL(level)) fprintf(dbf, msg1, msg2); } /* * charlog - log a string of characters * * SPECIAL CASE: msg=NULL, len=1 and msg[0]='\377' gets logged * when read does its 1 sec. timeout. Log "<1 sec.>" * so user can see elapsed time */ static void charlog(buf, len, mask, msg) char *buf; int len, mask; char *msg; { char tbuf[256]; if (DEBUG_LEVEL(mask) && dbf != NULL) { if(msg == (char *)NULL) msg = ""; strncpy(tbuf, buf, len); tbuf[len] = '\0'; if(len == 1 && tbuf[0] == '\377') strcpy(tbuf, "<1 sec.>"); fprintf(dbf, "%s %s\n", msg, tbuf); } } /* * setup_tty() * * Save current tty settings, then set up raw, single * character input processing, with 7-bit stripping. */ static void setup_tty() { register int i; ioctl(0, TCGETA, &old); new = old; for(i = 0; i < 7; i++) new.c_cc[i] = '\0'; new.c_cc[VMIN] = 0; /* MIN = 0, use requested count */ new.c_cc[VTIME] = 10; /* TIME = 1 sec. */ new.c_iflag = ISTRIP; /* Raw mode, 7-bit stripping */ new.c_lflag = 0; /* No special line discipline */ ioctl(0, TCSETA, &new); } /* * restore_tty() - restore signal handlers and tty modes on exit. */ static void restore_tty(sig) int sig; { ioctl(0, TCSETA, &old); return; } /* * ttoslow() - Send characters with pacing delays */ static void ttoslow(s, len, delay) char *s; int len; time_t delay; { int i; if (len == 0) len = strlen(s); charlog (s, len, DB_LGI, "Sending slowly"); for (i = 0; i < len; i++, s++) { write(1, s, 1); msleep(delay); } } /* * ttflui - flush input buffer */ static void ttflui() { if(isatty(0)) (void) ioctl ( 0, TCFLSH, 0); } /* * ttcd - Test if carrier is present * * NOT IMPLEMENTED. I don't know how!!! */ static int ttcd() { return TRUE; } /* * tthang - Force DTR low for 1-2 sec. */ static void tthang() { if(!isatty()) return; #ifdef TCCLRDTR (void) ioctl (1, TCCLRDTR, 0); sleep (2); (void) ioctl (1, TCSETDTR, 0); #endif return; } /* * ttbreak - Send a "break" on the line */ static void ttbreak() { (void) ioctl (1, TCSBRK, 0); } /* * ttblind - return TRUE if tty is "blind" * * NOT IMPLEMENTED - Don't know how!!! */ static int ttblind() { return FALSE; } /* * tt7bit - enable/disable 7-bit stripping on line */ static void tt7bit(enable) int enable; { if(enable) new.c_iflag |= ISTRIP; else new.c_iflag &= ~ISTRIP; ioctl(0, TCSETA, &new); } /* * ttpar - Set parity mode on line. Ignore parity errors on input. */ static void ttpar(mode) int mode; { switch(mode) { case NONE: new.c_iflag &= ~(INPCK | IGNPAR); new.c_cflag &= ~(CSIZE | PARENB | PARODD); new.c_cflag |= CS8; break; case EVEN: new.c_iflag |= (INPCK | IGNPAR); new.c_cflag &= ~(CSIZE | PARODD); new.c_cflag |= (CS7 | PARENB); break; case ODD: new.c_iflag |= (INPCK | IGNPAR); new.c_cflag &= ~(CSIZE); new.c_cflag |= (CS7 | PARENB | PARODD); break; } ioctl(0, TCSETA, &new); } uucp-1.07/contrib/xchat.man0000664000076400007640000004360707665321760011402 .TH xchat 8 .SH NAME xchat - Extended chat processor .SH SYNOPSIS .BI "xchat " "scriptfile" .RI " [ " parameter... " ] " .PP where .I scriptfile is the name of a file containing an .I xchat script. If .I scriptfile begins with ``/'', then it is assumed to be a full path name for the script file. If not, a configuration-dependent default directory path (usually .B "/usr/local/conf/uucp/" ) is prepended to the script file name. Normally, the default path is the same as that for the Taylor UUCP configuration files. .SH DESCRIPTION .I Xchat is a general-purpose dialing and login program designed for use with Taylor UUCP as a ``chat-program'', taking the place (or augmenting) the built-in chat scripting facility. It provides the ability to closely control timeouts, multiple simultaneous ``expect'' strings with separate actions, extended terminal control, modem command character pacing, and more. .PP When used in conjunction with Taylor UUCP's configuration features, .I xchat can provide you the ability to manage the most intricate login, dial and hangup needs. The scripts are written in a shell-like (well, sort-of) style with labels, commands, and parameters, easing the task of writing procedures for complex terminal communications situations. .PP Because .I xchat assumes that it is connected to the terminal device via stdin/stdout, you can easily debug scripts by invoking it from the shell and responding to the script from the keyboard. A debug logging facility is included, with the debug output going to a separate user-specified file. This makes it easy to debug connection problems without wading through large .I uucico log and debug files. .PP Formally, a script describes a state machine; .I xchat interprets the script and does what the state machine tells it to. This section will be much easier to understand if you obtain listings of the script files supplied with .I xchat. .SH "SCRIPT FILE FORMAT" Script files are ordinary text files containing comments, labels, and statements. Blank lines are ignored. Comments are denoted by leading ``#'' characters. Some statements (those which do not end with an ``extended string'' argument; see below) can also have trailing comments. .PP .I Labels begin in column one and are ended by colons (:). A label specifies a state name. All lines between a pair of labels are the statements for a single state. .PP Processing always begins at the head of the script (no leading state name is necessary). .PP .I Statements are divided into two categories, ``action'' and ``expect''. When a state is entered, all of its actions are performed in the order in which they appear in the file. .PP A .I transition to another state may occur for any of three reasons: .IP (1) 5 One of the actions may cause a transition to another state, in which case the rest of the current state's actions are skipped. Processing resumes with the first action statement of the new state. .IP (2) 5 If none of the actions cause a state transition, and there are no expects in the state, processing ``falls through'' to the next state in the file. .IP (3) 5 If none of the actions cause a state transition, but there are expects in the state, the state machine pauses until one of the expects is ``satisfied''. It then transitions to the state named in the expect statement. .PP Finally, there are two action statements which, when executed, cause the script to exit. .SH "SCRIPT FILE STATEMENTS" This section describes all of the statements that may appear in script files, except for a few special action statements. Those are described in a later section, ``Overriding Defaults''. .PP Some statements accept one or two arguments, referred to in the following descriptions as .IR int ", " ns ", " str ", or " .IR xstr ", to" indicate whether the argument is an integer, a new state name, a string, or an ``extended string'' (described in a later section). .PP For all statements that accept two arguments, the first is the name of a new state, and the second specifies a condition or reason for changing to the new state. .SS "Termination And Informational Statements" These statements are used to place entries into the Taylor UUCP .I Log file, and to cause .I xchat to exit with successful or failure status. It is also possible to open a separate .I debug log file and control the level of tracing and error reporting that will go into that log file. This is very useful in debugging .I xchat scripts. .br .ta 1.0i 1.5i 2.0i .TP 2.0i .B failed Exit script with ``failed'' status. This causes .I xchat to exit with status 0. .TP 2.0i .B success Exit script with ``success'' status. This causes .I xchat to exit with status 1. .TP 2.0i .BI "log " xstr Send informational message .I xstr to standard error. When used with Taylor UUCP, this is the .I Log file for the .I uucico program. .TP 2.0i .BI "logerr " xstr Send message .I xstr to standard error, with ``ERROR:'' indicator. When used with Taylor UUCP, this is the .I Log file for the .I uucico program. .TP 2.0i .BI "dbgfile " xstr Open script debugging file .I xstr. If .I xstr begins with ``/'', it is assumed to be an absolute path name for the debugging file. If not, then a configuration-dependent default directory path (usually .B "/usr/spool/uucp" ) is prepended to .I xstr. Normally the default path is that of the directory where Taylor UUCP puts its log files. The debugging file is used to capture a detailed log of the data sent and received, errors encountered, and a trace of script execution. The various types of logging are controlled by the .I "debug mask," described next. .B Note: A new log file is created each time .I xchat runs. Use the .B log and .B loge commands to log continuous information onto standard out, which is connected to the Taylor UUCP .I Log file when .I xchat is run by the Taylor .I uucico. .TP 2.0i .BI "dbgset " int Set the bits specified in .I int in the debugging mask. The value in .I int is ``or''ed into the mask. Set bit 0 (value \= 1) for error messages, bit 1 (value \= 2) for dial, login and init errors, bit 2 (value \= 4) for dial, login and init trace with character I/O, and bit 3 (value \= 8) for script processing internals. Normally, you will just turn it all on with a value of 15. .TP 2.0i .BI "dbgclr " int Clear the bits specified in .I int from the debugging mask. .TP 2.0i .BI "debug " xstr Write .I xstr into the debug log. The entry will be enclosed in angle brackets. .TP 2.0i .BI "debuge " xstr Write .I xstr into the debug log with ``ERROR: '' prepended. The entry will be enclosed in angle brackets. .SS "Sending Data" These statements are used to transmit data to standard out (the tty or TCP port when used with Taylor UUCP). .I No implied carriage returns are sent. You must include a \\r if you want a carriage return in the string sent by the .B send command. If you want a return sent after .B dial or .B sendstr, you must send it with a separate .B send command. .TP 2.0i .B dial Send the string previously set by the .B telno command to the serial port. .B W and .B P characters in the phone number are converted as described under .B Dial Strings, below. This statement also sets a default timeout value, as described under the .B timeout statement. .TP 2.0i .BI "send " xstr Send the string .I xstr to the serial port. .TP 2.0i .BI "sendstr " int The argument of this statement is a digit from 0 through 7. Send the corresponding string parameter as passed to .I xchat following the script file name. The parameter is interpreted as an extended string. .SS "Special Terminal Control Statements" These statements are used to cause the terminal port to perform some special action, or to change the mode of the port. .I The modes of the port are restored to their original settings .I by xchat before it exits. .TP 2.0i .B flush Flush the terminal port's input buffer. .TP 2.0i .B break Send a break signal. .TP 2.0i .B hangup Momentarily drop Data Terminal Ready (DTR) on the serial port, causing the modem to hang up. (Not usually needed, since .I uucico does this at the end of each call.) .TP 2.0i .B 7bit Change the port to strip incoming characters to 7 bits. .I This is the default mode. This mode is implied when the port has parity enabled, since parity characters are 7-bits wide. .TP 2.0i .B 8bit Change the port to allow incoming 8-bit characters to be passed to the script processor. This mode has no effect if parity is enabled, since parity characters are 7-bits wide. .TP 2.0i .B nopar Change the port to 8-bits, no parity. .I This is the default mode. .TP 2.0i .B evenpar Change the port to 7-bits, even parity. .I Incoming characters with parity errors are discarded. .TP 2.0i .B oddpar Change the port to 7-bits, odd parity. .I Incoming characters with parity errors are discarded. .SS "Counting, Branching, Timing and Testing Statements" These statements are used to control the flow of the .I xchat script itself, including branching, delays, and counter manipulation. .TP 2.0i .BI "sleep " int Delay for .I int milliseconds. .TP 2.0i .B zero Clear the counter. .TP 2.0i .B count Add one to the counter. .TP 2.0i .BI "ifgtr " "ns int" Go to state .I ns if counter greater than .I int. .TP 2.0i .BI "goto " ns Go to state .I ns unconditionally. .TP 2.0i .BI "ifstr " "ns int" Go to state .I ns if string parameter .I int is nonempty. .TP 2.0i .BI "ifnstr " "ns int" Go to state .I ns if string parameter .I int is empty. .TP 2.0i .BI "ifblind " ns Change to state .I ns if the port is ``blind'' without carrier (CD) asserted. .I This is not yet implemented, the test always fails. .TP 2.0i .BI "ifblgtr " "ns int" Change to state .I ns if the port is ``blind'' without carrier (CD) asserted, and counter is greater then .I int. .I This is not yet implemented, the test always fails. .SS "Expect Statements" Expect statements are usually the last statements that appear in a given state, though in fact they can appear anywhere within the state. Even if they appear at the beginning, the script processor always does all of the action statements first. As a practical matter, the order of these statements is not significant; they are all interpreted ``in parallel''. .TP 2.0i .BI "expect " "ns xstr" Change to state .I ns if the string specified by .I xstr is received from standard input (usually the serial port). Case is significant, but high-order bits are not checked. .TP 2.0i .BI "expectstr " "ns int" Change to state .I ns if the string specified in parameter .I int is received from standard input (usually the serial port). .I int must be in the range 0 to 7. Case is significant, but high-order bits are not checked. Useful where a prompt can change in different dial-in lines. .TP 2.0i .BI "ifcarr " ns Change to state .I ns if Carrier Detect (CD) is true. .I Not currently implemented. Always changes state. .TP 2.0i .BI "ifhang " ns Change to state .I ns if a data set hangup occurs (SIGHUP signal received). .TP 2.0i .BI "timeout " "ns int" Change to state .I ns if the time (in milliseconds) given by .I int has elapsed without satisfying any expects. If the time specified is 0, a default timeout value (calculated from the length and other characteristics of the most recent dial string) is used. .SH "SCRIPT PROCESSING DETAILS" .SS "Extended Strings" In the statements that accept string arguments, the strings are interpreted as .I extended strings. Extended strings begin with the first nonblank character and continue, including all imbedded and trailing blanks and other whitespace, until (but not including) the end of the line in the script file. (There is no provision for line continuation.) No trailing spaces should be present between the last ``desired'' character of the string and the end of the line, as they will be included in the stored string and sent or expected, just as they appear in the script file. And, obviously, no trailing comments are permitted! They will just be stored as part of the string. .PP Within an extended string, the following ``escape sequences'' will be converted as indicated before being sent or expected: .br .nf .in +0.5i \fB\\d\fR EOT character (control-D) \fB\\N\fR null character \fB\\n\fR line feed \fB\\r\fR carriage return \fB\\s\fR space \fB\\t\fR tab \fB\\\-\fR hyphen \fB\\\\\fR backslash \fB\\ooo\fR character with value ooo (in octal) .in -0.5i .fi .PP Since extended strings in scripts can include embedded spaces, tabs, etc., these escape sequences are only required in strings appearing in systems entries, though they may be used in script files to improve readability. .PP The octal-character specification (\\ooo) may have from one to three octal digits; it is terminated either after the third digit or when a non-octal character is encountered. But if you want to specify one of these followed by something that happens to be a valid octal character (for example, a control-A followed by a 7) make sure to include all three digits after the \\ . So \\0017 would become a control-A followed by the Ascii character ``7'', but \\17 or \\017 would become a control-Y (decimal value 25). \\1S would convert to a control-A followed by an ``S''. .PP Extended strings are stored without a trailing carriage return unless one is explicitly present in the string (via \\r). .SS "String Parameters" The .B sendstr statement sends (after conversion from extended string format) one of the parameters given on the .I xchat command line following the script file name. The parameter is selected by the integer argument of the statement. .PP This allows ``generic'' script files to serve for many different systems; the string parameters provide the phone number, username, password, etc. Character substitutions described under ``extended strings'' above are performed on these strings. .PP The ifstr and ifnstr statements allow further generality in script files, by testing whether a particular parameter is present in the systems entry. For example, a single script can be used both for those systems that require a password and those that do not. The password is specified as the last argument in the .xchat command; the script can test for this parameter's existence and skip the password sequence if the parameter is empty. .SS "``Wait'' And ``Pause'' Characters In Dial Strings" An additional conversion is performed on dial strings. Dial strings are interpreted as extended strings. Then the characters .B W and .B P within a dial string are interpreted as ``wait for dial tone'' and ``pause'', and may be converted to other characters. By default, .B W is left alone, and .B P is converted to a comma (,); these are appropriate for Hayes-type modems. The script may specify other substitutions (see below). .PP .B NOTE: The Taylor UUCP documentation states that the ``wait'' and ``pause'' characters are ``='' and ``-'', respectively. These are actual characters understood by some modems. When using .I xchat you should put .B W and .B P in the dial strings you specify in the Taylor configuration files. This way, the .I xchat processor can make the substitution appropriate for the particular modem in use. Make a separate .I xchat script for each modem type, e.g., .I "dial.hayes" and specify the translation there. This way, the phone number strings in the Taylor configuration files can be used with a variety of modems. .SS "Default Timeouts For Dial Strings" When a .B dial statement is executed, a default timeout value is set. This is the timeout value used by a subsequent timeout statement if the statement specifies a timeout value of 0. .PP The default timeout is given by: .br .nf .in +2 \fIctime\fR + (\fIndigits\fR * \fIdgttime\fR) + (\fInwchar\fR * \fIwtime\fR) + (\fInpchar\fR * \fI ptime\fR) .in -2 .fi .PP where .I ndigits, nwchar, and .I npchar are the number of digits, wait characters, and pause characters in the dial string, and .I ctime, dgttime, wtime, and .I ptime are 45 seconds, 0.1 seconds, 10 seconds, and 2 seconds, respectively. All of these times may be changed as specified below under ``Overriding Defaults.'' .SS "Trailing Carriage Returns Not Assumed" In the .B dial and .B sendstr statements, the dial string or parameter is sent with no trailing carriage return; if a carriage return must be sent after one of these, a separate send statement must provide it. .SH "OVERRIDING DEFAULTS" The script processor sets several default values. The following statements, which override these defaults, may be useful in certain circumstances. .TP 2.0i .BI "chrdly " int Since many modems cannot accept dialing commands at full ``computer speed'', the script processor sends all strings with a brief inter-character delay. This statement specifies the delay time, in milliseconds. The default is 100 (0.1 second). .TP 2.0i .BI "pchar " str Specifies the character to which .BR P s in the dial string should be converted. Default is ``,'', for use with Hayes-type modems. .TP 2.0i .BI "ptime " int Specifies the time, in milliseconds, to allow in the default timeout for each pause character in the dial string. Default is 2000 (2 seconds). .TP 2.0i .BI "wchar " str Specifies the character to which .BR W s in the dial string should be converted. Default is ``W'', for Hayes modems. .TP 2.0i .BI "wtime " int Specifies the time, in milliseconds, to allow in the default timeout for each wait-for-dialtone character in the dial string. Default is 10000 (10 seconds). .TP 2.0i .BI "dgttime " int Specifies the time, in milliseconds, to allow in the default timeout for each digit character in the dial string. Default is 100 (0.1 second). .TP 2.0i .BI "ctime " int Specifies the time, in milliseconds, to allow in the default timeout for carrier to appear after the dial string is sent. Default is 45000 (45 seconds). .SH "SEE ALSO" uucico(8) for Taylor UUCP, and documentation for Taylor UUCP. .SH AUTHOR Robert B. Denny (denny@alisa.com) .SH CONTRIBUTORS Daniel Hagerty (hag@eddie.mit.edu) .SH HISTORY This program is an adaptation of the dial/login script processing code that is a part of DECUS UUCP for VAX/VMS, written by Jamie Hanrahan, et. al. .SH BUGS This version (1.1) does not support BSD terminal facilities. Anyone volunteer to add this? uucp-1.07/sample/0000777000076400007640000000000007665533774007501 5uucp-1.07/sample/dialcode0000664000076400007640000000150607665321760011076 # This is an example of dialcode, the dialcode configuration file for # Taylor UUCP. To use it, you must compile the package with # HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy # this file to newconfigdir as set in Makefile.in (the default is # /usr/local/conf/uucp), and edit it as appropriate for your system. # Everything after a '#' character is a comment. To uncomment any of # the sample lines below, just delete the '#'. # The dialcode file is used if \T is used in the dialer chat script # and the telephone number begins with alphabetic characters. The # alphabetic characters are looked up and translated in dialcode. # Here are a couple of sample dialcodes. MA 617 CA 415 # For example, if the phone number (from the sys file) is MA7389449, # then the string sent to the modem will be 6177389449. uucp-1.07/sample/call0000664000076400007640000000163707665321760010252 # This is an example of call, the call out password file for Taylor # UUCP. To use it, you must compile the package with # HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy # this file to newconfigdir as set in Makefile.in (the default is # /usr/local/conf/uucp), and edit it as appropriate for your system. # Everything after a '#' character is a comment. To uncomment any of # the sample lines below, just delete the '#'. # This file is used when the ``call-login'' or ``call-password'' # commands are used in the sys file with a "*" argument (e.g., # ``call-login *''). The system name is looked up in this file, and # the login name and password are used. # The point of this is that the sys file may then be publically # readable, while still concealing the login names and passwords used # to connect to the remote system. # The format is just system-name login-name password. uunet Uairs foobar uucp-1.07/sample/config0000664000076400007640000001022407665321760010574 # This is an example of config, the main configuration file for Taylor # UUCP. To use it, you must compile the package with # HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy # this file to newconfigdir as set in Makefile.in (the default is # /usr/local/conf/uucp), and edit it as appropriate for your system. # You need not use this file at all; all the important commands have # defaults which will be used if this file can not be found. # Everything after a '#' character is a comment. To uncomment any of # the sample lines below, just delete the '#'. # You must choose a UUCP name. If your system is going to be # communicating with other systems outside your organization, the name # must be unique in the entire world. The usual method is to pick a # name, and then search the UUCP maps (in the newsgroup # comp.mail.maps) to see whether it has already been taken. See the # README posting in comp.mail.maps for more information. If the name # of your system as returned by "uuname -l" or "hostname" is the name # you want to use, you do not need to set the name in this file. # Otherwise uncomment and edit the following line. # nodename uucp # The UUCP name of this system # The default spool directory is set in policy.h (the default is # /usr/spool/uucp). All UUCP jobs and status information are kept in # the spool directory. If you wish to change it, use the spool # command. # spool /usr/spool/uucp # The UUCP spool directory # The default public directory is set in policy.h (the default is # /usr/spool/uucppublic). Remote systems may refer to a file in this # directory using "~/FILE". By default, the public directory is the # only directory which remote systems may transfer files in and out # of. If you wish to change the public directory, use the pubdir # command. # pubdir /usr/spool/uucppublic # The UUCP public directory # The names of the UUCP log files are set in policy.h. The default # names depend on the logging option you have chosen. If # HAVE_TAYLOR_LOGGING is set in policy.h, the default log file name is # /usr/spool/uucp/Log, the default statistics file name is # /usr/spool/uucp/Stats, and the default debugging file name is # /usr/spool/uucp/Debug. These file names may be set by the following # commands. # logfile /usr/spool/uucp/Log # The UUCP log file # statfile /usr/spool/uucp/Stats # The UUCP statistics file # debugfile /usr/spool/uucp/Debug # The UUCP debugging file # uuxqt is the program which executes UUCP requests from other # systems. Normally one is started after each run of uucico, the # communications daemon. You may control the maximum number of uuxqt # programs run at the same time with the following command. The # default is to have no maximum. # max-uuxqts 1 # The maximum number of uuxqts # There are several files that uucico uses. By default it looks for # them in newconfigdir, as set in Makefile.in. You may name one or # more of each type of file using the following commands. # sysfile FILES # Default "sys" # portfile FILES # Default "port" # dialfile FILES # Default "dial" # dialcodefile FILES # Default "dialcode" # callfile FILES # Default "call" # passwdfile FILES # Default "passwd" # The ``timetable'' command may be used to declare timetables. These # may then be referred to in time strings in the other files. # timetable Day Wk0905-1655 # The ``unknown'' command is followed by any command which may appear # in a sys file. These commands are taken together to describe what # is permitted to a system which is not listed in any sys file. If # the ``unknown'' command, then unknown systems are not permitted to # connect. # Here is an example which permits unknown systems to download files # from /usr/spool/anonymous, and to upload them to # /usr/spool/anonymous/upload. # # No commands may be executed (the list of permitted commands is empty) # unknown commands # The public directory is /usr/spool/anonymous # unknown pubdir /usr/spool/anonymous # Only files in the public directory may be sent; users may not download # files from the upload directory # unknown remote-send ~ !~/upload # May only upload files into /usr/spool/anonymous/upload # unknown remote-receive ~/upload uucp-1.07/sample/dial0000664000076400007640000000252007665321760010240 # This is an example of dial, the dialer configuration file for Taylor # UUCP. To use it, you must compile the package with # HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy # this file to newconfigdir as set in Makefile.in (the default is # /usr/local/conf/uucp), and edit it as appropriate for your system. # Everything after a '#' character is a comment. To uncomment any of # the sample lines below, just delete the '#'. # All dialers named in the port (or sys) file must be described in the # dial file. It is also possible to describe a dialer directly in the # port (or sys) file. # This is a typical Hayes modem definition. dialer hayes # The chat script used to dial the phone. # This means: # 1) expect nothing (i.e., continue with step 2) # 2) send "ATZ", then a carriage return, then sleep for 1 to 2 # seconds. The \c means to not send a final carriage return. # 3) wait until the modem echoes "OK" # 4) send "ATDT", then the telephone number (after translating any # dialcodes). # 5) wait until the modem echoes "CONNECT" chat "" ATZ\r\d\c OK ATDT\T CONNECT # If we get "BUSY" or "NO CARRIER" during the dial chat script we # abort the dial immediately. chat-fail BUSY chat-fail NO\sCARRIER # When the call is over, we make sure we hangup the modem. complete \d\d+++\d\dATH\r\c abort \d\d+++\d\dATH\r\c uucp-1.07/sample/passwd0000664000076400007640000000162607665321760010636 # This is an example of passwd, the call in password file for Taylor # UUCP. To use it, you must compile the package with # HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy # this file to newconfigdir as set in Makefile.in (the default is # /usr/local/conf/uucp), and edit it as appropriate for your system. # Everything after a '#' character is a comment. To uncomment any of # the sample lines below, just delete the '#'. # This file is used when uucico is invoked with the -l or -e argument. # uucico will then prompt for a login name and password. The login # name is looked up in this file to check the password (the system # password file, /etc/passwd, is not checked). This permits uucico to # completely take over a port, allowing UUCP access to remote systems # but not permitting remote users to actually log in to the system. # The format is just login-name password. Uairs foobar uucp-1.07/sample/port0000664000076400007640000000276207665321760010323 # This is an example of port, the port configuration file for Taylor # UUCP. To use it, you must compile the package with # HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), copy # this file to newconfigdir as set in Makefile.in (the default is # /usr/local/conf/uucp), and edit it as appropriate for your system. # Everything after a '#' character is a comment. To uncomment any of # the sample lines below, just delete the '#'. # All ports named in the sys file must be described in a port file. # It is also possible to describe the port directly in the sys file. # Commands that appears before the first ``port'' command are defaults # for all ports that appear later in the file. In this case all ports # will default to being modems (other possible types are direct, tcp # and tli). type modem # Now we describe two ports. # This is the name of the port. This name may be used in the sys file # to select the port, or the sys file may just specify a baud rate in # which case the first matching unlocked port will be used. port port1 # This is the device name to open to dial out. device /dev/ttyd0 # This is the dialer to use, as described in the dialer file. dialer hayes # This is the baud rate to dial out at. speed 2400 # Here is a second port. This is like the first, except that it uses # a different device. It also permits a range of speeds, which is # mainly useful if the system specifies a particular baud rate. port port2 device /dev/ttyd1 dialer hayes speed-range 2400 9600 uucp-1.07/sample/sys10000664000076400007640000000275507665321760010240 # This is an example of a sys file, the file(s) which describe remote # systems for Taylor UUCP. To use it, you must compile the package # with HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), # copy this file to newconfigdir as set in Makefile.in (the default is # /usr/local/conf/uucp), and edit it as appropriate for your system. # If you do not use the ``unknown'' command in the config file, then # each system that you communicate with must be listed in a sys file. # Everything after a '#' character is a comment. To uncomment any of # the sample lines below, just delete the '#'. # This is a sample sys file that might be used in a leaf system. A # leaf system is one that only contacts one other system. sys2 # provides another example. # The name of the remote system that we call. system uunet # The login name and password are kept in the callout password file # (by default this is the file "call" in newconfigdir). call-login * call-password * # We can send anything at any time. time any # During the day we only accept grade 'Z' or above; at other times # (not mentioned here) we accept all grades. uunet queues up news # at grade 'd', which is lower than 'Z'. call-timegrade Z Wk0755-2305,Su1655-2305 # The phone number to call. phone 7389449 # uunet tends to be slow, so we increase the timeout chat-timeout 120 # The port we use to dial out. port serial # Increase the timeout and the number of retries. protocol-parameter g timeout 20 protocol-parameter g retries 10 uucp-1.07/sample/sys20000664000076400007640000000277707665321760010245 # This is an example of a sys file, the file(s) which describe remote # systems for Taylor UUCP. To use it, you must compile the package # with HAVE_TAYLOR_CONFIG set to 1 in policy.h (that is the default), # copy this file to newconfigdir as set in Makefile.in (the default is # /usr/local/conf/uucp), and edit it as appropriate for your system. # If you do not use the ``unknown'' command in the config file, then # each system that you communicate with must be listed in a sys file. # Everything after a '#' character is a comment. To uncomment any of # the sample lines below, just delete the '#'. # This is a sample sys file that might be used by a system that # contacts a couple of other systems, both of which are treated the # same. sys1 provides another example. # Commands that appear before the first ``system'' commands are # defaults for all systems listed in the file. # Get the login name and password to use from the call-out file. By # default this is the file "call" in newconfigdir. call-login * call-password * # The systems must use a particular login called-login Ulocal # Permit local users to send any world readable file local-send / # Permit local uses to request into any world writable directory local-receive / # Call at any time time any # Use port1, then port2 port port1 alternate port port2 # Now define the systems themselves. Because of all the defaults we # used, there is very little to specify for the systems themselves. system comton phone 5551212 system bugs phone 5552424 uucp-1.07/lib/0000777000076400007640000000000007665533771006763 5uucp-1.07/lib/Makefile.am0000664000076400007640000000047607665321760010735 # This is the auto-Makefile for the lib subdirectory of Taylor UUCP. noinst_LIBRARIES = libuucp.a libuucp_a_SOURCES = buffer.c crc.c debug.c escape.c getopt.c getop1.c \ parse.c quote.c quotes.c spool.c status.c xfree.c xmall.c xreall.c libuucp_a_LIBADD = $(LIBOBJS) AM_CFLAGS = -I.. -I$(srcdir)/.. $(WARN_CFLAGS) uucp-1.07/lib/Makefile.in0000664000076400007640000002541607665532204010744 # Makefile.in generated automatically by automake 1.5 from Makefile.am. # Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 # 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@ # This is the auto-Makefile for the lib subdirectory of Taylor UUCP. SHELL = @SHELL@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ sharedstatedir = @sharedstatedir@ localstatedir = @localstatedir@ libdir = @libdir@ infodir = @infodir@ mandir = @mandir@ includedir = @includedir@ oldincludedir = /usr/include pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = .. ACLOCAL = @ACLOCAL@ AUTOCONF = @AUTOCONF@ AUTOMAKE = @AUTOMAKE@ AUTOHEADER = @AUTOHEADER@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_HEADER = $(INSTALL_DATA) transform = @program_transform_name@ NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : AMTAR = @AMTAR@ AR = @AR@ AWK = @AWK@ CC = @CC@ DEPDIR = @DEPDIR@ EXEEXT = @EXEEXT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LIBOBJS = @LIBOBJS@ LN_S = @LN_S@ MAINT = @MAINT@ NEWCONFIGDIR = @NEWCONFIGDIR@ OBJEXT = @OBJEXT@ OLDCONFIGDIR = @OLDCONFIGDIR@ OWNER = @OWNER@ PACKAGE = @PACKAGE@ POUNDBANG = @POUNDBANG@ RANLIB = @RANLIB@ UNIXOBJS = @UNIXOBJS@ VERSION = @VERSION@ WARN_CFLAGS = @WARN_CFLAGS@ am__include = @am__include@ am__quote = @am__quote@ install_sh = @install_sh@ noinst_LIBRARIES = libuucp.a libuucp_a_SOURCES = buffer.c crc.c debug.c escape.c getopt.c getop1.c \ parse.c quote.c quotes.c spool.c status.c xfree.c xmall.c xreall.c libuucp_a_LIBADD = $(LIBOBJS) AM_CFLAGS = -I.. -I$(srcdir)/.. $(WARN_CFLAGS) subdir = lib mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = LIBRARIES = $(noinst_LIBRARIES) libuucp_a_AR = $(AR) cru libuucp_a_DEPENDENCIES = @LIBOBJS@ am_libuucp_a_OBJECTS = buffer.$(OBJEXT) crc.$(OBJEXT) debug.$(OBJEXT) \ escape.$(OBJEXT) getopt.$(OBJEXT) getop1.$(OBJEXT) \ parse.$(OBJEXT) quote.$(OBJEXT) quotes.$(OBJEXT) \ spool.$(OBJEXT) status.$(OBJEXT) xfree.$(OBJEXT) \ xmall.$(OBJEXT) xreall.$(OBJEXT) libuucp_a_OBJECTS = $(am_libuucp_a_OBJECTS) DEFS = @DEFS@ DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ depcomp = $(SHELL) $(top_srcdir)/depcomp @AMDEP_TRUE@DEP_FILES = $(DEPDIR)/bsrch.Po $(DEPDIR)/buffer.Po \ @AMDEP_TRUE@ $(DEPDIR)/bzero.Po $(DEPDIR)/crc.Po \ @AMDEP_TRUE@ $(DEPDIR)/debug.Po $(DEPDIR)/escape.Po \ @AMDEP_TRUE@ $(DEPDIR)/getlin.Po $(DEPDIR)/getop1.Po \ @AMDEP_TRUE@ $(DEPDIR)/getopt.Po $(DEPDIR)/memchr.Po \ @AMDEP_TRUE@ $(DEPDIR)/memcmp.Po $(DEPDIR)/memcpy.Po \ @AMDEP_TRUE@ $(DEPDIR)/parse.Po $(DEPDIR)/quote.Po \ @AMDEP_TRUE@ $(DEPDIR)/quotes.Po $(DEPDIR)/spool.Po \ @AMDEP_TRUE@ $(DEPDIR)/status.Po $(DEPDIR)/strcas.Po \ @AMDEP_TRUE@ $(DEPDIR)/strchr.Po $(DEPDIR)/strdup.Po \ @AMDEP_TRUE@ $(DEPDIR)/strncs.Po $(DEPDIR)/strrch.Po \ @AMDEP_TRUE@ $(DEPDIR)/strstr.Po $(DEPDIR)/strtol.Po \ @AMDEP_TRUE@ $(DEPDIR)/strtou.Po $(DEPDIR)/xfree.Po \ @AMDEP_TRUE@ $(DEPDIR)/xmall.Po $(DEPDIR)/xreall.Po COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ CFLAGS = @CFLAGS@ DIST_SOURCES = $(libuucp_a_SOURCES) DIST_COMMON = Makefile.am Makefile.in bsrch.c bzero.c getlin.c memchr.c \ memcmp.c memcpy.c strcas.c strchr.c strdup.c strncs.c strrch.c \ strstr.c strtol.c strtou.c SOURCES = $(libuucp_a_SOURCES) all: all-am .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) cd $(top_srcdir) && \ $(AUTOMAKE) --gnu lib/Makefile Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status cd $(top_builddir) && \ CONFIG_HEADERS= CONFIG_LINKS= \ CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status AR = ar clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libuucp.a: $(libuucp_a_OBJECTS) $(libuucp_a_DEPENDENCIES) -rm -f libuucp.a $(libuucp_a_AR) libuucp.a $(libuucp_a_OBJECTS) $(libuucp_a_LIBADD) $(RANLIB) libuucp.a mostlyclean-compile: -rm -f *.$(OBJEXT) core *.core distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/bsrch.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/bzero.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/crc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/debug.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/escape.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getlin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getop1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getopt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/memchr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/memcmp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/memcpy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/parse.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/quote.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/quotes.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/spool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/status.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strcas.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strchr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strdup.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strncs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strrch.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)/strtou.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/xfree.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/xmall.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/xreall.Po@am__quote@ distclean-depend: -rm -rf $(DEPDIR) .c.o: @AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< .c.obj: @AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(COMPILE) -c `cygpath -w $<` CCDEPMODE = @CCDEPMODE@ uninstall-info-am: tags: TAGS ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(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 $(LISP) TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(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 "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) GTAGS: here=`CDPATH=: && cd $(top_builddir) && pwd` \ && cd $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) $$here distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) top_distdir = .. distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) distdir: $(DISTFILES) @for file in $(DISTFILES); do \ if test -f $$file; then d=.; else d=$(srcdir); fi; \ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ if test "$$dir" != "$$file" && test "$$dir" != "."; then \ $(mkinstalldirs) "$(distdir)/$$dir"; \ fi; \ if test -d $$d/$$file; then \ cp -pR $$d/$$file $(distdir) \ || 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 $(LIBRARIES) installdirs: 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)" \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* 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-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am distclean-am: clean-am distclean-compile distclean-depend \ distclean-generic distclean-tags dvi: dvi-am dvi-am: info: info-am info-am: install-data-am: install-exec-am: install-info: install-info-am install-man: installcheck-am: maintainer-clean: maintainer-clean-am maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic uninstall-am: uninstall-info-am .PHONY: GTAGS all all-am check check-am clean clean-generic \ clean-noinstLIBRARIES distclean distclean-compile \ distclean-depend distclean-generic distclean-tags distdir dvi \ dvi-am info info-am install install-am install-data \ install-data-am install-exec install-exec-am install-info \ install-info-am install-man install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic tags uninstall uninstall-am \ uninstall-info-am # 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: uucp-1.07/lib/bsrch.c0000664000076400007640000000323707665321760010144 /* Copyright (C) 1991 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. This file was modified slightly by Ian Lance Taylor, May 1992, for Taylor UUCP. */ #include "uucp.h" /* Perform a binary search for KEY in BASE which has NMEMB elements of SIZE bytes each. The comparisons are done by (*COMPAR)(). */ pointer bsearch (key, base, nmemb, size, compar) register constpointer key; register constpointer base; size_t nmemb; register size_t size; register int (*compar) P((constpointer, constpointer)); { register size_t l, u, idx; register constpointer p; register int comparison; l = 0; u = nmemb; while (l < u) { idx = (l + u) / 2; p = (constpointer) (((const char *) base) + (idx * size)); comparison = (*compar)(key, p); if (comparison < 0) u = idx; else if (comparison > 0) l = idx + 1; else return (pointer) p; } return NULL; } uucp-1.07/lib/bzero.c0000664000076400007640000000025607665321760010162 /* bzero.c Zero out a buffer. */ #include "uucp.h" void bzero (parg, c) pointer parg; int c; { char *p = (char *) parg; while (c-- != 0) *p++ = 0; } uucp-1.07/lib/getlin.c0000664000076400007640000000356407665321760010330 /* getlin.c Replacement for getline. Copyright (C) 1992 Ian Lance Taylor This file is part of Taylor UUCP. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" /* Read a line from a file, returning the number of characters read. This should really return ssize_t. Returns -1 on error. */ #define CGETLINE_DEFAULT (63) int getline (pzline, pcline, e) char **pzline; size_t *pcline; FILE *e; { char *zput, *zend; int bchar; if (*pzline == NULL) { *pzline = (char *) malloc (CGETLINE_DEFAULT); if (*pzline == NULL) return -1; *pcline = CGETLINE_DEFAULT; } zput = *pzline; zend = *pzline + *pcline - 1; while ((bchar = getc (e)) != EOF) { if (zput >= zend) { size_t cnew; char *znew; cnew = *pcline * 2 + 1; znew = (char *) realloc ((pointer) *pzline, cnew); if (znew == NULL) return -1; zput = znew + *pcline - 1; zend = znew + cnew - 1; *pzline = znew; *pcline = cnew; } *zput++ = bchar; if (bchar == '\n') break; } if (zput == *pzline) return -1; *zput = '\0'; return zput - *pzline; } uucp-1.07/lib/memchr.c0000664000076400007640000001203507665321760010312 /* Copyright (C) 1991 Free Software Foundation, Inc. Based on strlen implemention 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). The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. This file was modified slightly by Ian Lance Taylor, May 1992, for Taylor UUCP. It assumes 32 bit longs. I'm willing to trust that any system which does not have 32 bit longs will have its own implementation of memchr. */ #include "uucp.h" /* Search no more than N bytes of S for C. */ pointer memchr (s, c, n) constpointer s; int c; size_t n; { const char *char_ptr; const unsigned long int *longword_ptr; unsigned long int longword, magic_bits, charmask; c = BUCHAR (c); /* Handle the first few characters by reading one character at a time. Do this until CHAR_PTR is aligned on a 4-byte border. */ for (char_ptr = s; n > 0 && ((unsigned long int) char_ptr & 3) != 0; --n, ++char_ptr) if (BUCHAR (*char_ptr) == c) return (pointer) char_ptr; longword_ptr = (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. */ magic_bits = 0x7efefeff; /* Set up a longword, each of whose bytes is C. */ charmask = c | (c << 8); charmask |= charmask << 16; /* 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 >= 4) { /* 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 char *cp = (const char *) (longword_ptr - 1); if (BUCHAR (cp[0]) == c) return (pointer) cp; if (BUCHAR (cp[1]) == c) return (pointer) &cp[1]; if (BUCHAR (cp[2]) == c) return (pointer) &cp[2]; if (BUCHAR (cp[3]) == c) return (pointer) &cp[3]; } n -= 4; } char_ptr = (const char *) longword_ptr; while (n-- > 0) { if (BUCHAR (*char_ptr) == c) return (pointer) char_ptr; else ++char_ptr; } return NULL; } uucp-1.07/lib/memcmp.c0000664000076400007640000000053507665321760010317 /* memcmp.c Compare two memory buffers. */ #include "uucp.h" int memcmp (p1arg, p2arg, c) constpointer p1arg; constpointer p2arg; size_t c; { const char *p1 = (const char *) p1arg; const char *p2 = (const char *) p2arg; while (c-- != 0) if (*p1++ != *p2++) return BUCHAR (*--p1) - BUCHAR (*--p2); return 0; } uucp-1.07/lib/memcpy.c0000664000076400007640000000047507665321760010336 /* memcpy.c Copy one memory buffer to another. */ #include "uucp.h" pointer memcpy (ptoarg, pfromarg, c) pointer ptoarg; constpointer pfromarg; size_t c; { char *pto = (char *) ptoarg; const char *pfrom = (const char *) pfromarg; while (c-- != 0) *pto++ = *pfrom++; return ptoarg; } uucp-1.07/lib/strcas.c0000664000076400007640000000101307665321760010330 /* strcas.c Compare two strings case insensitively. */ #include "uucp.h" #include int strcasecmp (z1, z2) const char *z1; const char *z2; { char b1, b2; while ((b1 = *z1++) != '\0') { b2 = *z2++; if (b2 == '\0') return 1; if (b1 != b2) { if (isupper (BUCHAR (b1))) b1 = tolower (BUCHAR (b1)); if (isupper (BUCHAR (b2))) b2 = tolower (BUCHAR (b2)); if (b1 != b2) return b1 - b2; } } if (*z2 == '\0') return 0; else return -1; } uucp-1.07/lib/strchr.c0000664000076400007640000000037707665321760010352 /* strchr.c Look for a character in a string. This works for a null byte. */ #include "uucp.h" char * strchr (z, b) const char *z; int b; { b = (char) b; while (*z != b) if (*z++ == '\0') return NULL; return (char *) z; } uucp-1.07/lib/strdup.c0000664000076400007640000000040307665321760010354 /* strdup.c Duplicate a string into memory. */ #include "uucp.h" char * strdup (z) const char *z; { size_t csize; char *zret; csize = strlen (z) + 1; zret = malloc (csize); if (zret != NULL) memcpy (zret, z, csize); return zret; } uucp-1.07/lib/strncs.c0000664000076400007640000000116007665321760010350 /* strncs.c Compare two strings case insensitively up to a point. */ #include "uucp.h" #include int strncasecmp (z1, z2, c) const char *z1; const char *z2; size_t c; { char b1, b2; if (c == 0) return 0; while ((b1 = *z1++) != '\0') { b2 = *z2++; if (b2 == '\0') return 1; if (b1 != b2) { if (isupper (BUCHAR (b1))) b1 = tolower (BUCHAR (b1)); if (isupper (BUCHAR (b2))) b2 = tolower (BUCHAR (b2)); if (b1 != b2) return b1 - b2; } --c; if (c == 0) return 0; } if (*z2 == '\0') return 0; else return -1; } uucp-1.07/lib/strrch.c0000664000076400007640000000060507665321760010344 /* strrch.c Look for the last occurrence of a character in a string. This is supposed to work for a null byte, although we never actually call it with one. */ #include "uucp.h" char * strrchr (z, b) const char *z; int b; { char *zret; b = (char) b; zret = NULL; do { if (*z == b) zret = (char *) z; } while (*z++ != '\0'); return zret; } uucp-1.07/lib/strstr.c0000664000076400007640000000343707665321760010406 /* Copyright (C) 1991, 1992 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. This file was modified slightly by Ian Lance Taylor, May 1992, for Taylor UUCP. */ #include "uucp.h" /* Return the first ocurrence of NEEDLE in HAYSTACK. */ char * strstr (haystack, needle) const char *const haystack; const char *const needle; { register const char *const needle_end = strchr(needle, '\0'); register const char *const haystack_end = strchr(haystack, '\0'); register const size_t needle_len = needle_end - needle; register const size_t needle_last = needle_len - 1; register const char *begin; if (needle_len == 0) return (char *) haystack_end; if ((size_t) (haystack_end - haystack) < needle_len) return NULL; for (begin = &haystack[needle_last]; begin < haystack_end; ++begin) { register const char *n = &needle[needle_last]; register const char *h = begin; do if (*h != *n) goto loop; while (--n >= needle && --h >= haystack); return (char *) h; loop:; } return NULL; } uucp-1.07/lib/strtol.c0000664000076400007640000000764507665321760010401 /* Copyright (C) 1991 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. This file was modified slightly by Ian Lance Taylor, May 1992, for Taylor UUCP. */ #include "uucp.h" #include #include #if HAVE_LIMITS_H #include #else #define ULONG_MAX 4294967295 #define LONG_MIN (- LONG_MAX - 1) #define LONG_MAX 2147483647 #endif #ifndef UNSIGNED #define UNSIGNED 0 #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. */ #if UNSIGNED unsigned long int #define strtol strtoul #else long int #endif strtol (nptr, endptr, base) const char *nptr; char **endptr; int base; { int negative; register unsigned long int cutoff; register unsigned int cutlim; register unsigned long int i; register const char *s; register unsigned int c; const char *save; int overflow; if (base < 0 || base == 1 || base > 36) base = 10; s = nptr; /* Skip white space. */ while (isspace(BUCHAR (*s))) ++s; if (*s == '\0') goto noconv; /* Check for a sign. */ if (*s == '-') { negative = 1; ++s; } else if (*s == '+') { negative = 0; ++s; } else negative = 0; if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) s += 2; /* If BASE is zero, figure it out ourselves. */ if (base == 0) if (*s == '0') { if (s[1] == 'x' || s[1] == 'X') { s += 2; base = 16; } else base = 8; } else base = 10; /* Save the pointer so we can check later if anything happened. */ save = s; cutoff = ULONG_MAX / (unsigned long int) base; cutlim = ULONG_MAX % (unsigned long int) base; overflow = 0; i = 0; for (c = BUCHAR (*s); c != '\0'; c = BUCHAR (*++s)) { if (isdigit(c)) c -= '0'; else if (islower(c)) c = c - 'a' + 10; else if (isupper(c)) c = c - 'A' + 10; else break; if (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 = (char *) s; #if !UNSIGNED /* Check for a value that is within the range of `unsigned long int', but outside the range of `long int'. */ if (i > (negative ? - (unsigned long int) LONG_MIN : (unsigned long int) LONG_MAX)) overflow = 1; #endif if (overflow) { errno = ERANGE; #if UNSIGNED return ULONG_MAX; #else return negative ? LONG_MIN : LONG_MAX; #endif } /* Return the result of the appropriate sign. */ return (negative ? - i : i); noconv: /* There was no number to convert. */ if (endptr != NULL) *endptr = (char *) nptr; return 0L; } uucp-1.07/lib/strtou.c0000664000076400007640000000156607665321760010406 /* Copyright (C) 1991 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define UNSIGNED 1 #include "lib/strtol.c" uucp-1.07/lib/buffer.c0000664000076400007640000000604607665321760010315 /* buffer.c Manipulate buffers used to hold strings. Copyright (C) 1992, 1993, 2002 Ian Lance Taylor This file is part of Taylor UUCP. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" /* Define MALLOC_BUFFERS when compiling this file in order to more effectively use a debugging malloc library. */ #ifndef MALLOC_BUFFERS /* We keep a linked list of buffers. The union is a hack because the default definition of offsetof, in uucp.h, takes the address of the field, and some C compilers will not let you take the address of an array. */ struct sbuf { struct sbuf *qnext; size_t c; union { char ab[4]; char bdummy; } u; }; static struct sbuf *qBlist; /* Get a buffer of a given size. The buffer is returned with the ubuffree function. */ char * zbufalc (c) size_t c; { register struct sbuf *q; if (qBlist == NULL) { q = (struct sbuf *) xmalloc (sizeof (struct sbuf) + c - 4); q->c = c; } else { q = qBlist; qBlist = q->qnext; if (q->c < c) { q = (struct sbuf *) xrealloc ((pointer) q, sizeof (struct sbuf) + c - 4); q->c = c; } } return q->u.ab; } /* Free up a buffer back onto the linked list. */ void ubuffree (z) char *z; { struct sbuf *q; /* The type of ioff should be size_t, but making it int avoids a bug in some versions of the HP/UX compiler, and will always work. */ int ioff; if (z == NULL) return; ioff = offsetof (struct sbuf, u); q = (struct sbuf *) (pointer) (z - ioff); #ifdef DEBUG_BUFFER { struct sbuf *qlook; for (qlook = qBlist; qlook != NULL; qlook = qlook->qnext) { if (qlook == q) { ulog (LOG_ERROR, "ubuffree: Attempt to free buffer twice"); abort (); } } } #endif q->qnext = qBlist; qBlist = q; } #else /* MALLOC_BUFFERS */ char * zbufalc (c) size_t c; { return (char *) xmalloc (c); } /* Free up a buffer back onto the linked list. */ void ubuffree (z) char *z; { free (z); } #endif /* MALLOC_BUFFERS */ /* Get a buffer holding a given string. */ char * zbufcpy (z) const char *z; { size_t csize; char *zret; if (z == NULL) return NULL; csize = strlen (z) + 1; zret = zbufalc (csize); memcpy (zret, z, csize); return zret; } uucp-1.07/lib/crc.c0000664000076400007640000001520507665321760007610 /* * Copyright (C) 1986 Gary S. Brown. You may use this program, or * code or tables extracted from it, as desired without restriction. */ /* Modified slightly by Ian Lance Taylor, ian@airs.com, for use with Taylor UUCP. */ #include "uucp.h" #include "prot.h" /* First, the polynomial itself and its table of feedback terms. The */ /* polynomial is */ /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ /* Note that we take it "backwards" and put the highest-order term in */ /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ /* the MSB being 1. */ /* Note that the usual hardware shift register implementation, which */ /* is what we're using (we're merely optimizing it by doing eight-bit */ /* chunks at a time) shifts bits into the lowest-order term. In our */ /* implementation, that means shifting towards the right. Why do we */ /* do it this way? Because the calculated CRC must be transmitted in */ /* order from highest-order term to lowest-order term. UARTs transmit */ /* characters in order from LSB to MSB. By storing the CRC this way, */ /* we hand it to the UART in the order low-byte to high-byte; the UART */ /* sends each low-bit to hight-bit; and the result is transmission bit */ /* by bit from highest- to lowest-order term without requiring any bit */ /* shuffling on our part. Reception works similarly. */ /* The feedback terms table consists of 256, 32-bit entries. Notes: */ /* */ /* The table can be generated at runtime if desired; code to do so */ /* is shown later. It might not be obvious, but the feedback */ /* terms simply represent the results of eight shift/xor opera- */ /* tions for all combinations of data and CRC register values. */ /* [this code is no longer present--ian] */ /* */ /* The values must be right-shifted by eight bits by the "updcrc" */ /* logic; the shift must be unsigned (bring in zeroes). On some */ /* hardware you could probably optimize the shift in assembler by */ /* using byte-swap instructions. */ static const unsigned long aicrc32tab[] = { /* CRC polynomial 0xedb88320 */ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; /* * IUPDC32 macro derived from article Copyright (C) 1986 Stephen Satchell. * NOTE: First argument must be in range 0 to 255. * Second argument is referenced twice. * * Programmers may incorporate any or all code into their programs, * giving proper credit within the source. Publication of the * source routines is permitted so long as proper credit is given * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, * Omen Technology. */ #define IUPDC32(b, ick) \ (aicrc32tab[((int) (ick) ^ (b)) & 0xff] ^ (((ick) >> 8) & 0x00ffffffL)) unsigned long icrc (z, c, ick) const char *z; size_t c; unsigned long ick; { while (c > 4) { ick = IUPDC32 (*z++, ick); ick = IUPDC32 (*z++, ick); ick = IUPDC32 (*z++, ick); ick = IUPDC32 (*z++, ick); c -= 4; } while (c-- != 0) ick = IUPDC32 (*z++, ick); return ick; } uucp-1.07/lib/debug.c0000664000076400007640000000641407665321760010131 /* debug.c UUCP debugging functions. Copyright (C) 1991, 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include #include "uudefs.h" #if DEBUG > 1 /* The debugging level. */ int iDebug; /* Parse a debugging string. This may be a simple number, which sets the given number of bits in iDebug, or it may be a series of single letters. */ static const char * const azDebug_names[] = DEBUG_NAMES; int idebug_parse (z) const char *z; { char *zend; int i, iret; char *zcopy, *ztok; if (strncasecmp (z, DEBUG_NONE, sizeof DEBUG_NONE - 1) == 0) return 0; i = (int) strtol ((char *) z, &zend, 0); if (*zend == '\0') { if (i > 15) i = 15; else if (i < 0) i = 0; return (1 << i) - 1; } zcopy = zbufcpy (z); iret = 0; for (ztok = strtok (zcopy, ", \t"); ztok != NULL; ztok = strtok ((char *) NULL, ", \t")) { if (strcasecmp (ztok, "all") == 0) { iret = DEBUG_MAX; break; } for (i = 0; azDebug_names[i] != NULL; i++) { if (strncasecmp (ztok, azDebug_names[i], strlen (azDebug_names[i])) == 0) { iret |= 1 << i; break; } } if (azDebug_names[i] == NULL) ulog (LOG_ERROR, "Unrecognized debugging option \"%s\"", ztok); } ubuffree (zcopy); return iret; } #endif /* DEBUG > 1 */ /* A debugging routine used when displaying buffers. */ size_t cdebug_char (z, ichar) char *z; int ichar; { char b; if (isprint (BUCHAR (ichar)) && ichar != '\"' && ichar != '\\') { *z++ = (char) ichar; *z = '\0'; return 1; } *z++ = '\\'; switch (ichar) { case '\n': b = 'n'; break; case '\r': b = 'r'; break; case '\"': b = '\"'; break; case '\\': b = '\\'; break; default: sprintf (z, "%03o", (unsigned int) BUCHAR (ichar)); return strlen (z) + 1; } *z++ = b; *z = '\0'; return 2; } #if DEBUG > 1 /* Display a buffer when debugging. */ void udebug_buffer (zhdr, zbuf, clen) const char *zhdr; const char *zbuf; size_t clen; { char *z, *zalc; size_t i; zalc = zbufalc (clen * 4 + 1); z = zalc; for (i = 0; i < clen && i < 80; i++) z += cdebug_char (z, zbuf[i]); if (i < clen) { *z++ = '.'; *z++ = '.'; *z++ = '.'; } *z = '\0'; ulog (LOG_DEBUG, "%s %lu \"%s\"", zhdr, (unsigned long) clen, zalc); ubuffree (zalc); } #endif uucp-1.07/lib/escape.c0000664000076400007640000000300607665321760010275 /* escape.c Translate escape sequences. */ #include "uucp.h" #include #include "uudefs.h" size_t cescape (z) char *z; { char *zto, *zfrom; zto = z; zfrom = z; while (*zfrom != '\0') { if (*zfrom != '\\') { *zto++ = *zfrom++; continue; } ++zfrom; switch (*zfrom) { case '-': *zto++ = '-'; break; case 'b': *zto++ = '\b'; break; case 'n': *zto++ = '\n'; break; case 'N': *zto++ = '\0'; break; case 'r': *zto++ = '\r'; break; case 's': *zto++ = ' '; break; case 't': *zto++ = '\t'; break; case '\0': --zfrom; /* Fall through. */ case '\\': *zto++ = '\\'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int i; i = *zfrom - '0'; if (zfrom[1] >= '0' && zfrom[1] <= '7') i = 8 * i + *++zfrom - '0'; if (zfrom[1] >= '0' && zfrom[1] <= '7') i = 8 * i + *++zfrom - '0'; *zto++ = (char) i; } break; case 'x': { int i; i = 0; while (isxdigit (BUCHAR (zfrom[1]))) { if (isdigit (BUCHAR (zfrom[1]))) i = 16 * i + *++zfrom - '0'; else if (isupper (BUCHAR (zfrom[1]))) i = 16 * i + *++zfrom - 'A' + 10; else i = 16 * i + *++zfrom - 'a' + 10; } *zto++ = (char) i; } break; default: ulog (LOG_ERROR, "Unrecognized escape sequence \\%c", *zfrom); *zto++ = *zfrom; break; } ++zfrom; } *zto = '\0'; return (size_t) (zto - z); } uucp-1.07/lib/getopt.c0000664000076400007640000004313307665321760010344 /* 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 roland@gnu.ai.mit.edu before changing it! Copyright (C) 1987, 88, 89, 90, 91, 1992 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. This file was modified slightly by Ian Lance Taylor, June 1992, for Taylor UUCP. */ #include "uucp.h" #include "uudefs.h" /* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a long-named option. Because this is not POSIX.2 compliant, it is being phased out. */ #undef GETOPT_COMPAT /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' 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. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the 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.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 = 0; /* 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 EOF, 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. */ int optind = 0; /* 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. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* 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. 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 EOF with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; #define my_index strchr #define my_bcopy(src, dst, n) memcpy ((dst), (src), (n)) /* 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. */ static int first_nonopt; static int last_nonopt; /* 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 (argv) char **argv; { size_t nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); char **temp = (char **) malloc (nonopts_size); if (temp == NULL) abort (); /* Interchange the two blocks of data in ARGV. */ my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size); my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt], (optind - last_nonopt) * sizeof (char *)); my_bcopy ((char *) temp, (char *) &argv[first_nonopt + optind - last_nonopt], nonopts_size); xfree (temp); /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* 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 `EOF'. 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. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. 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. */ int _getopt_internal (argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { int option_index; optarg = 0; /* Initialize the internal data when the first call is made. 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. */ if (optind == 0) { first_nonopt = last_nonopt = optind = 1; nextchar = NULL; /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (getenv ("POSIXLY_CORRECT") != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; } if (nextchar == NULL || *nextchar == '\0') { if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Now skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && (argv[optind][0] != '-' || argv[optind][1] == '\0') #ifdef GETOPT_COMPAT && (longopts == NULL || argv[optind][0] != '+' || argv[optind][1] == '\0') #endif /* GETOPT_COMPAT */ ) optind++; last_nonopt = optind; } /* 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 (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; 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 (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return EOF; } /* 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 ((argv[optind][0] != '-' || argv[optind][1] == '\0') #ifdef GETOPT_COMPAT && (longopts == NULL || argv[optind][0] != '+' || argv[optind][1] == '\0') #endif /* GETOPT_COMPAT */ ) { if (ordering == REQUIRE_ORDER) return EOF; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Start decoding its characters. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } if (longopts != NULL && ((argv[optind][0] == '-' && (argv[optind][1] == '-' || long_only)) #ifdef GETOPT_COMPAT || argv[optind][0] == '+' #endif /* GETOPT_COMPAT */ )) { const struct option *p; char *s = nextchar; int exact = 0; int ambig = 0; const struct option *pfound = NULL; int indfound = 0; while (*s && *s != '=') s++; /* Test all options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, (size_t) (s - nextchar))) { if (s - 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 nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*s) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = s + 1; else { if (opterr) { if (argv[optind - 1][1] == '-') /* --option */ fprintf (stderr, "%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name); else /* +option or -option */ fprintf (stderr, "%s: option `%c%s' doesn't allow an argument\n", argv[0], argv[optind - 1][0], pfound->name); } nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]); nextchar += strlen (nextchar); return '?'; } } nextchar += strlen (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[optind][1] == '-' #ifdef GETOPT_COMPAT || argv[optind][0] == '+' #endif /* GETOPT_COMPAT */ || my_index (optstring, *nextchar) == NULL) { if (opterr) { if (argv[optind][1] == '-') /* --option */ fprintf (stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar); else /* +option or -option */ fprintf (stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; return '?'; } } /* Look at and handle the next option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (opterr) { if (c < 040 || c >= 0177) fprintf (stderr, "%s: unrecognized option, character code 0%o\n", argv[0], BUCHAR (c)); else fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); } return '?'; } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = 0; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) fprintf (stderr, "%s: option `-%c' requires an argument\n", argv[0], c); c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main (argc, argv) 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 == EOF) 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 */ uucp-1.07/lib/getop1.c0000664000076400007640000000632307665321760010241 /* Getopt for GNU. Copyright (C) 1987, 88, 89, 90, 91, 1992 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. This file was modified slightly by Ian Lance Taylor, June 1992, for Taylor UUCP. */ #include "uucp.h" #include "getopt.h" int getopt_long (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } /* 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 (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 1); } #ifdef TEST #include int main (argc, argv) 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 == EOF) 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 */ uucp-1.07/lib/parse.c0000664000076400007640000001451407665321760010155 /* parse.c Parse a UUCP command string. Copyright (C) 1991, 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char parse_rcsid[] = "$Id: parse.c,v 1.11 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" /* Local functions. */ static void ulunquote_cmd P((struct scmd *qcmd)); /* Parse a UUCP command string into an scmd structure. This is called by the 'g' protocol and the UNIX command file reading routines. It destroys the string it is passed, and the scmd string pointers are left pointing into it. For the convenience of the Unix work file routines, it will parse "P" into a simple 'P' command (representing a poll file). If 'q' appears in the options, it will unquote all the relevant strings. It returns TRUE if the string is successfully parsed, FALSE otherwise. */ boolean fparse_cmd (zcmd, qcmd) char *zcmd; struct scmd *qcmd; { char *z, *zend; z = strtok (zcmd, " \t\n"); if (z == NULL) return FALSE; qcmd->bcmd = *z; if (qcmd->bcmd != 'S' && qcmd->bcmd != 'R' && qcmd->bcmd != 'X' && qcmd->bcmd != 'E' && qcmd->bcmd != 'H' && qcmd->bcmd != 'P') return FALSE; qcmd->bgrade = '\0'; qcmd->pseq = NULL; qcmd->zfrom = NULL; qcmd->zto = NULL; qcmd->zuser = NULL; qcmd->zoptions = NULL; qcmd->ztemp = NULL; qcmd->imode = 0666; qcmd->znotify = NULL; qcmd->cbytes = -1; qcmd->zcmd = NULL; qcmd->ipos = 0; /* Handle hangup commands specially. If it's just "H", return the command 'H' to indicate a hangup request. If it's "HY" return 'Y' and if it's "HN" return 'N'. */ if (qcmd->bcmd == 'H') { if (z[1] != '\0') { if (z[1] == 'Y') qcmd->bcmd = 'Y'; else if (z[1] == 'N') qcmd->bcmd = 'N'; else return FALSE; } return TRUE; } if (qcmd->bcmd == 'P') return TRUE; if (z[1] != '\0') return FALSE; z = strtok ((char *) NULL, " \t\n"); if (z == NULL) return FALSE; qcmd->zfrom = z; z = strtok ((char *) NULL, " \t\n"); if (z == NULL) return FALSE; qcmd->zto = z; z = strtok ((char *) NULL, " \t\n"); if (z == NULL) return FALSE; qcmd->zuser = z; z = strtok ((char *) NULL, " \t\n"); if (z == NULL || *z != '-') return FALSE; qcmd->zoptions = z + 1; if (qcmd->bcmd == 'X') { ulunquote_cmd (qcmd); return TRUE; } if (qcmd->bcmd == 'R') { z = strtok ((char *) NULL, " \t\n"); if (z != NULL) { if (strcmp (z, "dummy") != 0) { /* This may be the maximum number of bytes the remote system wants to receive, if it using Taylor UUCP size negotiation. */ qcmd->cbytes = strtol (z, &zend, 0); if (*zend != '\0') qcmd->cbytes = -1; } else { /* This is from an SVR4 system, and may include the position at which to start sending the file. The next fields are the mode bits, the remote owner (?), the remote temporary file name, and finally the restart position. */ if (strtok ((char *) NULL, " \t\n") != NULL && strtok ((char *) NULL, " \t\n") != NULL && strtok ((char *) NULL, " \t\n") != NULL) { z = strtok ((char *) NULL, " \t\n"); if (z != NULL) { qcmd->ipos = strtol (z, &zend, 0); if (*zend != '\0') qcmd->ipos = 0; } } } } ulunquote_cmd (qcmd); return TRUE; } z = strtok ((char *) NULL, " \t\n"); if (z == NULL) return FALSE; qcmd->ztemp = z; z = strtok ((char *) NULL, " \t\n"); if (z == NULL) return FALSE; qcmd->imode = (int) strtol (z, &zend, 0); if (*zend != '\0') return FALSE; /* As a magic special case, if the mode came out as the decimal values 666 or 777, assume that they actually meant the octal values. Most systems use a leading zero, but a few do not. Since both 666 and 777 are greater than the largest legal mode value, which is 0777 == 511, this hack does not restrict any legal values. */ if (qcmd->imode == 666) qcmd->imode = 0666; else if (qcmd->imode == 777) qcmd->imode = 0777; z = strtok ((char *) NULL, " \t\n"); if (qcmd->bcmd == 'E' && z == NULL) return FALSE; qcmd->znotify = z; /* SVR4 UUCP will send the string "dummy" after the notify string but before the size. I do not know when it sends anything other than "dummy". Fortunately, it doesn't really hurt to not get the file size. */ if (z != NULL && strcmp (z, "dummy") == 0) z = strtok ((char *) NULL, " \t\n"); if (z != NULL) { z = strtok ((char *) NULL, " \t\n"); if (z != NULL) { qcmd->cbytes = strtol (z, &zend, 0); if (*zend != '\0') qcmd->cbytes = -1; } else if (qcmd->bcmd == 'E') return FALSE; if (z != NULL) { z = strtok ((char *) NULL, ""); if (z != NULL) z[strcspn (z, "\n")] = '\0'; if (qcmd->bcmd == 'E' && z == NULL) return FALSE; qcmd->zcmd = z; } } ulunquote_cmd (qcmd); return TRUE; } /* If 'q' appears in the options of a command, unquote all the relevant strings. */ static void ulunquote_cmd (qcmd) struct scmd *qcmd; { if (qcmd->zoptions == NULL || strchr (qcmd->zoptions, 'q') == NULL) return; if (qcmd->zfrom != NULL) (void) cescape ((char *) qcmd->zfrom); if (qcmd->zto != NULL) (void) cescape ((char *) qcmd->zto); if (qcmd->zuser != NULL) (void) cescape ((char *) qcmd->zuser); if (qcmd->znotify != NULL) (void) cescape ((char *) qcmd->znotify); if (qcmd->zcmd != NULL) (void) cescape ((char *) qcmd->zcmd); } uucp-1.07/lib/quote.c0000664000076400007640000000643307665321760010201 /* quote.c Quote a UUCP command. Copyright (C) 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char quote_rcsid[] = "$Id: quote.c,v 1.2 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" /* Local functions. */ __inline__ static boolean fneeds_quotes P((const char *z)); /* Return whether a string needs quotes. We want to be conservative here--we don't want to reject a string which would work with an older UUCP version. */ __inline__ static boolean fneeds_quotes (z) const char *z; { return z != NULL && z[strcspn (z, " \t\n")] != '\0'; } /* Return whether a command needs quotes. */ boolean fcmd_needs_quotes (qcmd) const struct scmd *qcmd; { if (fneeds_quotes (qcmd->zfrom) || fneeds_quotes (qcmd->zto) || fneeds_quotes (qcmd->zuser) || fneeds_quotes (qcmd->znotify)) return TRUE; /* We don't check qcmd->zcmd. It is already permitted to have spaces, and uux will never generate a command with an embedded newline. */ return FALSE; } /* Quote the strings which appear in a UUCP command string. Add 'q' to the list of options. This creates a new command in qnew, with freshly allocated strings. */ void uquote_cmd (qorig, qnew) const struct scmd *qorig; struct scmd *qnew; { qnew->bcmd = qorig->bcmd; qnew->bgrade = qorig->bgrade; qnew->pseq = qorig->pseq; qnew->zfrom = zquote_cmd_string (qorig->zfrom, FALSE); qnew->zto = zquote_cmd_string (qorig->zto, FALSE); qnew->zuser = zquote_cmd_string (qorig->zuser, FALSE); if (strchr (qorig->zoptions, 'q') != NULL) qnew->zoptions = zbufcpy (qorig->zoptions); else { size_t clen; char *z; clen = strlen (qorig->zoptions); z = zbufalc (clen + 2); memcpy (z, qorig->zoptions, clen); z[clen] = 'q'; z[clen + 1] = '\0'; qnew->zoptions = z; } qnew->ztemp = zbufcpy (qorig->ztemp); qnew->imode = qorig->imode; qnew->znotify = zquote_cmd_string (qorig->znotify, FALSE); qnew->cbytes = qorig->cbytes; /* The zcmd field is never quoted. */ qnew->zcmd = zbufcpy (qorig->zcmd); qnew->ipos = qorig->ipos; } /* Free a command structure created by uquote_cmd. */ void ufree_quoted_cmd (qcmd) struct scmd *qcmd; { ubuffree ((char *) qcmd->zfrom); ubuffree ((char *) qcmd->zto); ubuffree ((char *) qcmd->zuser); ubuffree ((char *) qcmd->ztemp); ubuffree ((char *) qcmd->znotify); ubuffree ((char *) qcmd->zcmd); ubuffree ((char *) qcmd->zoptions); } uucp-1.07/lib/quotes.c0000664000076400007640000000323507665321760010361 /* quotes.c Quote a field in a UUCP command. Copyright (C) 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char quotes_rcsid[] = "$Id: quotes.c,v 1.2 2002/03/05 19:10:42 ian Rel $"; #endif #include #include "uudefs.h" /* Copy a string, adding quotes if necessary. */ char * zquote_cmd_string (zorig, fbackslashonly) const char *zorig; boolean fbackslashonly; { const char *z; char *zret; char *zto; if (zorig == NULL) return NULL; zret = zbufalc (strlen (zorig) * 4 + 1); zto = zret; for (z = zorig; *z != '\0'; ++z) { if (*z == '\\') { *zto++ = '\\'; *zto++ = '\\'; } else if (fbackslashonly || isgraph (BUCHAR (*z))) *zto++ = *z; else { sprintf (zto, "\\%03o", (unsigned int) BUCHAR (*z)); zto += strlen (zto); } } *zto = '\0'; return zret; } uucp-1.07/lib/spool.c0000664000076400007640000000144207665321760010173 /* spool.c See whether a filename is legal for the spool directory. */ #include "uucp.h" #include #include "uudefs.h" /* See whether a file is a spool file. Spool file names are specially crafted to hand around to other UUCP packages. They always begin with 'C', 'D' or 'X', and the second character is always a period. The remaining characters may be any printable characters, since they may include a grade set by another system. */ boolean fspool_file (zfile) const char *zfile; { const char *z; if (*zfile != 'C' && *zfile != 'D' && *zfile != 'X') return FALSE; if (zfile[1] != '.') return FALSE; for (z = zfile + 2; *z != '\0'; z++) if (*z == '/' || ! isprint (BUCHAR (*z)) || isspace (BUCHAR (*z))) return FALSE; return TRUE; } uucp-1.07/lib/status.c0000664000076400007640000000106107665321760010357 /* status.c Strings for status codes. */ #include "uucp.h" #include "uudefs.h" /* Status strings. These must match enum tstatus_type. */ #if USE_TRADITIONAL_STATUS const char *azStatus[] = { "SUCCESSFUL", "DEVICE FAILED", "DIAL FAILED", "LOGIN FAILED", "STARTUP FAILED", "CONVERSATION FAILED", "TALKING", "WRONG TIME TO CALL" }; #else const char *azStatus[] = { "Conversation complete", "Port unavailable", "Dial failed", "Login failed", "Handshake failed", "Call failed", "Talking", "Wrong time to call" }; #endif uucp-1.07/lib/xfree.c0000664000076400007640000000037707665321760010156 /* xfree.c Some versions of free (like the one in SCO Unix 3.2.2) don't handle null pointers correctly, so we go through our own routine. */ #include "uucp.h" #include "uudefs.h" void xfree (p) pointer p; { if (p != NULL) free (p); } uucp-1.07/lib/xmall.c0000664000076400007640000000040707665321760010154 /* xmalloc.c Allocate a block of memory without fail. */ #include "uucp.h" #include "uudefs.h" pointer xmalloc (c) size_t c; { pointer pret; pret = malloc (c); if (pret == NULL && c != 0) ulog (LOG_FATAL, "Out of memory"); return pret; } uucp-1.07/lib/xreall.c0000664000076400007640000000066007665321760010327 /* xreall.c Realloc a block of memory without fail. Supposedly some versions of realloc can't handle a NULL first argument, so we check for that here. */ #include "uucp.h" #include "uudefs.h" pointer xrealloc (p, c) pointer p; size_t c; { pointer pret; if (p == NULL) return xmalloc (c); pret = realloc (p, c); if (pret == NULL && c != 0) ulog (LOG_FATAL, "Out of memory"); return pret; } uucp-1.07/uuconf/0000777000076400007640000000000007665533772007515 5uucp-1.07/uuconf/README0000664000076400007640000001100207665321761010276 This is the README file for the beta release of the uuconf library. It was written by Ian Lance Taylor. I can be reached at ian@airs.com. This package is covered by the Gnu Library General Public License. See the file COPYING.LIB for details. If you would like to do something with this package that you feel is reasonable but you feel is prohibited by the license, contact me to see if we can work it out. WHAT IT IS This is a beta release of the uuconf library. The uuconf library provides a set of functions which can be used to read UUCP configuration files. V2, HDB, and Taylor UUCP configuration files are supported. Also included are two programs, uuchk and uuconv. uuchk will read configuration files and display the information it finds in a verbose format. This can be helpful to ensure that your configuration files are set up as you expect. uuconv can be used to convert configuration files from one type to another. This is particularly helpful for people installing Taylor UUCP on a existing system who want to take advantage of the additional functionality provided by the Taylor UUCP configuration files. This is strictly a beta release. The library provides all the information needed for uuchk and uuconv, but does not yet provide everything needed for uucp or cu. I am releasing it now to get feedback and to provide the uuconv program to people using Taylor UUCP. This may well be the only time this library is release independently. This library will be provided with Taylor UUCP, and future releases of the library will probably only occur as part of the complete Taylor UUCP package. HOW TO USE IT Configure and optionally install the package as described in INSTALL. The functions provided by the library are described in uuconf.h. At the moment there is no additional documentation. Programs which use the library should include uuconf.h, and should not include any of the other header files. The functions listed in uuconf.h all begin with the string "uuconf_". The internal library functions all begin with the string "_uuconf_". The internal library functions should not be called by a program which uses the library, as they may change in future releases. The uuchk program is an example of program which uses the library; uuconv is not, as it relies upon internal data structures. The uuchk program takes a single optional option, -I, which may be used to specify an alternate Taylor UUCP main configuration file. The default configuration file is $(newconfigdir)/config ($(newconfigdir) is defined in Makefile). For example: uuchk uuchk -I /usr/tmp/tstuu/Config1 The uuconv program requires two options: -i to specify the input type and -o to specify the output type. Both options take a string argument, which must be one of "v2", "hdb", or "taylor". uuconv also takes an optional -I option, which is the same as the -I option to uuchk. The conversion is not intended to be perfect, and the results should be manually inspected. In particular, the dialcode file is not converted (as the format is the same for all three configuration file types, it may simply be copied to the appropriate new name). uuconv will create new files in the current working directory. For example: uuconv -i hdb -o taylor uuconv -i taylor -I /usr/tmp/tstuu/Config1 -o v2 NOTES The initial underscore on the internal library functions is required by the GNU standards. As ANSI C reserves external identifiers with an initial underscore for the implementation, it is possible, though unlikely, that this will cause problems on other implementations; no workaround is currently provided for such problems. The library functions rely upon the following functions: fclose fopen free fseek ftell getc isalpha isdigit islower isspace isupper malloc realloc rewind strchr strcmp strcspn strlen strncmp strspn tolower toupper and the following header files: ctype.h errno.h stdio.h If the following functions cannot be found by the configure script, replacements will be used (the replacement for strerror is Unix dependent): getline memcpy strcasecmp strdup strerror strncasecmp strtol If the following header files are found, they will be included: libc.h limits.h memory.h stddef.h stdlib.h string.h strings.h sys/types.h The following functions are required on Unix only: fcntl fileno The following headers are used, if found, on Unix only: fcntl.h sys/file.h uucp-1.07/uuconf/COPYING.LIB0000664000076400007640000006126107665321761011072 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, 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 library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, 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 companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Library 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! uucp-1.07/uuconf/Makefile.am0000664000076400007640000000202607665321761011460 # This is the auto-Makefile for the uuconf subdirectory of Taylor UUCP. noinst_LIBRARIES = libuuconf.a libuuconf_a_SOURCES = addblk.c addstr.c allblk.c alloc.c base.c bool.c \ callin.c calout.c chatc.c cmdarg.c cmdfil.c cmdlin.c cnfnms.c \ debfil.c deblev.c diacod.c dial.c diasub.c dnams.c errno.c errstr.c \ filnam.c freblk.c fredia.c free.c freprt.c fresys.c grdcmp.c hdial.c \ hdnams.c hinit.c hlocnm.c hport.c hrmunk.c hsinfo.c hsnams.c \ hsys.c hunk.c iniglb.c init.c int.c lckdir.c lineno.c llocnm.c \ local.c locnm.c logfil.c maxuxq.c mrgblk.c paramc.c port.c \ prtsub.c pubdir.c rdlocs.c rdperm.c reliab.c remunk.c runuxq.c \ sinfo.c snams.c split.c spool.c stafil.c strip.c syssub.c \ tcalou.c tdial.c tdialc.c tdnams.c tgcmp.c thread.c time.c \ tinit.c tlocnm.c tport.c tportc.c tsinfo.c tsnams.c tsys.c \ tval.c ugtlin.c unk.c val.c vinit.c vport.c vsinfo.c vsnams.c \ vsys.c alloc.h syshdr.h uucnfi.h AM_CFLAGS = -I.. -I$(srcdir)/.. $(WARN_CFLAGS) -DNEWCONFIGLIB=\"$(NEWCONFIGDIR)\" -DOLDCONFIGLIB=\"$(OLDCONFIGDIR)\" uucp-1.07/uuconf/Makefile.in0000664000076400007640000004423607665532205011477 # Makefile.in generated automatically by automake 1.5 from Makefile.am. # Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 # 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@ # This is the auto-Makefile for the uuconf subdirectory of Taylor UUCP. SHELL = @SHELL@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ sharedstatedir = @sharedstatedir@ localstatedir = @localstatedir@ libdir = @libdir@ infodir = @infodir@ mandir = @mandir@ includedir = @includedir@ oldincludedir = /usr/include pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = .. ACLOCAL = @ACLOCAL@ AUTOCONF = @AUTOCONF@ AUTOMAKE = @AUTOMAKE@ AUTOHEADER = @AUTOHEADER@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_HEADER = $(INSTALL_DATA) transform = @program_transform_name@ NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : AMTAR = @AMTAR@ AR = @AR@ AWK = @AWK@ CC = @CC@ DEPDIR = @DEPDIR@ EXEEXT = @EXEEXT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LIBOBJS = @LIBOBJS@ LN_S = @LN_S@ MAINT = @MAINT@ NEWCONFIGDIR = @NEWCONFIGDIR@ OBJEXT = @OBJEXT@ OLDCONFIGDIR = @OLDCONFIGDIR@ OWNER = @OWNER@ PACKAGE = @PACKAGE@ POUNDBANG = @POUNDBANG@ RANLIB = @RANLIB@ UNIXOBJS = @UNIXOBJS@ VERSION = @VERSION@ WARN_CFLAGS = @WARN_CFLAGS@ am__include = @am__include@ am__quote = @am__quote@ install_sh = @install_sh@ noinst_LIBRARIES = libuuconf.a libuuconf_a_SOURCES = addblk.c addstr.c allblk.c alloc.c base.c bool.c \ callin.c calout.c chatc.c cmdarg.c cmdfil.c cmdlin.c cnfnms.c \ debfil.c deblev.c diacod.c dial.c diasub.c dnams.c errno.c errstr.c \ filnam.c freblk.c fredia.c free.c freprt.c fresys.c grdcmp.c hdial.c \ hdnams.c hinit.c hlocnm.c hport.c hrmunk.c hsinfo.c hsnams.c \ hsys.c hunk.c iniglb.c init.c int.c lckdir.c lineno.c llocnm.c \ local.c locnm.c logfil.c maxuxq.c mrgblk.c paramc.c port.c \ prtsub.c pubdir.c rdlocs.c rdperm.c reliab.c remunk.c runuxq.c \ sinfo.c snams.c split.c spool.c stafil.c strip.c syssub.c \ tcalou.c tdial.c tdialc.c tdnams.c tgcmp.c thread.c time.c \ tinit.c tlocnm.c tport.c tportc.c tsinfo.c tsnams.c tsys.c \ tval.c ugtlin.c unk.c val.c vinit.c vport.c vsinfo.c vsnams.c \ vsys.c alloc.h syshdr.h uucnfi.h AM_CFLAGS = -I.. -I$(srcdir)/.. $(WARN_CFLAGS) -DNEWCONFIGLIB=\"$(NEWCONFIGDIR)\" -DOLDCONFIGLIB=\"$(OLDCONFIGDIR)\" subdir = uuconf mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = LIBRARIES = $(noinst_LIBRARIES) libuuconf_a_AR = $(AR) cru libuuconf_a_LIBADD = am_libuuconf_a_OBJECTS = addblk.$(OBJEXT) addstr.$(OBJEXT) \ allblk.$(OBJEXT) alloc.$(OBJEXT) base.$(OBJEXT) bool.$(OBJEXT) \ callin.$(OBJEXT) calout.$(OBJEXT) chatc.$(OBJEXT) \ cmdarg.$(OBJEXT) cmdfil.$(OBJEXT) cmdlin.$(OBJEXT) \ cnfnms.$(OBJEXT) debfil.$(OBJEXT) deblev.$(OBJEXT) \ diacod.$(OBJEXT) dial.$(OBJEXT) diasub.$(OBJEXT) \ dnams.$(OBJEXT) errno.$(OBJEXT) errstr.$(OBJEXT) \ filnam.$(OBJEXT) freblk.$(OBJEXT) fredia.$(OBJEXT) \ free.$(OBJEXT) freprt.$(OBJEXT) fresys.$(OBJEXT) \ grdcmp.$(OBJEXT) hdial.$(OBJEXT) hdnams.$(OBJEXT) \ hinit.$(OBJEXT) hlocnm.$(OBJEXT) hport.$(OBJEXT) \ hrmunk.$(OBJEXT) hsinfo.$(OBJEXT) hsnams.$(OBJEXT) \ hsys.$(OBJEXT) hunk.$(OBJEXT) iniglb.$(OBJEXT) init.$(OBJEXT) \ int.$(OBJEXT) lckdir.$(OBJEXT) lineno.$(OBJEXT) \ llocnm.$(OBJEXT) local.$(OBJEXT) locnm.$(OBJEXT) \ logfil.$(OBJEXT) maxuxq.$(OBJEXT) mrgblk.$(OBJEXT) \ paramc.$(OBJEXT) port.$(OBJEXT) prtsub.$(OBJEXT) \ pubdir.$(OBJEXT) rdlocs.$(OBJEXT) rdperm.$(OBJEXT) \ reliab.$(OBJEXT) remunk.$(OBJEXT) runuxq.$(OBJEXT) \ sinfo.$(OBJEXT) snams.$(OBJEXT) split.$(OBJEXT) spool.$(OBJEXT) \ stafil.$(OBJEXT) strip.$(OBJEXT) syssub.$(OBJEXT) \ tcalou.$(OBJEXT) tdial.$(OBJEXT) tdialc.$(OBJEXT) \ tdnams.$(OBJEXT) tgcmp.$(OBJEXT) thread.$(OBJEXT) \ time.$(OBJEXT) tinit.$(OBJEXT) tlocnm.$(OBJEXT) tport.$(OBJEXT) \ tportc.$(OBJEXT) tsinfo.$(OBJEXT) tsnams.$(OBJEXT) \ tsys.$(OBJEXT) tval.$(OBJEXT) ugtlin.$(OBJEXT) unk.$(OBJEXT) \ val.$(OBJEXT) vinit.$(OBJEXT) vport.$(OBJEXT) vsinfo.$(OBJEXT) \ vsnams.$(OBJEXT) vsys.$(OBJEXT) libuuconf_a_OBJECTS = $(am_libuuconf_a_OBJECTS) DEFS = @DEFS@ DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ depcomp = $(SHELL) $(top_srcdir)/depcomp @AMDEP_TRUE@DEP_FILES = $(DEPDIR)/addblk.Po $(DEPDIR)/addstr.Po \ @AMDEP_TRUE@ $(DEPDIR)/allblk.Po $(DEPDIR)/alloc.Po \ @AMDEP_TRUE@ $(DEPDIR)/base.Po $(DEPDIR)/bool.Po \ @AMDEP_TRUE@ $(DEPDIR)/callin.Po $(DEPDIR)/calout.Po \ @AMDEP_TRUE@ $(DEPDIR)/chatc.Po $(DEPDIR)/cmdarg.Po \ @AMDEP_TRUE@ $(DEPDIR)/cmdfil.Po $(DEPDIR)/cmdlin.Po \ @AMDEP_TRUE@ $(DEPDIR)/cnfnms.Po $(DEPDIR)/debfil.Po \ @AMDEP_TRUE@ $(DEPDIR)/deblev.Po $(DEPDIR)/diacod.Po \ @AMDEP_TRUE@ $(DEPDIR)/dial.Po $(DEPDIR)/diasub.Po \ @AMDEP_TRUE@ $(DEPDIR)/dnams.Po $(DEPDIR)/errno.Po \ @AMDEP_TRUE@ $(DEPDIR)/errstr.Po $(DEPDIR)/filnam.Po \ @AMDEP_TRUE@ $(DEPDIR)/freblk.Po $(DEPDIR)/fredia.Po \ @AMDEP_TRUE@ $(DEPDIR)/free.Po $(DEPDIR)/freprt.Po \ @AMDEP_TRUE@ $(DEPDIR)/fresys.Po $(DEPDIR)/grdcmp.Po \ @AMDEP_TRUE@ $(DEPDIR)/hdial.Po $(DEPDIR)/hdnams.Po \ @AMDEP_TRUE@ $(DEPDIR)/hinit.Po $(DEPDIR)/hlocnm.Po \ @AMDEP_TRUE@ $(DEPDIR)/hport.Po $(DEPDIR)/hrmunk.Po \ @AMDEP_TRUE@ $(DEPDIR)/hsinfo.Po $(DEPDIR)/hsnams.Po \ @AMDEP_TRUE@ $(DEPDIR)/hsys.Po $(DEPDIR)/hunk.Po \ @AMDEP_TRUE@ $(DEPDIR)/iniglb.Po $(DEPDIR)/init.Po \ @AMDEP_TRUE@ $(DEPDIR)/int.Po $(DEPDIR)/lckdir.Po \ @AMDEP_TRUE@ $(DEPDIR)/lineno.Po $(DEPDIR)/llocnm.Po \ @AMDEP_TRUE@ $(DEPDIR)/local.Po $(DEPDIR)/locnm.Po \ @AMDEP_TRUE@ $(DEPDIR)/logfil.Po $(DEPDIR)/maxuxq.Po \ @AMDEP_TRUE@ $(DEPDIR)/mrgblk.Po $(DEPDIR)/paramc.Po \ @AMDEP_TRUE@ $(DEPDIR)/port.Po $(DEPDIR)/prtsub.Po \ @AMDEP_TRUE@ $(DEPDIR)/pubdir.Po $(DEPDIR)/rdlocs.Po \ @AMDEP_TRUE@ $(DEPDIR)/rdperm.Po $(DEPDIR)/reliab.Po \ @AMDEP_TRUE@ $(DEPDIR)/remunk.Po $(DEPDIR)/runuxq.Po \ @AMDEP_TRUE@ $(DEPDIR)/sinfo.Po $(DEPDIR)/snams.Po \ @AMDEP_TRUE@ $(DEPDIR)/split.Po $(DEPDIR)/spool.Po \ @AMDEP_TRUE@ $(DEPDIR)/stafil.Po $(DEPDIR)/strip.Po \ @AMDEP_TRUE@ $(DEPDIR)/syssub.Po $(DEPDIR)/tcalou.Po \ @AMDEP_TRUE@ $(DEPDIR)/tdial.Po $(DEPDIR)/tdialc.Po \ @AMDEP_TRUE@ $(DEPDIR)/tdnams.Po $(DEPDIR)/tgcmp.Po \ @AMDEP_TRUE@ $(DEPDIR)/thread.Po $(DEPDIR)/time.Po \ @AMDEP_TRUE@ $(DEPDIR)/tinit.Po $(DEPDIR)/tlocnm.Po \ @AMDEP_TRUE@ $(DEPDIR)/tport.Po $(DEPDIR)/tportc.Po \ @AMDEP_TRUE@ $(DEPDIR)/tsinfo.Po $(DEPDIR)/tsnams.Po \ @AMDEP_TRUE@ $(DEPDIR)/tsys.Po $(DEPDIR)/tval.Po \ @AMDEP_TRUE@ $(DEPDIR)/ugtlin.Po $(DEPDIR)/unk.Po \ @AMDEP_TRUE@ $(DEPDIR)/val.Po $(DEPDIR)/vinit.Po \ @AMDEP_TRUE@ $(DEPDIR)/vport.Po $(DEPDIR)/vsinfo.Po \ @AMDEP_TRUE@ $(DEPDIR)/vsnams.Po $(DEPDIR)/vsys.Po COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ CFLAGS = @CFLAGS@ DIST_SOURCES = $(libuuconf_a_SOURCES) DIST_COMMON = README COPYING.LIB Makefile.am Makefile.in SOURCES = $(libuuconf_a_SOURCES) all: all-am .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) cd $(top_srcdir) && \ $(AUTOMAKE) --gnu uuconf/Makefile Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status cd $(top_builddir) && \ CONFIG_HEADERS= CONFIG_LINKS= \ CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status AR = ar clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libuuconf.a: $(libuuconf_a_OBJECTS) $(libuuconf_a_DEPENDENCIES) -rm -f libuuconf.a $(libuuconf_a_AR) libuuconf.a $(libuuconf_a_OBJECTS) $(libuuconf_a_LIBADD) $(RANLIB) libuuconf.a mostlyclean-compile: -rm -f *.$(OBJEXT) core *.core distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/addblk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/addstr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/allblk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/alloc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/base.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/bool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/callin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/calout.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/chatc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/cmdarg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/cmdfil.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/cmdlin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/cnfnms.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/debfil.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/deblev.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/diacod.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/dial.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/diasub.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/dnams.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/errno.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/errstr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/filnam.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/freblk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/fredia.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/free.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/freprt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/fresys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/grdcmp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hdial.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hdnams.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hinit.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hlocnm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hport.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hrmunk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hsinfo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hsnams.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hsys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/hunk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/iniglb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/init.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/int.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/lckdir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/lineno.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/llocnm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/local.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/locnm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/logfil.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/maxuxq.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/mrgblk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/paramc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/port.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/prtsub.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/pubdir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/rdlocs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/rdperm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/reliab.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/remunk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/runuxq.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/sinfo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/snams.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/split.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/spool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/stafil.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strip.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/syssub.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tcalou.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tdial.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tdialc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tdnams.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tgcmp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/thread.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/time.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tinit.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tlocnm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tport.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tportc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tsinfo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tsnams.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tsys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tval.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ugtlin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/unk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/val.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/vinit.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/vport.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/vsinfo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/vsnams.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/vsys.Po@am__quote@ distclean-depend: -rm -rf $(DEPDIR) .c.o: @AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< .c.obj: @AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(COMPILE) -c `cygpath -w $<` CCDEPMODE = @CCDEPMODE@ uninstall-info-am: tags: TAGS ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(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 $(LISP) TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(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 "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) GTAGS: here=`CDPATH=: && cd $(top_builddir) && pwd` \ && cd $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) $$here distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) top_distdir = .. distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) distdir: $(DISTFILES) @for file in $(DISTFILES); do \ if test -f $$file; then d=.; else d=$(srcdir); fi; \ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ if test "$$dir" != "$$file" && test "$$dir" != "."; then \ $(mkinstalldirs) "$(distdir)/$$dir"; \ fi; \ if test -d $$d/$$file; then \ cp -pR $$d/$$file $(distdir) \ || 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 $(LIBRARIES) installdirs: 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)" \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* 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-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am distclean-am: clean-am distclean-compile distclean-depend \ distclean-generic distclean-tags dvi: dvi-am dvi-am: info: info-am info-am: install-data-am: install-exec-am: install-info: install-info-am install-man: installcheck-am: maintainer-clean: maintainer-clean-am maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic uninstall-am: uninstall-info-am .PHONY: GTAGS all all-am check check-am clean clean-generic \ clean-noinstLIBRARIES distclean distclean-compile \ distclean-depend distclean-generic distclean-tags distdir dvi \ dvi-am info info-am install install-am install-data \ install-data-am install-exec install-exec-am install-info \ install-info-am install-man install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic tags uninstall uninstall-am \ uninstall-info-am # 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: uucp-1.07/uuconf/addblk.c0000664000076400007640000000321207665321761011007 /* addblk.c Add an malloc block to a memory block. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_addblk_rcsid[] = "$Id: addblk.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif #include "alloc.h" /* Add a memory buffer allocated by malloc to a memory block. This is used by the uuconf_cmd functions so that they don't have to constantly copy data into memory. Returns 0 on success, non 0 on failure. */ int uuconf_add_block (pblock, padd) pointer pblock; pointer padd; { struct sblock *q = (struct sblock *) pblock; struct sadded *qnew; qnew = (struct sadded *) uuconf_malloc (pblock, sizeof (struct sadded)); if (qnew == NULL) return 1; qnew->qnext = q->qadded; qnew->padded = padd; q->qadded = qnew; return 0; } uucp-1.07/uuconf/addstr.c0000664000076400007640000000750407665321761011057 /* addstr.c Add a string to a list of strings. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_addstr_rcsid[] = "$Id: addstr.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif #include /* When setting system information, we need to be able to distinguish between a value that is not set and a value that has been set to NULL. We do this by initializing the value to point to the variable _uuconf_unset, and then correcting it in the function _uuconf_isystem_basic_default. This variable is declared in this file because some linkers will apparently not pull in an object file which merely declarates a variable. This functions happens to be pulled in by almost everything. */ char *_uuconf_unset; /* Add a string to a list of strings. The list is maintained as an array of elements ending in NULL. The total number of available slots is always a multiple of CSLOTS, so by counting the current number of elements we can tell whether a new slot is needed. If the fcopy argument is TRUE, the new string is duplicated into memory. If the fcheck argument is TRUE, this does not add a string that is already in the list. The pblock argument may be used to do the allocations within a memory block. This returns a standard uuconf error code. */ #define CSLOTS (8) int _uuconf_iadd_string (qglobal, zadd, fcopy, fcheck, ppzstrings, pblock) struct sglobal *qglobal; char *zadd; boolean fcopy; boolean fcheck; char ***ppzstrings; pointer pblock; { char **pz; size_t c; if (fcheck && *ppzstrings != NULL) { for (pz = *ppzstrings; *pz != NULL; pz++) if (strcmp (zadd, *pz) == 0) return UUCONF_SUCCESS; } if (fcopy) { size_t clen; char *znew; clen = strlen (zadd) + 1; znew = (char *) uuconf_malloc (pblock, clen); if (znew == NULL) { if (qglobal != NULL) qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) znew, (pointer) zadd, clen); zadd = znew; } pz = *ppzstrings; if (pz == NULL || pz == (char **) &_uuconf_unset) { pz = (char **) uuconf_malloc (pblock, CSLOTS * sizeof (char *)); if (pz == NULL) { if (qglobal != NULL) qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } *ppzstrings = pz; } else { c = 0; while (*pz != NULL) { ++pz; ++c; } if ((c + 1) % CSLOTS == 0) { char **pznew; pznew = (char **) uuconf_malloc (pblock, ((c + 1 + CSLOTS) * sizeof (char *))); if (pznew == NULL) { if (qglobal != NULL) qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) pznew, (pointer) *ppzstrings, c * sizeof (char *)); uuconf_free (pblock, *ppzstrings); *ppzstrings = pznew; pz = pznew + c; } } pz[0] = zadd; pz[1] = NULL; return UUCONF_SUCCESS; } uucp-1.07/uuconf/allblk.c0000664000076400007640000000276707665321761011045 /* allblk.c Allocate a memory block. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_allblk_rcsid[] = "$Id: allblk.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif #include "alloc.h" /* Allocate a new memory block. If this fails, uuconf_errno will be set, and the calling routine may return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO. */ pointer uuconf_malloc_block () { struct sblock *qret; qret = (struct sblock *) malloc (sizeof (struct sblock)); if (qret == NULL) return NULL; qret->qnext = NULL; qret->ifree = 0; qret->plast = NULL; qret->qadded = NULL; return (pointer) qret; } uucp-1.07/uuconf/alloc.c0000664000076400007640000000427107665321761010666 /* alloc.c Allocate within a memory block. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_alloc_rcsid[] = "$Id: alloc.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif #include "alloc.h" /* Allocate some memory out of a memory block. If the memory block is NULL, this just calls malloc; this is convenient for a number of routines. If this fails, uuconf_errno will be set, and the calling routine may return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO. */ pointer uuconf_malloc (pblock, c) pointer pblock; size_t c; { struct sblock *q = (struct sblock *) pblock; pointer pret; if (c == 0) return NULL; if (q == NULL) return malloc (c); /* Make sure that c is aligned to a double boundary. */ c = ((c + sizeof (double) - 1) / sizeof (double)) * sizeof (double); while (q->ifree + c > CALLOC_SIZE) { if (q->qnext != NULL) q = q->qnext; else { if (c > CALLOC_SIZE) q->qnext = (struct sblock *) malloc (sizeof (struct sblock) + c - CALLOC_SIZE); else q->qnext = (struct sblock *) malloc (sizeof (struct sblock)); if (q->qnext == NULL) return NULL; q = q->qnext; q->qnext = NULL; q->ifree = 0; q->qadded = NULL; break; } } pret = q->u.ab + q->ifree; q->ifree += c; q->plast = pret; return pret; } uucp-1.07/uuconf/base.c0000664000076400007640000000327507665321761010511 /* base.c Subroutine to turn a cmdtab_offset table into a uuconf_cmdtab table. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_base_rcsid[] = "$Id: base.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* This turns a cmdtab_offset table into a uuconf_cmdtab table. Each offset is adjusted by a base value. */ void _uuconf_ucmdtab_base (qoff, celes, pbase, qset) register const struct cmdtab_offset *qoff; size_t celes; char *pbase; register struct uuconf_cmdtab *qset; { register size_t i; for (i = 0; i < celes; i++, qoff++, qset++) { qset->uuconf_zcmd = qoff->zcmd; qset->uuconf_itype = qoff->itype; if (qoff->ioff == (size_t) -1) qset->uuconf_pvar = NULL; else qset->uuconf_pvar = pbase + qoff->ioff; qset->uuconf_pifn = qoff->pifn; } } uucp-1.07/uuconf/bool.c0000664000076400007640000000346707665321761010535 /* bool.c Parse a boolean string into a variable. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_bool_rcsid[] = "$Id: bool.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif /* Parse a boolean string into a variable. This is called by uuconf_cmd_args, as well as other functions. The parsing is done in a single place to make it easy to change. This should return an error code, including both UUCONF_CMDTABRET_KEEP and UUCONF_CMDTABRET_EXIT if appropriate. */ int _uuconf_iboolean (qglobal, zval, pi) struct sglobal *qglobal ATTRIBUTE_UNUSED; const char *zval; boolean *pi; { switch (*zval) { case 'y': case 'Y': case 't': case 'T': *pi = TRUE; break; case 'n': case 'N': case 'f': case 'F': *pi = FALSE; break; default: return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; } return UUCONF_CMDTABRET_CONTINUE; } uucp-1.07/uuconf/callin.c0000664000076400007640000001171507665321761011037 /* callin.c Check a login name and password against the UUCP password file. Copyright (C) 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_callin_rcsid[] = "$Id: callin.c,v 1.14 2002/03/05 19:10:42 ian Rel $"; #endif #include static int ipcheck P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); struct sinfo { int (*pcmpfn) P((int, pointer, const char *)); pointer pinfo; boolean ffound; boolean fmatched; }; /* Check a login name and password against the UUCP password file. This looks at the Taylor UUCP password file, but will work even if uuconf_taylor_init was not called. It accepts either spaces or colons as field delimiters. */ int uuconf_callin (pglobal, pcmpfn, pinfo) pointer pglobal; int (*pcmpfn) P((int, pointer, const char *)); pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; char **pz; struct uuconf_cmdtab as[1]; struct sinfo s; char *zline; size_t cline; /* If we have no password file names, fill in the default name. */ if (qglobal->qprocess->pzpwdfiles == NULL) { char ab[sizeof NEWCONFIGLIB + sizeof PASSWDFILE - 1]; memcpy ((pointer) ab, (pointer) NEWCONFIGLIB, sizeof NEWCONFIGLIB - 1); memcpy ((pointer) (ab + sizeof NEWCONFIGLIB - 1), (pointer) PASSWDFILE, sizeof PASSWDFILE); iret = _uuconf_iadd_string (qglobal, ab, TRUE, FALSE, &qglobal->qprocess->pzpwdfiles, qglobal->pblock); if (iret != UUCONF_SUCCESS) return iret; } as[0].uuconf_zcmd = NULL; s.pcmpfn = pcmpfn; s.pinfo = pinfo; s.ffound = FALSE; s.fmatched = FALSE; zline = NULL; cline = 0; iret = UUCONF_SUCCESS; for (pz = qglobal->qprocess->pzpwdfiles; *pz != NULL; pz++) { FILE *e; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } qglobal->ilineno = 0; iret = UUCONF_SUCCESS; while (getline (&zline, &cline, e) > 0) { char *z0, *z1; ++qglobal->ilineno; /* We have a few hacks to make Unix style passwd files work. 1) We turn the first two colon characters into spaces. 2) If the colon characters are adjacent, we assume there is no password, and we skip the entry. 3) If the password between colon characters contains a space, we assume that it has been disabled, and we skip the entry. */ z0 = strchr (zline, ':'); if (z0 != NULL) { *z0 = ' '; z1 = strchr (z0, ':'); if (z1 != NULL) { if (z1 - z0 == 1) continue; *z1 = '\0'; if (strchr (z0 + 1, ' ') != NULL) continue; } } iret = uuconf_cmd_line (pglobal, zline, as, (pointer) &s, ipcheck, 0, (pointer) NULL); if ((iret & UUCONF_CMDTABRET_EXIT) != 0) { iret &=~ UUCONF_CMDTABRET_EXIT; if (iret != UUCONF_SUCCESS) iret |= UUCONF_ERROR_LINENO; break; } iret = UUCONF_SUCCESS; } (void) fclose (e); if (iret != UUCONF_SUCCESS || s.ffound) break; } if (zline != NULL) free ((pointer) zline); if (iret != UUCONF_SUCCESS) { qglobal->zfilename = *pz; iret |= UUCONF_ERROR_FILENAME; } else if (! s.ffound || ! s.fmatched) iret = UUCONF_NOT_FOUND; return iret; } /* This is called on each line of the file. It checks to see if the login name from the file is the one we are looking for. If it is, it sets ffound, and then sets fmatched according to whether the password matches or not. */ static int ipcheck (pglobal, argc, argv, pvar, pinfo) pointer pglobal ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sinfo *q = (struct sinfo *) pinfo; if (argc != 2) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; if (! (*q->pcmpfn) (0, q->pinfo, argv[0])) return UUCONF_CMDTABRET_CONTINUE; q->ffound = TRUE; q->fmatched = (*q->pcmpfn) (1, q->pinfo, argv[1]) != 0; return UUCONF_CMDTABRET_EXIT; } uucp-1.07/uuconf/calout.c0000664000076400007640000000470607665321761011066 /* calout.c Find callout login name and password for a system. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_calout_rcsid[] = "$Id: calout.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Find callout login name and password for a system. */ /*ARGSUSED*/ int uuconf_callout (pglobal, qsys, pzlog, pzpass) pointer pglobal; const struct uuconf_system *qsys; char **pzlog; char **pzpass; { #if HAVE_TAYLOR_CONFIG return uuconf_taylor_callout (pglobal, qsys, pzlog, pzpass); #else /* ! HAVE_TAYLOR_CONFIG */ struct sglobal *qglobal = (struct sglobal *) pglobal; *pzlog = NULL; *pzpass = NULL; if (qsys->uuconf_zcall_login == NULL && qsys->uuconf_zcall_password == NULL) return UUCONF_NOT_FOUND; if ((qsys->uuconf_zcall_login != NULL && strcmp (qsys->uuconf_zcall_login, "*") == 0) || (qsys->uuconf_zcall_password != NULL && strcmp (qsys->uuconf_zcall_password, "*") == 0)) return UUCONF_NOT_FOUND; if (qsys->uuconf_zcall_login != NULL) { *pzlog = strdup (qsys->uuconf_zcall_login); if (*pzlog == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } } if (qsys->uuconf_zcall_password != NULL) { *pzpass = strdup (qsys->uuconf_zcall_password); if (*pzpass == NULL) { qglobal->ierrno = errno; if (*pzlog != NULL) { free ((pointer) *pzlog); *pzlog = NULL; } return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } } return UUCONF_SUCCESS; #endif /* ! HAVE_TAYLOR_CONFIG */ } uucp-1.07/uuconf/chatc.c0000664000076400007640000001410107665321761010647 /* chatc.c Subroutines to handle chat script commands. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_chatc_rcsid[] = "$Id: chatc.c,v 1.10 2002/03/05 19:10:42 ian Rel $"; #endif #include #include static int icchat P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int icchat_fail P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int icunknown P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); /* The chat script commands. */ static const struct cmdtab_offset asChat_cmds[] = { { "chat", UUCONF_CMDTABTYPE_FN, offsetof (struct uuconf_chat, uuconf_pzchat), icchat }, { "chat-program", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_chat, uuconf_pzprogram), NULL }, { "chat-timeout", UUCONF_CMDTABTYPE_INT, offsetof (struct uuconf_chat, uuconf_ctimeout), NULL }, { "chat-fail", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct uuconf_chat, uuconf_pzfail), icchat_fail }, { "chat-seven-bit", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_chat, uuconf_fstrip), NULL }, { NULL, 0, 0, NULL } }; #define CCHAT_CMDS (sizeof asChat_cmds / sizeof asChat_cmds[0]) /* Handle a chat script command. The chat script commands are entered as UUCONF_CMDTABTYPE_PREFIX, and the commands are routed to this function. We copy the command table onto the stack and repoint it at qchat in order to make the function reentrant. The return value can include UUCONF_CMDTABRET_KEEP, but should not include UUCONF_CMDTABRET_EXIT. */ int _uuconf_ichat_cmd (qglobal, argc, argv, qchat, pblock) struct sglobal *qglobal; int argc; char **argv; struct uuconf_chat *qchat; pointer pblock; { char *zchat; struct uuconf_cmdtab as[CCHAT_CMDS]; int iret; /* This is only invoked when argv[0] will contain the string "chat"; the specific chat script command comes after that point. */ for (zchat = argv[0]; *zchat != '\0'; zchat++) if ((*zchat == 'c' || *zchat == 'C') && strncasecmp (zchat, "chat", sizeof "chat" - 1) == 0) break; if (*zchat == '\0') return UUCONF_SYNTAX_ERROR; argv[0] = zchat; _uuconf_ucmdtab_base (asChat_cmds, CCHAT_CMDS, (char *) qchat, as); iret = uuconf_cmd_args ((pointer) qglobal, argc, argv, as, pblock, icunknown, 0, pblock); /* If chat-program was specified with no arguments, treat that as no chat-program. This may be used to override an earlier chat-program. There is a space leak here. */ if (qchat->uuconf_pzprogram != NULL && qchat->uuconf_pzprogram[0] == NULL) qchat->uuconf_pzprogram = NULL; return iret &~ UUCONF_CMDTABRET_EXIT; } /* Handle the "chat" command. This breaks up substrings in expect strings, and sticks the arguments into a NULL terminated array. */ static int icchat (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; char ***ppz = (char ***) pvar; pointer pblock = pinfo; int i; *ppz = NULL; for (i = 1; i < argc; i += 2) { char *z, *zdash; int iret; /* Break the expect string into substrings. */ z = argv[i]; zdash = strchr (z, '-'); while (zdash != NULL) { *zdash = '\0'; iret = _uuconf_iadd_string (qglobal, z, TRUE, FALSE, ppz, pblock); if (iret != UUCONF_SUCCESS) return iret; *zdash = '-'; z = zdash; zdash = strchr (z + 1, '-'); } iret = _uuconf_iadd_string (qglobal, z, FALSE, FALSE, ppz, pblock); if (iret != UUCONF_SUCCESS) return iret; /* Add the send string without breaking it up. If it starts with a dash we must replace it with an escape sequence, to prevent it from being interpreted as a subsend. */ if (i + 1 < argc) { if (argv[i + 1][0] != '-') iret = _uuconf_iadd_string (qglobal, argv[i + 1], FALSE, FALSE, ppz, pblock); else { size_t clen; clen = strlen (argv[i + 1]); z = uuconf_malloc (pblock, clen + 2); if (z == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } z[0] = '\\'; memcpy ((pointer) (z + 1), (pointer) argv[i + 1], clen + 1); iret = _uuconf_iadd_string (qglobal, z, FALSE, FALSE, ppz, pblock); } if (iret != UUCONF_SUCCESS) return iret; } } return UUCONF_CMDTABRET_KEEP; } /* Add a new chat failure string. */ /*ARGSUSED*/ static int icchat_fail (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; char ***ppz = (char ***) pvar; pointer pblock = pinfo; return _uuconf_iadd_string (qglobal, argv[1], TRUE, FALSE, ppz, pblock); } /* Return a syntax error for an unknown command. */ /*ARGSUSED*/ static int icunknown (pglobal, argc, argv, pvar, pinfo) pointer pglobal ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { return UUCONF_SYNTAX_ERROR; } uucp-1.07/uuconf/cmdarg.c0000664000076400007640000001134007665321761011024 /* cmdarg.c Look up a command with arguments in a command table. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_cmdarg_rcsid[] = "$Id: cmdarg.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif #include #undef strcmp #if HAVE_STRCASECMP #undef strcasecmp #endif extern int strcmp (), strcasecmp (); /* Look up a command with arguments in a table and execute it. */ int uuconf_cmd_args (pglobal, cargs, pzargs, qtab, pinfo, pfiunknown, iflags, pblock) pointer pglobal; int cargs; char **pzargs; const struct uuconf_cmdtab *qtab; pointer pinfo; int (*pfiunknown) P((pointer, int, char **, pointer, pointer)); int iflags; pointer pblock; { struct sglobal *qglobal = (struct sglobal *) pglobal; int bfirstu, bfirstl; int (*pficmp) P((const char *, const char *)); register const struct uuconf_cmdtab *q; int itype; int callowed; bfirstu = bfirstl = pzargs[0][0]; if ((iflags & UUCONF_CMDTABFLAG_CASE) != 0) pficmp = strcmp; else { if (islower (bfirstu)) bfirstu = toupper (bfirstu); if (isupper (bfirstl)) bfirstl = tolower (bfirstl); pficmp = strcasecmp; } itype = 0; for (q = qtab; q->uuconf_zcmd != NULL; q++) { int bfirst; bfirst = q->uuconf_zcmd[0]; if (bfirst != bfirstu && bfirst != bfirstl) continue; itype = UUCONF_TTYPE_CMDTABTYPE (q->uuconf_itype); if (itype != UUCONF_CMDTABTYPE_PREFIX) { if ((*pficmp) (q->uuconf_zcmd, pzargs[0]) == 0) break; } else { size_t clen; clen = strlen (q->uuconf_zcmd); if ((iflags & UUCONF_CMDTABFLAG_CASE) != 0) { if (strncmp (q->uuconf_zcmd, pzargs[0], clen) == 0) break; } else { if (strncasecmp (q->uuconf_zcmd, pzargs[0], clen) == 0) break; } } } if (q->uuconf_zcmd == NULL) { if (pfiunknown == NULL) return UUCONF_CMDTABRET_CONTINUE; return (*pfiunknown) (pglobal, cargs, pzargs, (pointer) NULL, pinfo); } callowed = UUCONF_CARGS_CMDTABTYPE (q->uuconf_itype); if (callowed != 0 && callowed != cargs) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; switch (itype) { case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_STRING): if (cargs == 1) *(char **) q->uuconf_pvar = (char *) ""; else if (cargs == 2) *(char **) q->uuconf_pvar = pzargs[1]; else return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; return UUCONF_CMDTABRET_KEEP; case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_INT): return _uuconf_iint (qglobal, pzargs[1], q->uuconf_pvar, TRUE); case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_LONG): return _uuconf_iint (qglobal, pzargs[1], q->uuconf_pvar, FALSE); case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_BOOLEAN): return _uuconf_iboolean (qglobal, pzargs[1], (int *) q->uuconf_pvar); case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_FULLSTRING): if (cargs == 1) { char ***ppz = (char ***) q->uuconf_pvar; int iret; *ppz = NULL; iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, ppz, pblock); if (iret != UUCONF_SUCCESS) return iret | UUCONF_CMDTABRET_EXIT; return UUCONF_CMDTABRET_CONTINUE; } else { char ***ppz = (char ***) q->uuconf_pvar; int i; *ppz = NULL; for (i = 1; i < cargs; i++) { int iret; iret = _uuconf_iadd_string (qglobal, pzargs[i], FALSE, FALSE, ppz, pblock); if (iret != UUCONF_SUCCESS) { *ppz = NULL; return iret | UUCONF_CMDTABRET_EXIT; } } return UUCONF_CMDTABRET_KEEP; } case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_FN): case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_PREFIX): return (*q->uuconf_pifn) (pglobal, cargs, pzargs, q->uuconf_pvar, pinfo); default: return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; } /*NOTREACHED*/ } uucp-1.07/uuconf/cmdfil.c0000664000076400007640000000502607665321761011031 /* cmdfil.c Read and parse commands from a file. Copyright (C) 1992, 1993 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_cmdfil_rcsid[] = "$Id: cmdfil.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Read and parse commands from a file, updating uuconf_lineno as appropriate. */ int uuconf_cmd_file (pglobal, e, qtab, pinfo, pfiunknown, iflags, pblock) pointer pglobal; FILE *e; const struct uuconf_cmdtab *qtab; pointer pinfo; int (*pfiunknown) P((pointer, int, char **, pointer, pointer)); int iflags; pointer pblock; { struct sglobal *qglobal = (struct sglobal *) pglobal; boolean fcont; char *zline; size_t cline; int iret; fcont = (iflags & UUCONF_CMDTABFLAG_BACKSLASH) != 0; zline = NULL; cline = 0; iret = UUCONF_SUCCESS; qglobal->ilineno = 0; while ((fcont ? _uuconf_getline (qglobal, &zline, &cline, e) : getline (&zline, &cline, e)) > 0) { ++qglobal->ilineno; iret = uuconf_cmd_line (pglobal, zline, qtab, pinfo, pfiunknown, iflags, pblock); if ((iret & UUCONF_CMDTABRET_KEEP) != 0) { iret &=~ UUCONF_CMDTABRET_KEEP; if (pblock != NULL) { if (uuconf_add_block (pblock, zline) != 0) { qglobal->ierrno = errno; iret = (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_ERROR_LINENO); break; } } zline = NULL; cline = 0; } if ((iret & UUCONF_CMDTABRET_EXIT) != 0) { iret &=~ UUCONF_CMDTABRET_EXIT; if (iret != UUCONF_SUCCESS) iret |= UUCONF_ERROR_LINENO; break; } iret = UUCONF_SUCCESS; } if (zline != NULL) free ((pointer) zline); return iret; } uucp-1.07/uuconf/cmdlin.c0000664000076400007640000000672107665321761011044 /* cmdlin.c Parse a command line. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_cmdlin_rcsid[] = "$Id: cmdlin.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif #include #include /* Parse a command line into fields and process it via a command table. The command table functions may keep the memory allocated for the line, but they may not keep the memory allocated for the argv list. This function strips # comments. */ #define CSTACK (16) int uuconf_cmd_line (pglobal, zline, qtab, pinfo, pfiunknown, iflags, pblock) pointer pglobal; char *zline; const struct uuconf_cmdtab *qtab; pointer pinfo; int (*pfiunknown) P((pointer, int, char **, pointer, pointer)); int iflags; pointer pblock; { struct sglobal *qglobal = (struct sglobal *) pglobal; char *z; int cargs; char *azargs[CSTACK]; char **pzargs; int iret; if ((iflags & UUCONF_CMDTABFLAG_NOCOMMENTS) == 0) { /* Any # not preceeded by a backslash starts a comment. */ z = zline; while ((z = strchr (z, '#')) != NULL) { if (z == zline || *(z - 1) != '\\') { *z = '\0'; break; } /* Remove the backslash. */ while ((*(z - 1) = *z) != '\0') ++z; } } /* Parse the first CSTACK arguments by hand to avoid malloc. */ z = zline; cargs = 0; pzargs = azargs; while (TRUE) { while (*z != '\0' && isspace (BUCHAR (*z))) ++z; if (*z == '\0') break; if (cargs >= CSTACK) { char **pzsplit; size_t csplit; int cmore; pzsplit = NULL; csplit = 0; cmore = _uuconf_istrsplit (z, '\0', &pzsplit, &csplit); if (cmore < 0) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } pzargs = (char **) malloc ((cmore + CSTACK) * sizeof (char *)); if (pzargs == NULL) { qglobal->ierrno = errno; free ((pointer) pzsplit); return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) pzargs, (pointer) azargs, CSTACK * sizeof (char *)); memcpy ((pointer) (pzargs + CSTACK), (pointer) pzsplit, cmore * sizeof (char *)); cargs = cmore + CSTACK; free ((pointer) pzsplit); break; } azargs[cargs] = z; ++cargs; while (*z != '\0' && ! isspace (BUCHAR (*z))) z++; if (*z == '\0') break; *z++ = '\0'; } if (cargs <= 0) return UUCONF_CMDTABRET_CONTINUE; iret = uuconf_cmd_args (pglobal, cargs, pzargs, qtab, pinfo, pfiunknown, iflags, pblock); if (pzargs != azargs) free ((pointer) pzargs); return iret; } uucp-1.07/uuconf/cnfnms.c0000664000076400007640000000612407665321761011057 /* cnfnms.c Return configuration file names. Copyright (C) 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_cnfnms_rcsid[] = "$Id: cnfnms.c,v 1.2 2002/03/05 19:10:42 ian Rel $"; #endif #include int uuconf_config_files (pglobal, qnames) pointer pglobal; struct uuconf_config_file_names* qnames; { struct sglobal *qglobal = (struct sglobal *) pglobal; qnames->uuconf_ztaylor_config = qglobal->qprocess->zconfigfile; qnames->uuconf_pztaylor_sys = (const char * const *) qglobal->qprocess->pzsysfiles; qnames->uuconf_pztaylor_port = (const char * const *) qglobal->qprocess->pzportfiles; qnames->uuconf_pztaylor_dial = (const char * const *) qglobal->qprocess->pzdialfiles; qnames->uuconf_pzdialcode = (const char * const *) qglobal->qprocess->pzdialcodefiles; qnames->uuconf_pztaylor_pwd = (const char * const *) qglobal->qprocess->pzpwdfiles; qnames->uuconf_pztaylor_call = (const char * const *) qglobal->qprocess->pzcallfiles; qnames->uuconf_zv2_systems = qglobal->qprocess->zv2systems; qnames->uuconf_zv2_device = qglobal->qprocess->zv2devices; qnames->uuconf_zv2_userfile = qglobal->qprocess->zv2userfile; qnames->uuconf_zv2_cmds = qglobal->qprocess->zv2cmds; qnames->uuconf_pzhdb_systems = (const char * const *) qglobal->qprocess->pzhdb_systems; qnames->uuconf_pzhdb_devices = (const char * const *) qglobal->qprocess->pzhdb_devices; qnames->uuconf_pzhdb_dialers = (const char * const *) qglobal->qprocess->pzhdb_dialers; qnames->uuconf_zhdb_permissions = NULL; #if HAVE_HDB_CONFIG if (qglobal->qprocess->fhdb) { /* FIXME: There is a memory leak here. */ qnames->uuconf_zhdb_permissions = (char *) uuconf_malloc(qglobal->pblock, (sizeof OLDCONFIGLIB + sizeof HDB_PERMISSIONS - 1)); if (qnames->uuconf_zhdb_permissions == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy((pointer) qnames->uuconf_zhdb_permissions, (pointer) OLDCONFIGLIB, sizeof OLDCONFIGLIB - 1); memcpy((pointer) (qnames->uuconf_zhdb_permissions + sizeof OLDCONFIGLIB - 1), (pointer) HDB_PERMISSIONS, sizeof HDB_PERMISSIONS); } #endif return UUCONF_SUCCESS; } uucp-1.07/uuconf/debfil.c0000664000076400007640000000252107665321761011015 /* debfil.c Get the name of the UUCP debugging file. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_debfil_rcsid[] = "$Id: debfil.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif /* Get the name of the UUCP debugging file. */ int uuconf_debugfile (pglobal, pzdebug) pointer pglobal; const char **pzdebug; { struct sglobal *qglobal = (struct sglobal *) pglobal; *pzdebug = qglobal->qprocess->zdebugfile; return UUCONF_SUCCESS; } uucp-1.07/uuconf/deblev.c0000664000076400007640000000247007665321761011034 /* deblev.c Get the UUCP debugging level. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_deblev_rcsid[] = "$Id: deblev.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* Get the UUCP debugging level. */ int uuconf_debuglevel (pglobal, pzdebug) pointer pglobal; const char **pzdebug; { struct sglobal *qglobal = (struct sglobal *) pglobal; *pzdebug = qglobal->qprocess->zdebug; return UUCONF_SUCCESS; } uucp-1.07/uuconf/diacod.c0000664000076400007640000000613707665321761011022 /* diacod.c Translate a dialcode. Copyright (C) 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_diacod_rcsid[] = "$Id: diacod.c,v 1.12 2002/03/05 19:10:42 ian Rel $"; #endif #include static int idcode P((pointer pglobal, int argc, char **argv, pointer pinfo, pointer pvar)); /* Get the name of the UUCP log file. */ int uuconf_dialcode (pglobal, zdial, pznum) pointer pglobal; const char *zdial; char **pznum; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_cmdtab as[2]; char **pz; int iret; as[0].uuconf_zcmd = zdial; as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 0; as[0].uuconf_pvar = (pointer) pznum; as[0].uuconf_pifn = idcode; as[1].uuconf_zcmd = NULL; *pznum = NULL; iret = UUCONF_SUCCESS; for (pz = qglobal->qprocess->pzdialcodefiles; *pz != NULL; pz++) { FILE *e; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } iret = uuconf_cmd_file (pglobal, e, as, (pointer) NULL, (uuconf_cmdtabfn) NULL, 0, (pointer) NULL); (void) fclose (e); if (iret != UUCONF_SUCCESS || *pznum != NULL) break; } if (iret != UUCONF_SUCCESS) { qglobal->zfilename = *pz; iret |= UUCONF_ERROR_FILENAME; } else if (*pznum == NULL) iret = UUCONF_NOT_FOUND; return iret; } /* This is called if the dialcode is found. It copies the number into the heap and gets out of reading the file. */ /*ARGSUSED*/ static int idcode (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; char **pznum = (char **) pvar; if (argc == 1) { *pznum = malloc (1); if (*pznum != NULL) **pznum = '\0'; } else if (argc == 2) *pznum = strdup (argv[1]); else return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; if (*pznum == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } return UUCONF_CMDTABRET_EXIT; } uucp-1.07/uuconf/dial.c0000664000076400007640000000322407665321761010502 /* dial.c Find a dialer. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_dial_rcsid[] = "$Id: dial.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif /* Find a dialer by name. */ int uuconf_dialer_info (pglobal, zdialer, qdialer) pointer pglobal; const char *zdialer; struct uuconf_dialer *qdialer; { #if HAVE_HDB_CONFIG struct sglobal *qglobal = (struct sglobal *) pglobal; #endif int iret; #if HAVE_TAYLOR_CONFIG iret = uuconf_taylor_dialer_info (pglobal, zdialer, qdialer); if (iret != UUCONF_NOT_FOUND) return iret; #endif #if HAVE_HDB_CONFIG if (qglobal->qprocess->fhdb) { iret = uuconf_hdb_dialer_info (pglobal, zdialer, qdialer); if (iret != UUCONF_NOT_FOUND) return iret; } #endif return UUCONF_NOT_FOUND; } uucp-1.07/uuconf/diasub.c0000664000076400007640000000412707665321761011043 /* diasub.c Dialer information subroutines. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_diasub_rcsid[] = "$Id: diasub.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif /* Clear the information in a dialer. */ #define INIT_CHAT(q) \ ((q)->uuconf_pzchat = NULL, \ (q)->uuconf_pzprogram = NULL, \ (q)->uuconf_ctimeout = 60, \ (q)->uuconf_pzfail = NULL, \ (q)->uuconf_fstrip = TRUE) void _uuconf_uclear_dialer (qdialer) struct uuconf_dialer *qdialer; { qdialer->uuconf_zname = NULL; INIT_CHAT (&qdialer->uuconf_schat); qdialer->uuconf_zdialtone = (char *) ","; qdialer->uuconf_zpause = (char *) ","; qdialer->uuconf_fcarrier = TRUE; qdialer->uuconf_ccarrier_wait = 60; qdialer->uuconf_fdtr_toggle = FALSE; qdialer->uuconf_fdtr_toggle_wait = FALSE; INIT_CHAT (&qdialer->uuconf_scomplete); INIT_CHAT (&qdialer->uuconf_sabort); qdialer->uuconf_qproto_params = NULL; /* Note that we do not set RELIABLE_SPECIFIED; this just sets defaults, so that ``seven-bit true'' does not imply ``reliable false''. */ qdialer->uuconf_ireliable = (UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX); qdialer->uuconf_palloc = NULL; } uucp-1.07/uuconf/dnams.c0000664000076400007640000000500007665321761010665 /* dnams.c Get all known dialer names. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_dnams_rcsid[] = "$Id: dnams.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* Get all known dialer names. */ int uuconf_dialer_names (pglobal, ppzdialers) pointer pglobal; char ***ppzdialers; { struct sglobal *qglobal = (struct sglobal *) pglobal; char **pztaylor; char **pzhdb; int iret; *ppzdialers = NULL; pztaylor = NULL; pzhdb = NULL; #if HAVE_TAYLOR_CONFIG iret = uuconf_taylor_dialer_names (pglobal, &pztaylor); if (iret != UUCONF_SUCCESS) return iret; #endif #if HAVE_HDB_CONFIG if (qglobal->qprocess->fhdb) { iret = uuconf_hdb_dialer_names (pglobal, &pzhdb); if (iret != UUCONF_SUCCESS) return iret; } #endif if (pzhdb == NULL) *ppzdialers = pztaylor; else if (pztaylor == NULL) *ppzdialers = pzhdb; else { char **pz; iret = UUCONF_SUCCESS; for (pz = pztaylor; *pz != NULL; pz++) { iret = _uuconf_iadd_string (qglobal, *pz, FALSE, TRUE, ppzdialers, (pointer) NULL); if (iret != UUCONF_SUCCESS) break; } if (iret == UUCONF_SUCCESS) { for (pz = pzhdb; *pz != NULL; pz++) { iret = _uuconf_iadd_string (qglobal, *pz, FALSE, TRUE, ppzdialers, (pointer) NULL); if (iret != UUCONF_SUCCESS) break; } } if (pztaylor != NULL) free ((pointer) pztaylor); if (pzhdb != NULL) free ((pointer) pzhdb); } if (iret == UUCONF_SUCCESS && *ppzdialers == NULL) iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, ppzdialers, (pointer) NULL); return iret; } uucp-1.07/uuconf/errno.c0000664000076400007640000000246107665321761010720 /* errno.c Return the saved errno value. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_errno_rcsid[] = "$Id: errno.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Return the saved errno value. */ int uuconf_error_errno (pglobal) pointer pglobal; { struct sglobal *qglobal = (struct sglobal *) pglobal; if (qglobal == NULL) return errno; else return qglobal->ierrno; } uucp-1.07/uuconf/errstr.c0000664000076400007640000001247507665321761011122 /* errstr.c Return a string for a uuconf error. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_errstr_rcsid[] = "$Id: errstr.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif static char *zeprint_num P((char *zbuf, size_t cbuf, int ival)); /* Return an error string for a uuconf error. This does not return a uuconf error code, but instead returns the total buffer length. */ int uuconf_error_string (pglobal, ierr, zbuf, cbuf) pointer pglobal; int ierr; char *zbuf; size_t cbuf; { struct sglobal *qglobal = (struct sglobal *) pglobal; const char *zfile; size_t cfile; const char *zlineno; char ablineno[100]; size_t clineno; const char *zmsg; char abmsg[100]; size_t cmsg; const char *zerrno; size_t cerrno; size_t cret; size_t ccopy; /* The format of the message is filename:lineno: message: errno If there is no filename, the trailing colon is not output. If there is no linenumber, the trailing colon is not output. If there is no filename, the linenumber is not output, and neither is the space before message. If there is no errno, the preceeding colon and space are not output. */ /* Get the filename to put in the error message, if any. */ if ((ierr & UUCONF_ERROR_FILENAME) == 0 || qglobal == NULL || qglobal->zfilename == NULL) { zfile = ""; cfile = 0; } else { zfile = qglobal->zfilename; cfile = strlen (zfile) + 1; } /* Get the line number to put in the error message, if any. */ if (cfile == 0 || (ierr & UUCONF_ERROR_LINENO) == 0 || qglobal == NULL || qglobal->ilineno <= 0) { zlineno = ""; clineno = 0; } else { zlineno = zeprint_num (ablineno, sizeof ablineno, qglobal->ilineno); clineno = strlen (zlineno) + 1; } /* Get the main message. */ switch (UUCONF_ERROR_VALUE (ierr)) { case UUCONF_SUCCESS: zmsg = "no error"; break; case UUCONF_NOT_FOUND: zmsg = "not found"; break; case UUCONF_FOPEN_FAILED: zmsg = "fopen"; break; case UUCONF_FSEEK_FAILED: zmsg = "fseek"; break; case UUCONF_MALLOC_FAILED: zmsg = "malloc"; break; case UUCONF_SYNTAX_ERROR: zmsg = "syntax error"; break; default: zmsg = zeprint_num (abmsg, sizeof abmsg, UUCONF_ERROR_VALUE (ierr)); zmsg -= sizeof "error " - 1; memcpy ((pointer) zmsg, (pointer) "error ", sizeof "error " - 1); break; } cmsg = strlen (zmsg); if (cfile > 0) ++cmsg; /* Get the errno string. Note that strerror is not necessarily reentrant. */ if ((ierr & UUCONF_ERROR_ERRNO) == 0 || qglobal == NULL) { zerrno = ""; cerrno = 0; } else { zerrno = strerror (qglobal->ierrno); cerrno = strlen (zerrno) + 2; } cret = cfile + clineno + cmsg + cerrno + 1; if (cbuf == 0) return cret; /* Leave room for the null byte. */ --cbuf; if (cfile > 0) { ccopy = cfile - 1; if (ccopy > cbuf) ccopy = cbuf; memcpy ((pointer) zbuf, (pointer) zfile, ccopy); zbuf += ccopy; cbuf -= ccopy; if (cbuf > 0) { *zbuf++ = ':'; --cbuf; } } if (clineno > 0) { ccopy = clineno - 1; if (ccopy > cbuf) ccopy = cbuf; memcpy ((pointer) zbuf, (pointer) zlineno, ccopy); zbuf += ccopy; cbuf -= ccopy; if (cbuf > 0) { *zbuf++ = ':'; --cbuf; } } if (cbuf > 0 && cfile > 0) { *zbuf++ = ' '; --cbuf; --cmsg; } ccopy = cmsg; if (ccopy > cbuf) ccopy = cbuf; memcpy ((pointer) zbuf, (pointer) zmsg, ccopy); zbuf += ccopy; cbuf -= ccopy; if (cerrno > 0) { if (cbuf > 0) { *zbuf++ = ':'; --cbuf; } if (cbuf > 0) { *zbuf++ = ' '; --cbuf; } ccopy = cerrno - 2; if (ccopy > cbuf) ccopy = cbuf; memcpy ((pointer) zbuf, (pointer) zerrno, ccopy); zbuf += ccopy; cbuf -= ccopy; } *zbuf = '\0'; return cret; } /* Turn a number into a string. This should really call sprintf, but since nothing else in the uuconf library calls any print routine, it's more interesting to not call it here either. */ static char * zeprint_num (ab, c, i) char *ab; size_t c; register int i; { register char *z; z = ab + c; *--z = '\0'; do { *--z = i % 10 + '0'; i /= 10; } while (i != 0); return z; } uucp-1.07/uuconf/filnam.c0000664000076400007640000000245707665321761011046 /* filnam.c Return the saved file name. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_filnam_rcsid[] = "$Id: filnam.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif /* Return the saved file name. */ UUCONF_CONST char * uuconf_error_filename (pglobal) pointer pglobal; { struct sglobal *qglobal = (struct sglobal *) pglobal; if (qglobal == NULL) return ""; else return qglobal->zfilename; } uucp-1.07/uuconf/freblk.c0000664000076400007640000000330407665321761011035 /* freblk.c Free up an entire memory block. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_freblk_rcsid[] = "$Id: freblk.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif #include "alloc.h" /* Free up an entire memory block. */ #if UUCONF_ANSI_C void #endif uuconf_free_block (pblock) pointer pblock; { struct sblock *q = (struct sblock *) pblock; struct sblock *qloop; /* We have to free the added blocks first because the list may link into blocks that are earlier on the list. */ for (qloop = q; qloop != NULL; qloop = qloop->qnext) { struct sadded *qadd; for (qadd = qloop->qadded; qadd != NULL; qadd = qadd->qnext) free (qadd->padded); } while (q != NULL) { struct sblock *qnext; qnext = q->qnext; free ((pointer) q); q = qnext; } } uucp-1.07/uuconf/fredia.c0000664000076400007640000000252307665321761011024 /* fredia.c Free dialer information. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_fredia_rcsid[] = "$Id: fredia.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif /* Free the memory allocated for a dialer. */ #undef uuconf_dialer_free /*ARGSUSED*/ int uuconf_dialer_free (pglobal, qdialer) pointer pglobal ATTRIBUTE_UNUSED; struct uuconf_dialer *qdialer; { uuconf_free_block (qdialer->uuconf_palloc); return UUCONF_SUCCESS; } uucp-1.07/uuconf/free.c0000664000076400007640000000355507665321761010521 /* free.c Free a buffer from within a memory block. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_free_rcsid[] = "$Id: free.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif #include "alloc.h" /* Free memory allocated by uuconf_malloc. If the memory block is NULL, this just calls free; this is convenient for a number of routines. Otherwise, this will only do something if this was the last buffer allocated for one of the memory blocks in the list; in other cases, the memory is lost until the entire memory block is freed. */ #if UUCONF_ANSI_C void #endif uuconf_free (pblock, pbuf) pointer pblock; pointer pbuf; { struct sblock *q = (struct sblock *) pblock; if (pbuf == NULL) return; if (q == NULL) { free (pbuf); return; } for (; q != NULL; q = q->qnext) { if (q->plast == pbuf) { q->ifree = (char *) pbuf - q->u.ab; /* We could reset q->plast here, but it doesn't matter. */ return; } } } uucp-1.07/uuconf/freprt.c0000664000076400007640000000250307665321761011072 /* freprt.c Free port information. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_freprt_rcsid[] = "$Id: freprt.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif /* Free the memory allocated for a port. */ #undef uuconf_port_free /*ARGSUSED*/ int uuconf_port_free (pglobal, qport) pointer pglobal ATTRIBUTE_UNUSED; struct uuconf_port *qport; { uuconf_free_block (qport->uuconf_palloc); return UUCONF_SUCCESS; } uucp-1.07/uuconf/fresys.c0000664000076400007640000000251207665321761011103 /* fresys.c Free system information. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_fresys_rcsid[] = "$Id: fresys.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif /* Free the memory allocated for a system. */ #undef uuconf_system_free /*ARGSUSED*/ int uuconf_system_free (pglobal, qsys) pointer pglobal ATTRIBUTE_UNUSED; struct uuconf_system *qsys; { uuconf_free_block (qsys->uuconf_palloc); return UUCONF_SUCCESS; } uucp-1.07/uuconf/grdcmp.c0000664000076400007640000000365107665321761011051 /* grdcmp.c Compare two grades. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_grdcmp_rcsid[] = "$Id: grdcmp.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Compare two grades, returning < 0 if b1 should be executed before b2, == 0 if they are the same, or > 0 if b1 should be executed after b2. This can not fail, and does not return a standard uuconf error code. This implementation assumes that the upper case letters are in sequence, and that the lower case letters are in sequence. */ int uuconf_grade_cmp (barg1, barg2) int barg1; int barg2; { int b1, b2; /* Make sure the arguments are unsigned. */ b1 = (int) BUCHAR (barg1); b2 = (int) BUCHAR (barg2); if (isdigit (b1)) { if (isdigit (b2)) return b1 - b2; else return -1; } else if (isupper (b1)) { if (isdigit (b2)) return 1; else if (isupper (b2)) return b1 - b2; else return -1; } else { if (! islower (b2)) return 1; else return b1 - b2; } } uucp-1.07/uuconf/hdial.c0000664000076400007640000001033507665321761010653 /* hdial.c Find a dialer in the HDB configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_hdial_rcsid[] = "$Id: hdial.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif #include #include /* Find a dialer in the HDB configuration files by name. */ int uuconf_hdb_dialer_info (pglobal, zname, qdialer) pointer pglobal; const char *zname; struct uuconf_dialer *qdialer; { struct sglobal *qglobal = (struct sglobal *) pglobal; char **pz; char *zline; size_t cline; char **pzsplit; size_t csplit; int iret; zline = NULL; cline = 0; pzsplit = NULL; csplit = 0; iret = UUCONF_NOT_FOUND; for (pz = qglobal->qprocess->pzhdb_dialers; *pz != NULL; pz++) { FILE *e; int cchars; qglobal->ilineno = 0; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) { int ctoks; pointer pblock; ++qglobal->ilineno; --cchars; if (zline[cchars] == '\n') zline[cchars] = '\0'; if (isspace (BUCHAR (zline[0])) || zline[0] == '#') continue; ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); if (ctoks < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } if (ctoks < 1) continue; if (strcmp (zname, pzsplit[0]) != 0) continue; /* We found the dialer. */ pblock = uuconf_malloc_block (); if (pblock == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } if (uuconf_add_block (pblock, zline) != 0) { qglobal->ierrno = errno; uuconf_free_block (pblock); iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } zline = NULL; _uuconf_uclear_dialer (qdialer); qdialer->uuconf_zname = pzsplit[0]; qdialer->uuconf_palloc = pblock; if (ctoks > 1) { /* The second field is characters to send instead of "=" and "-" in phone numbers. */ if (strcmp (pzsplit[1], "\"\"") == 0) { char *zsubs; char bnext; zsubs = pzsplit[1]; bnext = *zsubs; while (bnext != '\0') { if (bnext == '=') qdialer->uuconf_zdialtone = zsubs + 1; else if (bnext == '-') qdialer->uuconf_zpause = zsubs + 1; if (zsubs[1] == '\0') break; zsubs += 2; bnext = *zsubs; *zsubs = '\0'; } } /* Any remaining fields form a chat script. */ if (ctoks > 2) { pzsplit[1] = (char *) "chat"; iret = _uuconf_ichat_cmd (qglobal, ctoks - 1, pzsplit + 1, &qdialer->uuconf_schat, pblock); iret &=~ UUCONF_CMDTABRET_KEEP; if (iret != UUCONF_SUCCESS) { uuconf_free_block (pblock); break; } } } iret = UUCONF_SUCCESS; break; } (void) fclose (e); if (iret != UUCONF_NOT_FOUND) break; } if (zline != NULL) free ((pointer) zline); if (pzsplit != NULL) free ((pointer) pzsplit); if (iret != UUCONF_SUCCESS && iret != UUCONF_NOT_FOUND) { qglobal->zfilename = *pz; iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; } return iret; } uucp-1.07/uuconf/hdnams.c0000664000076400007640000000530607665321761011046 /* hdnams.c Get all known dialer names from the HDB configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_hdnams_rcsid[] = "$Id: hdnams.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif #include #include /* Get all the dialer names from the HDB Dialers file. */ int uuconf_hdb_dialer_names (pglobal, ppzdialers) pointer pglobal; char ***ppzdialers; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; char *zline; size_t cline; char **pz; *ppzdialers = NULL; iret = UUCONF_SUCCESS; zline = NULL; cline = 0; for (pz = qglobal->qprocess->pzhdb_dialers; *pz != NULL; pz++) { FILE *e; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } qglobal->ilineno = 0; while (_uuconf_getline (qglobal, &zline, &cline, e) > 0) { ++qglobal->ilineno; /* Lines beginning with whitespace are treated as comments. No dialer name can contain a '#', which is another comment character, so eliminating the first '#' does no harm and catches comments. */ zline[strcspn (zline, " \t#\n")] = '\0'; if (*zline == '\0') continue; iret = _uuconf_iadd_string (qglobal, zline, TRUE, TRUE, ppzdialers, (pointer) NULL); if (iret != UUCONF_SUCCESS) { iret |= UUCONF_ERROR_LINENO; break; } } (void) fclose (e); } if (zline != NULL) free ((pointer) zline); if (iret != UUCONF_SUCCESS) { qglobal->zfilename = *pz; return iret | UUCONF_ERROR_FILENAME; } if (*ppzdialers == NULL) iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, ppzdialers, (pointer) NULL); return UUCONF_SUCCESS; } uucp-1.07/uuconf/hinit.c0000664000076400007640000001755507665321761010720 /* hinit.c Initialize for reading HDB configuration files. Copyright (C) 1992, 1994 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_hinit_rcsid[] = "$Id: hinit.c,v 1.9 2002/03/05 19:10:42 ian Rel $"; #endif #include #include /* Avoid replicating OLDCONFIGLIB several times if not necessary. */ static const char abHoldconfiglib[] = OLDCONFIGLIB; /* Initialize the routines which read HDB configuration files. */ int uuconf_hdb_init (ppglobal, zprogram) pointer *ppglobal; const char *zprogram; { struct sglobal **pqglobal = (struct sglobal **) ppglobal; int iret; struct sglobal *qglobal; pointer pblock; char abdialcodes[sizeof OLDCONFIGLIB + sizeof HDB_DIALCODES - 1]; char *zsys; FILE *e; if (*pqglobal == NULL) { iret = _uuconf_iinit_global (pqglobal); if (iret != UUCONF_SUCCESS) return iret; } qglobal = *pqglobal; pblock = qglobal->pblock; if (zprogram == NULL || strcmp (zprogram, "uucp") == 0) zprogram = "uucico"; /* Add the Dialcodes file to the global list. */ memcpy ((pointer) abdialcodes, (pointer) abHoldconfiglib, sizeof OLDCONFIGLIB - 1); memcpy ((pointer) (abdialcodes + sizeof OLDCONFIGLIB - 1), (pointer) HDB_DIALCODES, sizeof HDB_DIALCODES); iret = _uuconf_iadd_string (qglobal, abdialcodes, TRUE, FALSE, &qglobal->qprocess->pzdialcodefiles, pblock); if (iret != UUCONF_SUCCESS) return iret; /* Read the Sysfiles file. We allocate the name on the heap rather than the stack so that we can return it in qerr->uuconf_zfilename. */ zsys = uuconf_malloc (pblock, sizeof OLDCONFIGLIB + sizeof HDB_SYSFILES - 1); if (zsys == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) zsys, (pointer) abHoldconfiglib, sizeof OLDCONFIGLIB - 1); memcpy ((pointer) (zsys + sizeof OLDCONFIGLIB - 1), (pointer) HDB_SYSFILES, sizeof HDB_SYSFILES); iret = UUCONF_SUCCESS; e = fopen (zsys, "r"); if (e == NULL) uuconf_free (pblock, zsys); else { char *zline; size_t cline; char **pzargs; size_t cargs; char **pzcolon; size_t ccolon; int cchars; zline = NULL; cline = 0; pzargs = NULL; cargs = 0; pzcolon = NULL; ccolon = 0; qglobal->ilineno = 0; while (iret == UUCONF_SUCCESS && (cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) { int ctypes, cnames; int i; ++qglobal->ilineno; --cchars; if (zline[cchars] == '\n') zline[cchars] = '\0'; if (zline[0] == '#') continue; ctypes = _uuconf_istrsplit (zline, '\0', &pzargs, &cargs); if (ctypes < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } if (ctypes == 0) continue; if (strncmp (pzargs[0], "service=", sizeof "service=" - 1) != 0) { iret = UUCONF_SYNTAX_ERROR; break; } pzargs[0] += sizeof "service=" - 1; cnames = _uuconf_istrsplit (pzargs[0], ':', &pzcolon, &ccolon); if (cnames < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } for (i = 0; i < cnames; i++) if (strcmp (zprogram, pzcolon[i]) == 0) break; if (i >= cnames) continue; for (i = 1; i < ctypes && iret == UUCONF_SUCCESS; i++) { char ***ppz; int cfiles, ifile; if (strncmp (pzargs[i], "systems=", sizeof "systems=" - 1) == 0) { ppz = &qglobal->qprocess->pzhdb_systems; pzargs[i] += sizeof "systems=" - 1; } else if (strncmp (pzargs[i], "devices=", sizeof "devices=" - 1) == 0) { ppz = &qglobal->qprocess->pzhdb_devices; pzargs[i] += sizeof "devices=" - 1; } else if (strncmp (pzargs[i], "dialers=", sizeof "dialers=" - 1) == 0) { ppz = &qglobal->qprocess->pzhdb_dialers; pzargs[i] += sizeof "dialers=" - 1; } else { iret = UUCONF_SYNTAX_ERROR; break; } cfiles = _uuconf_istrsplit (pzargs[i], ':', &pzcolon, &ccolon); if (cfiles < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } for (ifile = 0; ifile < cfiles && iret == UUCONF_SUCCESS; ifile++) { /* Looking for a leading '/' is Unix dependent, and should probably be changed. */ if (pzcolon[ifile][0] == '/') iret = _uuconf_iadd_string (qglobal, pzcolon[ifile], TRUE, FALSE, ppz, pblock); else { char *zdir; size_t clen; clen = strlen (pzcolon[ifile]); zdir = (char *) uuconf_malloc (pblock, (sizeof OLDCONFIGLIB + sizeof HDB_SEPARATOR + clen - 1)); if (zdir == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } memcpy ((pointer) zdir, (pointer) abHoldconfiglib, sizeof OLDCONFIGLIB - 1); memcpy ((pointer) (zdir + sizeof OLDCONFIGLIB - 1), HDB_SEPARATOR, sizeof HDB_SEPARATOR - 1); memcpy ((pointer) (zdir + sizeof OLDCONFIGLIB - 1 + sizeof HDB_SEPARATOR - 1), (pointer) pzcolon[ifile], clen + 1); iret = _uuconf_iadd_string (qglobal, zdir, FALSE, FALSE, ppz, pblock); } } } } (void) fclose (e); if (zline != NULL) free ((pointer) zline); if (pzargs != NULL) free ((pointer) pzargs); if (pzcolon != NULL) free ((pointer) pzcolon); if (iret != UUCONF_SUCCESS) { qglobal->zfilename = zsys; return iret | UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; } } if (qglobal->qprocess->pzhdb_systems == NULL) { char ab[sizeof OLDCONFIGLIB + sizeof HDB_SYSTEMS - 1]; memcpy ((pointer) ab, (pointer) abHoldconfiglib, sizeof OLDCONFIGLIB - 1); memcpy ((pointer) (ab + sizeof OLDCONFIGLIB - 1), (pointer) HDB_SYSTEMS, sizeof HDB_SYSTEMS); iret = _uuconf_iadd_string (qglobal, ab, TRUE, FALSE, &qglobal->qprocess->pzhdb_systems, pblock); } if (qglobal->qprocess->pzhdb_devices == NULL && iret == UUCONF_SUCCESS) { char ab[sizeof OLDCONFIGLIB + sizeof HDB_DEVICES - 1]; memcpy ((pointer) ab, (pointer) abHoldconfiglib, sizeof OLDCONFIGLIB - 1); memcpy ((pointer) (ab + sizeof OLDCONFIGLIB - 1), (pointer) HDB_DEVICES, sizeof HDB_DEVICES); iret = _uuconf_iadd_string (qglobal, ab, TRUE, FALSE, &qglobal->qprocess->pzhdb_devices, pblock); } if (qglobal->qprocess->pzhdb_dialers == NULL && iret == UUCONF_SUCCESS) { char ab[sizeof OLDCONFIGLIB + sizeof HDB_DIALERS - 1]; memcpy ((pointer) ab, (pointer) abHoldconfiglib, sizeof OLDCONFIGLIB - 1); memcpy ((pointer) (ab + sizeof OLDCONFIGLIB - 1), (pointer) HDB_DIALERS, sizeof HDB_DIALERS); iret = _uuconf_iadd_string (qglobal, ab, TRUE, FALSE, &qglobal->qprocess->pzhdb_dialers, pblock); } return iret; } uucp-1.07/uuconf/hlocnm.c0000664000076400007640000000434607665321761011057 /* hlocnm.c Get the local name to use from the HDB configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_hlocnm_rcsid[] = "$Id: hlocnm.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Get the local name to use, based on the login name, from the HDB configuration files. */ int uuconf_hdb_login_localname (pglobal, zlogin, pzname) pointer pglobal; const char *zlogin; char **pzname; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct shpermissions *qperm; if (! qglobal->qprocess->fhdb_read_permissions) { int iret; iret = _uuconf_ihread_permissions (qglobal); if (iret != UUCONF_SUCCESS) return iret; } for (qperm = qglobal->qprocess->qhdb_permissions; qperm != NULL; qperm = qperm->qnext) { if (qperm->zmyname != NULL && qperm->zmyname != (char *) &_uuconf_unset && qperm->pzlogname != NULL && qperm->pzlogname != (char **) &_uuconf_unset) { char **pz; for (pz = qperm->pzlogname; *pz != NULL; pz++) { if (strcmp (*pz, zlogin) == 0) { *pzname = strdup (qperm->zmyname); if (*pzname == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } return UUCONF_SUCCESS; } } } } *pzname = NULL; return UUCONF_NOT_FOUND; } uucp-1.07/uuconf/hport.c0000664000076400007640000002313607665321761010731 /* hport.c Find a port in the HDB configuration files. Copyright (C) 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_hport_rcsid[] = "$Id: hport.c,v 1.18 2002/03/05 19:10:42 ian Rel $"; #endif #include #include /* Find a port in the HDB configuration files by name, baud rate, and special purpose function. */ int uuconf_hdb_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, qport) pointer pglobal; const char *zname; long ibaud; long ihighbaud ATTRIBUTE_UNUSED; int (*pifn) P((struct uuconf_port *, pointer)); pointer pinfo; struct uuconf_port *qport; { struct sglobal *qglobal = (struct sglobal *) pglobal; char *zline; size_t cline; char **pzsplit; size_t csplit; int iret; char **pz; zline = NULL; cline = 0; pzsplit = NULL; csplit = 0; iret = UUCONF_NOT_FOUND; for (pz = qglobal->qprocess->pzhdb_devices; *pz != NULL; pz++) { FILE *e; int cchars; qglobal->ilineno = 0; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } iret = UUCONF_NOT_FOUND; while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) { int ctoks; char *z, *zprotos, *zport; long ilow, ihigh; pointer pblock; char ***ppzdialer; ++qglobal->ilineno; iret = UUCONF_NOT_FOUND; --cchars; if (zline[cchars] == '\n') zline[cchars] = '\0'; if (isspace (BUCHAR (zline[0])) || zline[0] == '#') continue; ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); if (ctoks < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } /* An entry in Devices is type device dial-device baud dialer-token pairs The type (normally "ACU") is treated as the name. */ /* If there aren't enough entries, ignore the line; this should probably do something more useful. */ if (ctoks < 4) continue; /* There may be a comma separated list of protocols after the name. */ zprotos = strchr (pzsplit[0], ','); if (zprotos != NULL) { *zprotos = '\0'; ++zprotos; } zport = pzsplit[0]; /* Get any modem class, and pick up the baud rate while we're at it. The modem class will be appended to the name, so we need to get it before we see if we've found the port with the right name. */ z = pzsplit[3]; if (strcasecmp (z, "Any") == 0 || strcmp (z, "-") == 0) { ilow = 0L; ihigh = 0L; } else { char *zend; while (*z != '\0' && ! isdigit (BUCHAR (*z))) ++z; ilow = strtol (z, &zend, 10); if (*zend == '-') ihigh = strtol (zend + 1, (char **) NULL, 10); else ihigh = ilow; if (z != pzsplit[3]) { size_t cclass, cport; cclass = z - pzsplit[3]; cport = strlen (pzsplit[0]); zport = malloc (cport + cclass + 1); if (zport == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } memcpy ((pointer) zport, (pointer) pzsplit[0], cport); memcpy ((pointer) (zport + cport), (pointer) pzsplit[3], cclass); zport[cport + cclass] = '\0'; } } /* Make sure the name and baud rate match any argument. */ if ((zname != NULL && strcmp (zport, zname) != 0) || (ibaud != 0 && ilow != 0 && (ilow > ibaud || ihigh < ibaud))) { if (zport != pzsplit[0]) free ((pointer) zport); continue; } /* Some systems permit ,M after the device name. This means to open the port with O_NDELAY and then change it. We just ignore this flag, although perhaps we should record it somewhere. */ pzsplit[1][strcspn (pzsplit[1], ",")] = '\0'; /* Now we must construct the port information, so that we can pass it to pifn. The port type is determined by its name, unfortunately. The name "Direct" is used for a direct port, "TCP" for a TCP port, and anything else for a modem port. */ pblock = NULL; _uuconf_uclear_port (qport); qport->uuconf_zname = zport; qport->uuconf_zprotocols = zprotos; if (strcmp (pzsplit[0], "Direct") == 0) { qport->uuconf_ttype = UUCONF_PORTTYPE_DIRECT; qport->uuconf_u.uuconf_sdirect.uuconf_zdevice = pzsplit[1]; qport->uuconf_u.uuconf_sdirect.uuconf_ibaud = ilow; qport->uuconf_u.uuconf_sdirect.uuconf_fcarrier = FALSE; qport->uuconf_u.uuconf_sdirect.uuconf_fhardflow = TRUE; ppzdialer = NULL; } else if (strcmp (pzsplit[0], "TCP") == 0) { /* For a TCP port, the device name is taken as the TCP port to use. */ qport->uuconf_ttype = UUCONF_PORTTYPE_TCP; qport->uuconf_ireliable = (UUCONF_RELIABLE_ENDTOEND | UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX | UUCONF_RELIABLE_SPECIFIED); qport->uuconf_u.uuconf_stcp.uuconf_zport = pzsplit[1]; qport->uuconf_u.uuconf_stcp.uuconf_iversion = 0; ppzdialer = &qport->uuconf_u.uuconf_stcp.uuconf_pzdialer; } else if (ctoks >= 5 && (strcmp (pzsplit[4], "TLI") == 0 || strcmp (pzsplit[4], "TLIS") == 0)) { qport->uuconf_ttype = UUCONF_PORTTYPE_TLI; qport->uuconf_u.uuconf_stli.uuconf_zdevice = pzsplit[1]; qport->uuconf_u.uuconf_stli.uuconf_fstream = strcmp (pzsplit[4], "TLIS") == 0; qport->uuconf_u.uuconf_stli.uuconf_pzpush = NULL; qport->uuconf_u.uuconf_stli.uuconf_zservaddr = NULL; qport->uuconf_ireliable = (UUCONF_RELIABLE_ENDTOEND | UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX | UUCONF_RELIABLE_SPECIFIED); ppzdialer = &qport->uuconf_u.uuconf_stli.uuconf_pzdialer; } else { qport->uuconf_ttype = UUCONF_PORTTYPE_MODEM; qport->uuconf_u.uuconf_smodem.uuconf_zdevice = pzsplit[1]; if (strcmp (pzsplit[2], "-") != 0) qport->uuconf_u.uuconf_smodem.uuconf_zdial_device = pzsplit[2]; else qport->uuconf_u.uuconf_smodem.uuconf_zdial_device = NULL; if (ilow == ihigh) { qport->uuconf_u.uuconf_smodem.uuconf_ibaud = ilow; qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud = 0L; qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud = 0L; } else { qport->uuconf_u.uuconf_smodem.uuconf_ibaud = 0L; qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud = ilow; qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud = ihigh; } qport->uuconf_u.uuconf_smodem.uuconf_fcarrier = TRUE; qport->uuconf_u.uuconf_smodem.uuconf_fhardflow = TRUE; qport->uuconf_u.uuconf_smodem.uuconf_qdialer = NULL; ppzdialer = &qport->uuconf_u.uuconf_smodem.uuconf_pzdialer; } if (ppzdialer != NULL) { if (ctoks < 5) *ppzdialer = NULL; else { size_t c; char **pzd; pblock = uuconf_malloc_block (); if (pblock == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } c = (ctoks - 4) * sizeof (char *); pzd = (char **) uuconf_malloc (pblock, c + sizeof (char *)); if (pzd == NULL) { qglobal->ierrno = errno; uuconf_free_block (pblock); iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } memcpy ((pointer) pzd, (pointer) (pzsplit + 4), c); pzd[ctoks - 4] = NULL; *ppzdialer = pzd; } } if (pifn != NULL) { iret = (*pifn) (qport, pinfo); if (iret != UUCONF_SUCCESS) { if (zport != pzsplit[0]) free ((pointer) zport); if (pblock != NULL) uuconf_free_block (pblock); if (iret != UUCONF_NOT_FOUND) break; continue; } } /* This is the port we want. */ if (pblock == NULL) { pblock = uuconf_malloc_block (); if (pblock == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } } if (uuconf_add_block (pblock, zline) != 0 || (zport != pzsplit[0] && uuconf_add_block (pblock, zport) != 0)) { qglobal->ierrno = errno; uuconf_free_block (pblock); iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } zline = NULL; qport->uuconf_palloc = pblock; iret = UUCONF_SUCCESS; break; } (void) fclose (e); if (iret != UUCONF_NOT_FOUND) break; } if (zline != NULL) free ((pointer) zline); if (pzsplit != NULL) free ((pointer) pzsplit); if (iret != UUCONF_SUCCESS && iret != UUCONF_NOT_FOUND) { qglobal->zfilename = *pz; iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; } return iret; } uucp-1.07/uuconf/hrmunk.c0000664000076400007640000000332207665321761011074 /* remunk.c Get the name of the HDB remote.unknown shell script. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_hrmunk_rcsid[] = "$Id: hrmunk.c,v 1.5 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Get the name of the HDB remote.unknown shell script. */ int uuconf_hdb_remote_unknown (pglobal, pzname) pointer pglobal; char **pzname; { struct sglobal *qglobal = (struct sglobal *) pglobal; size_t csize; csize = sizeof OLDCONFIGLIB + sizeof HDB_REMOTE_UNKNOWN - 1; *pzname = malloc (csize); if (*pzname == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy (*pzname, OLDCONFIGLIB, sizeof OLDCONFIGLIB - 1); memcpy (*pzname + sizeof OLDCONFIGLIB - 1, HDB_REMOTE_UNKNOWN, sizeof HDB_REMOTE_UNKNOWN); return UUCONF_SUCCESS; } uucp-1.07/uuconf/hsinfo.c0000664000076400007640000004152607665321761011066 /* hsinfo.c Get information about a system from the HDB configuration files. Copyright (C) 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_hsinfo_rcsid[] = "$Id: hsinfo.c,v 1.17 2002/03/05 19:10:42 ian Rel $"; #endif #include #include static int ihadd_machine_perm P((struct sglobal *qglobal, struct uuconf_system *qsys, struct shpermissions *qperm)); static int ihadd_logname_perm P((struct sglobal *qglobal, struct uuconf_system *qsys, struct shpermissions *qperm)); /* Get the information for a particular system from the HDB configuration files. This does not make sure that all the default values are set. */ int _uuconf_ihdb_system_internal (qglobal, zsystem, qsys) struct sglobal *qglobal; const char *zsystem; struct uuconf_system *qsys; { int iret; struct shpermissions *qperm; char *zline; size_t cline; char **pzsplit; size_t csplit; char **pzcomma; size_t ccomma; pointer pblock; char **pz; boolean ffound_machine, ffound_login; struct shpermissions *qother_machine; struct uuconf_system *qalt; if (! qglobal->qprocess->fhdb_read_permissions) { iret = _uuconf_ihread_permissions (qglobal); if (iret != UUCONF_SUCCESS) return iret; } /* First look through the Permissions information to see if this is an alias for some system. I assume that an alias applies to the first name in the corresponding MACHINE entry. */ for (qperm = qglobal->qprocess->qhdb_permissions; qperm != NULL; qperm = qperm->qnext) { if (qperm->pzalias == NULL || qperm->pzmachine == NULL || qperm->pzalias == (char **) &_uuconf_unset || qperm->pzmachine == (char **) &_uuconf_unset) continue; for (pz = qperm->pzalias; *pz != NULL; pz++) { if (strcmp (*pz, zsystem) == 0) { zsystem = qperm->pzmachine[0]; break; } } if (*pz != NULL) break; } zline = NULL; cline = 0; pzsplit = NULL; csplit = 0; pzcomma = NULL; ccomma = 0; pblock = NULL; iret = UUCONF_SUCCESS; for (pz = qglobal->qprocess->pzhdb_systems; *pz != NULL; pz++) { FILE *e; int cchars; qglobal->ilineno = 0; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) { int ctoks, ctimes, i; struct uuconf_system *qset; char *z, *zretry; int cretry; ++qglobal->ilineno; --cchars; if (zline[cchars] == '\n') zline[cchars] = '\0'; if (isspace (BUCHAR (zline[0])) || zline[0] == '#') continue; ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); if (ctoks < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } /* If this isn't the system we're looking for, keep reading the file. */ if (ctoks < 1 || strcmp (zsystem, pzsplit[0]) != 0) continue; /* If this is the first time we've found the system, we want to set *qsys directly. Otherwise, we allocate a new alternate. */ if (pblock == NULL) { pblock = uuconf_malloc_block (); if (pblock == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } _uuconf_uclear_system (qsys); qsys->uuconf_palloc = pblock; qset = qsys; } else { struct uuconf_system **pq; qset = ((struct uuconf_system *) uuconf_malloc (pblock, sizeof (struct uuconf_system))); if (qset == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } _uuconf_uclear_system (qset); for (pq = &qsys->uuconf_qalternate; *pq != NULL; pq = &(*pq)->uuconf_qalternate) ; *pq = qset; } /* Add this line to the memory block we are building for the system. */ if (uuconf_add_block (pblock, zline) != 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } zline = NULL; cline = 0; /* The format of a line in Systems is system time device speed phone chat For example, airs Any ACU 9600 5551212 ogin: foo pass: bar */ /* Get the system name. */ qset->uuconf_zname = pzsplit[0]; qset->uuconf_fcall = TRUE; qset->uuconf_fcalled = FALSE; if (ctoks < 2) continue; /* A time string is "time/grade,time/grade;retry". A missing grade is taken as BGRADE_LOW. */ zretry = strchr (pzsplit[1], ';'); if (zretry == NULL) cretry = 0; else { *zretry = '\0'; cretry = (int) strtol (zretry + 1, (char **) NULL, 10); } ctimes = _uuconf_istrsplit (pzsplit[1], ',', &pzcomma, &ccomma); if (ctimes < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } for (i = 0; i < ctimes; i++) { char *zslash; char bgrade; z = pzcomma[i]; zslash = strchr (z, '/'); if (zslash == NULL) bgrade = UUCONF_GRADE_LOW; else { *zslash = '\0'; bgrade = zslash[1]; if (! UUCONF_GRADE_LEGAL (bgrade)) bgrade = UUCONF_GRADE_LOW; } iret = _uuconf_itime_parse (qglobal, z, (long) bgrade, cretry, _uuconf_itime_grade_cmp, &qset->uuconf_qtimegrade, pblock); /* We treat a syntax error in the time field as equivalent to ``never'', on the assumption that that is what HDB does. */ if (iret == UUCONF_SYNTAX_ERROR) iret = UUCONF_SUCCESS; if (iret != UUCONF_SUCCESS) break; /* Treat any time/grade setting as both a timegrade and a call-timegrade. */ if (bgrade != UUCONF_GRADE_LOW) qset->uuconf_qcalltimegrade = qset->uuconf_qtimegrade; } if (iret != UUCONF_SUCCESS) break; if (ctoks < 3) continue; /* Pick up the device name. It can be followed by a comma and a list of protocols. */ qset->uuconf_zport = pzsplit[2]; z = strchr (pzsplit[2], ','); if (z != NULL) { qset->uuconf_zprotocols = z + 1; *z = '\0'; } if (ctoks < 4) continue; /* The speed entry can be a numeric speed, or a range of speeds, or "Any", or "-". If it starts with a letter, the initial nonnumeric prefix is a modem class, which gets appended to the port name. */ z = pzsplit[3]; if (strcasecmp (z, "Any") != 0 && strcmp (z, "-") != 0) { char *zend; while (*z != '\0' && ! isdigit (BUCHAR (*z))) ++z; qset->uuconf_ibaud = strtol (z, &zend, 10); if (*zend == '-') qset->uuconf_ihighbaud = strtol (zend + 1, (char **) NULL, 10); if (z != pzsplit[3]) { size_t cport, cclass; cport = strlen (pzsplit[2]); cclass = z - pzsplit[3]; qset->uuconf_zport = uuconf_malloc (pblock, cport + cclass + 1); if (qset->uuconf_zport == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } memcpy ((pointer) qset->uuconf_zport, (pointer) pzsplit[2], cport); memcpy ((pointer) (qset->uuconf_zport + cport), (pointer) pzsplit[3], cclass); qset->uuconf_zport[cport + cclass] = '\0'; } } if (ctoks < 5) continue; /* Get the phone number. */ qset->uuconf_zphone = pzsplit[4]; if (ctoks < 6) continue; /* Get the chat script. We just hand this off to the chat script processor, so that it will parse subsend and subexpect strings correctly. */ pzsplit[4] = (char *) "chat"; iret = _uuconf_ichat_cmd (qglobal, ctoks - 4, pzsplit + 4, &qset->uuconf_schat, pblock); iret &=~ UUCONF_CMDTABRET_KEEP; if (iret != UUCONF_SUCCESS) break; } (void) fclose (e); if (iret != UUCONF_SUCCESS) break; } if (zline != NULL) free ((pointer) zline); if (pzsplit != NULL) free ((pointer) pzsplit); if (pzcomma != NULL) free ((pointer) pzcomma); if (iret != UUCONF_SUCCESS) { qglobal->zfilename = *pz; return iret | UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; } if (pblock == NULL) return UUCONF_NOT_FOUND; /* Now we have to put in the Permissions information. The relevant Permissions entries are those with this system in the MACHINE list and (if this system does not have a VALIDATE entry) those with a LOGNAME list but no MACHINE list. If no entry is found with this system in the MACHINE list, then we must look for an entry with "OTHER" in the MACHINE list. */ ffound_machine = FALSE; ffound_login = FALSE; qother_machine = NULL; for (qperm = qglobal->qprocess->qhdb_permissions; qperm != NULL; qperm = qperm->qnext) { boolean fmachine; /* MACHINE=OTHER is recognized specially. It appears that OTHER need only be recognized by itself, not when combined with other machine names. */ if (qother_machine == NULL && qperm->pzmachine != NULL && qperm->pzmachine != (char **) &_uuconf_unset && qperm->pzmachine[0][0] == 'O' && strcmp (qperm->pzmachine[0], "OTHER") == 0) qother_machine = qperm; /* If this system is named in a MACHINE entry, we must add the appropriate information to every alternate that could be used for calling out. */ fmachine = FALSE; if (! ffound_machine && qperm->pzmachine != NULL && qperm->pzmachine != (char **) &_uuconf_unset) { for (pz = qperm->pzmachine; *pz != NULL; pz++) { if ((*pz)[0] == zsystem[0] && strcmp (*pz, zsystem) == 0) { for (qalt = qsys; qalt != NULL; qalt = qalt->uuconf_qalternate) { if (qalt->uuconf_fcall) { iret = ihadd_machine_perm (qglobal, qalt, qperm); if (iret != UUCONF_SUCCESS) return iret; } } fmachine = TRUE; ffound_machine = TRUE; break; } } } /* A LOGNAME line applies to this machine if it is listed in the corresponding VALIDATE entry, or if it is not listed in any VALIDATE entry. On this pass through the Permissions entry we pick up the information if the system appears in a VALIDATE entry; if it does not, we make another pass to put in all the LOGNAME lines. */ if (qperm->pzlogname != NULL && qperm->pzlogname != (char **) &_uuconf_unset && qperm->pzvalidate != NULL && qperm->pzvalidate != (char **) &_uuconf_unset) { for (pz = qperm->pzvalidate; *pz != NULL; ++pz) if ((*pz)[0] == zsystem[0] && strcmp (*pz, zsystem) == 0) break; if (*pz != NULL) { for (pz = qperm->pzlogname; *pz != NULL; ++pz) { /* If this LOGNAME line is also a matching MACHINE line, we can add the LOGNAME permissions to the first alternate. Otherwise, we must create a new alternate. We cannot put a LOGNAME line in the first alternate if MACHINE does not match, because certain permissions (e.g. READ) may be specified by both types of lines, and we must use LOGNAME entries only when accepting calls and MACHINE entries only when placing calls. */ if (fmachine && (qsys->uuconf_zcalled_login == NULL || (qsys->uuconf_zcalled_login == (char *) &_uuconf_unset))) { qsys->uuconf_zcalled_login = *pz; iret = ihadd_logname_perm (qglobal, qsys, qperm); } else { struct uuconf_system *qnew; struct uuconf_system **pq; qnew = ((struct uuconf_system *) uuconf_malloc (pblock, sizeof (struct uuconf_system))); if (qnew == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } *qnew = *qsys; qnew->uuconf_qalternate = NULL; for (pq = &qsys->uuconf_qalternate; *pq != NULL; pq = &(*pq)->uuconf_qalternate) ; *pq = qnew; qnew->uuconf_zcalled_login = *pz; qnew->uuconf_fcall = FALSE; iret = ihadd_logname_perm (qglobal, qnew, qperm); } if (iret != UUCONF_SUCCESS) return iret; } ffound_login = TRUE; } } } /* If we didn't find an entry for the machine, we must use the MACHINE=OTHER entry, if any. */ if (! ffound_machine && qother_machine != NULL) { for (qalt = qsys; qalt != NULL; qalt = qalt->uuconf_qalternate) { if (qalt->uuconf_fcall) { iret = ihadd_machine_perm (qglobal, qalt, qother_machine); if (iret != UUCONF_SUCCESS) return iret; } } } /* If this system was not listed in any VALIDATE entry, then we must add a called-login for each LOGNAME entry in Permissions. */ if (! ffound_login) { for (qperm = qglobal->qprocess->qhdb_permissions; qperm != NULL; qperm = qperm->qnext) { if (qperm->pzlogname == NULL || qperm->pzlogname == (char **) &_uuconf_unset) continue; for (pz = qperm->pzlogname; *pz != NULL; pz++) { struct uuconf_system *qnew; struct uuconf_system **pq; qnew = ((struct uuconf_system *) uuconf_malloc (pblock, sizeof (struct uuconf_system))); if (qnew == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } *qnew = *qsys; qnew->uuconf_qalternate = NULL; for (pq = &qsys->uuconf_qalternate; *pq != NULL; pq = &(*pq)->uuconf_qalternate) ; *pq = qnew; /* We recognize LOGNAME=OTHER specially, although this appears to be an SCO innovation. */ if (strcmp (*pz, "OTHER") == 0) qnew->uuconf_zcalled_login = (char *) "ANY"; else qnew->uuconf_zcalled_login = *pz; qnew->uuconf_fcall = FALSE; iret = ihadd_logname_perm (qglobal, qnew, qperm); if (iret != UUCONF_SUCCESS) return iret; } } } /* HDB permits local requests to receive to any directory, which is not the default put in by _uuconf_isystem_basic_default. We set it here instead. */ for (qalt = qsys; qalt != NULL; qalt = qalt->uuconf_qalternate) { iret = _uuconf_iadd_string (qglobal, (char *) ZROOTDIR, FALSE, FALSE, &qalt->uuconf_pzlocal_receive, pblock); if (iret != UUCONF_SUCCESS) return iret; } /* HDB does not have a maximum number of retries if a retry time is given in the time field. */ if (qsys->uuconf_qtimegrade != NULL && qsys->uuconf_qtimegrade != (struct uuconf_timespan *) &_uuconf_unset && qsys->uuconf_qtimegrade->uuconf_cretry > 0) qsys->uuconf_cmax_retries = 0; return UUCONF_SUCCESS; } /* Add the settings of a MACHINE line in Permissions to a system. */ /*ARGSIGNORED*/ static int ihadd_machine_perm (qglobal, qsys, qperm) struct sglobal *qglobal ATTRIBUTE_UNUSED; struct uuconf_system *qsys; struct shpermissions *qperm; { if (qperm->frequest >= 0) qsys->uuconf_fsend_request = qperm->frequest; else qsys->uuconf_fsend_request = FALSE; qsys->uuconf_pzremote_send = qperm->pzread; qsys->uuconf_pzremote_receive = qperm->pzwrite; qsys->uuconf_pzcmds = qperm->pzcommands; qsys->uuconf_zlocalname = qperm->zmyname; qsys->uuconf_zpubdir = qperm->zpubdir; qsys->uuconf_pzalias = qperm->pzalias; return UUCONF_SUCCESS; } /* Add the settings of a LOGNAME line in Permissions to a system. */ /*ARGSIGNORED*/ static int ihadd_logname_perm (qglobal, qsys, qperm) struct sglobal *qglobal ATTRIBUTE_UNUSED; struct uuconf_system *qsys; struct shpermissions *qperm; { qsys->uuconf_fcalled = TRUE; if (qperm->frequest >= 0) qsys->uuconf_fsend_request = qperm->frequest; else qsys->uuconf_fsend_request = FALSE; qsys->uuconf_fcalled_transfer = qperm->fsendfiles; qsys->uuconf_pzremote_send = qperm->pzread; qsys->uuconf_pzremote_receive = qperm->pzwrite; qsys->uuconf_fcallback = qperm->fcallback; qsys->uuconf_zlocalname = qperm->zmyname; qsys->uuconf_zpubdir = qperm->zpubdir; return UUCONF_SUCCESS; } uucp-1.07/uuconf/hsnams.c0000664000076400007640000000667507665321761011077 /* hsnams.c Get all known system names from the HDB configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_hsnams_rcsid[] = "$Id: hsnams.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif #include #include /* Get all the system names from the HDB Systems file. We have to read the Permissions file in order to support aliases. */ int uuconf_hdb_system_names (pglobal, ppzsystems, falias) pointer pglobal; char ***ppzsystems; int falias; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; char *zline; size_t cline; char **pz; *ppzsystems = NULL; iret = UUCONF_SUCCESS; zline = NULL; cline = 0; for (pz = qglobal->qprocess->pzhdb_systems; *pz != NULL; pz++) { FILE *e; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } qglobal->ilineno = 0; while (_uuconf_getline (qglobal, &zline, &cline, e) > 0) { ++qglobal->ilineno; /* Lines beginning with whitespace are treated as comments. No system name can contain a '#', which is another comment character, so eliminating the first '#' does no harm and catches comments. */ zline[strcspn (zline, " \t#\n")] = '\0'; if (*zline == '\0') continue; iret = _uuconf_iadd_string (qglobal, zline, TRUE, TRUE, ppzsystems, (pointer) NULL); if (iret != UUCONF_SUCCESS) { iret |= UUCONF_ERROR_LINENO; break; } } (void) fclose (e); } if (zline != NULL) free ((pointer) zline); if (iret != UUCONF_SUCCESS) { qglobal->zfilename = *pz; return iret | UUCONF_ERROR_FILENAME; } /* If we are supposed to return aliases, we must read the Permissions file. */ if (falias) { struct shpermissions *q; if (! qglobal->qprocess->fhdb_read_permissions) { iret = _uuconf_ihread_permissions (qglobal); if (iret != UUCONF_SUCCESS) return iret; } for (q = qglobal->qprocess->qhdb_permissions; q != NULL; q = q->qnext) { pz = q->pzalias; if (pz == NULL || pz == (char **) &_uuconf_unset) continue; for (; *pz != NULL; pz++) { iret = _uuconf_iadd_string (qglobal, *pz, TRUE, TRUE, ppzsystems, (pointer) NULL); if (iret != UUCONF_SUCCESS) return iret; } } } if (*ppzsystems == NULL) iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, ppzsystems, (pointer) NULL); return iret; } uucp-1.07/uuconf/hsys.c0000664000076400007640000000316707665321761010565 /* hsys.c User function to get a system from the HDB configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_hsys_rcsid[] = "$Id: hsys.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* Get system information from the HDB configuration files. This is a wrapper for the internal function which makes sure that every field gets a default value. */ int uuconf_hdb_system_info (pglobal, zsystem, qsys) pointer pglobal; const char *zsystem; struct uuconf_system *qsys; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; iret = _uuconf_ihdb_system_internal (qglobal, zsystem, qsys); if (iret != UUCONF_SUCCESS) return iret; return _uuconf_isystem_basic_default (qglobal, qsys); } uucp-1.07/uuconf/hunk.c0000664000076400007640000000767707665321761010556 /* hunk.c Get information about an unknown system from the HDB Permissions file. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_hunk_rcsid[] = "$Id: hunk.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Get information about an unknown system from the HDB Permissions file. This doesn't run the remote.unknown shell script, because that's too system dependent. */ int uuconf_hdb_system_unknown (pglobal, qsys) pointer pglobal; struct uuconf_system *qsys; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; boolean ffirst; struct shpermissions *qperm; struct uuconf_system *qalt; if (! qglobal->qprocess->fhdb_read_permissions) { iret = _uuconf_ihread_permissions (qglobal); if (iret != UUCONF_SUCCESS) return iret; } _uuconf_uclear_system (qsys); qsys->uuconf_palloc = uuconf_malloc_block (); if (qsys->uuconf_palloc == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } ffirst = TRUE; for (qperm = qglobal->qprocess->qhdb_permissions; qperm != NULL; qperm = qperm->qnext) { char **pz; if (qperm->pzlogname == NULL || qperm->pzlogname == (char **) &_uuconf_unset) continue; for (pz = qperm->pzlogname; *pz != NULL; pz++) { if (ffirst) { qalt = qsys; ffirst = FALSE; } else { struct uuconf_system **pq; qalt = ((struct uuconf_system *) uuconf_malloc (qsys->uuconf_palloc, sizeof (struct uuconf_system))); if (qalt == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } _uuconf_uclear_system (qalt); for (pq = &qsys->uuconf_qalternate; *pq != NULL; pq = &(*pq)->uuconf_qalternate) ; *pq = qalt; } /* We recognize LOGNAME=OTHER specially, although this appears to be an SCO innovation. */ if (strcmp (*pz, "OTHER") == 0) qalt->uuconf_zcalled_login = (char *) "ANY"; else qalt->uuconf_zcalled_login = *pz; qalt->uuconf_fcall = FALSE; qsys->uuconf_fcalled = TRUE; if (qperm->frequest >= 0) qsys->uuconf_fsend_request = qperm->frequest; else qsys->uuconf_fsend_request = FALSE; qsys->uuconf_fcalled_transfer = qperm->fsendfiles; qsys->uuconf_pzremote_send = qperm->pzread; qsys->uuconf_pzremote_receive = qperm->pzwrite; qsys->uuconf_fcallback = qperm->fcallback; qsys->uuconf_zlocalname = qperm->zmyname; qsys->uuconf_zpubdir = qperm->zpubdir; } } if (ffirst) return UUCONF_NOT_FOUND; /* HDB permits local requests to receive to any directory, which is not the default put in by _uuconf_isystem_basic_default. We set it here instead. */ for (qalt = qsys; qalt != NULL; qalt = qalt->uuconf_qalternate) { iret = _uuconf_iadd_string (qglobal, (char *) ZROOTDIR, FALSE, FALSE, &qalt->uuconf_pzlocal_receive, qsys->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; } return _uuconf_isystem_basic_default (qglobal, qsys); } uucp-1.07/uuconf/iniglb.c0000664000076400007640000001242007665321761011033 /* iniglb.c Initialize the global information structure. Copyright (C) 1992, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_iniglb_rcsid[] = "$Id: iniglb.c,v 1.14 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Initialize the global information structure. */ int _uuconf_iinit_global (pqglobal) struct sglobal **pqglobal; { pointer pblock; register struct sprocess *qprocess; char *azargs[3]; int iret; pblock = uuconf_malloc_block (); if (pblock == NULL) return UUCONF_MALLOC_FAILED; *pqglobal = (struct sglobal *) uuconf_malloc (pblock, sizeof (struct sglobal)); if (*pqglobal == NULL) { uuconf_free_block (pblock); return UUCONF_MALLOC_FAILED; } (*pqglobal)->qprocess = ((struct sprocess *) uuconf_malloc (pblock, sizeof (struct sprocess))); if ((*pqglobal)->qprocess == NULL) { uuconf_free_block (pblock); *pqglobal = NULL; return UUCONF_MALLOC_FAILED; } (*pqglobal)->pblock = pblock; (*pqglobal)->ierrno = 0; (*pqglobal)->ilineno = 0; (*pqglobal)->zfilename = NULL; qprocess = (*pqglobal)->qprocess; qprocess->zlocalname = NULL; qprocess->zspooldir = SPOOLDIR; qprocess->zpubdir = PUBDIR; #ifdef LOCKDIR qprocess->zlockdir = LOCKDIR; #else qprocess->zlockdir = SPOOLDIR; #endif qprocess->zlogfile = LOGFILE; qprocess->zstatsfile = STATFILE; qprocess->zdebugfile = DEBUGFILE; qprocess->zdebug = ""; qprocess->fstrip_login = TRUE; qprocess->fstrip_proto = TRUE; qprocess->cmaxuuxqts = 0; qprocess->zrunuuxqt = NULL; qprocess->fv2 = TRUE; qprocess->fhdb = TRUE; qprocess->pzdialcodefiles = NULL; qprocess->pztimetables = NULL; qprocess->zconfigfile = NULL; qprocess->pzsysfiles = NULL; qprocess->pzportfiles = NULL; qprocess->pzdialfiles = NULL; qprocess->pzpwdfiles = NULL; qprocess->pzcallfiles = NULL; qprocess->qunknown = NULL; qprocess->fread_syslocs = FALSE; qprocess->qsyslocs = NULL; qprocess->qvalidate = NULL; qprocess->fuses_myname = FALSE; qprocess->zv2systems = NULL; qprocess->zv2devices = NULL; qprocess->zv2userfile = NULL; qprocess->zv2cmds = NULL; qprocess->pzhdb_systems = NULL; qprocess->pzhdb_devices = NULL; qprocess->pzhdb_dialers = NULL; qprocess->fhdb_read_permissions = FALSE; qprocess->qhdb_permissions = NULL; azargs[0] = NULL; azargs[1] = (char *) "Evening"; azargs[2] = (char *) "Wk1705-0755,Sa,Su"; iret = _uuconf_itimetable ((pointer) *pqglobal, 3, azargs, (pointer) NULL, (pointer) NULL); if (UUCONF_ERROR_VALUE (iret) == UUCONF_SUCCESS) { azargs[1] = (char *) "Night"; azargs[2] = (char *) "Wk2305-0755,Sa,Su2305-1655"; iret = _uuconf_itimetable ((pointer) *pqglobal, 3, azargs, (pointer) NULL, (pointer) NULL); } if (UUCONF_ERROR_VALUE (iret) == UUCONF_SUCCESS) { azargs[1] = (char *) "NonPeak"; azargs[2] = (char *) "Wk1805-0655,Sa,Su"; iret = _uuconf_itimetable ((pointer) *pqglobal, 3, azargs, (pointer) NULL, (pointer) NULL); } if (UUCONF_ERROR_VALUE (iret) != UUCONF_SUCCESS) { uuconf_free_block (pblock); *pqglobal = NULL; /* Strip off any special bits, since there's no global structure. */ return UUCONF_ERROR_VALUE (iret); } return UUCONF_SUCCESS; } /* Add a timetable. This is also called by the Taylor UUCP initialization code, as well as by the Taylor UUCP sys file code (although the latter is obsolete). There's no point in putting this in a separate file, since everything must call _uuconf_init_global. There is a race condition here if this is called by two different threads on a sys file command, but the sys file command is obsolete anyhow. */ /*ARGSUSED*/ int _uuconf_itimetable (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; iret = _uuconf_iadd_string (qglobal, argv[1], FALSE, FALSE, &qglobal->qprocess->pztimetables, qglobal->pblock); if (iret != UUCONF_SUCCESS) return iret | UUCONF_CMDTABRET_EXIT; iret = _uuconf_iadd_string (qglobal, argv[2], FALSE, FALSE, &qglobal->qprocess->pztimetables, qglobal->pblock); if (iret != UUCONF_SUCCESS) return iret | UUCONF_CMDTABRET_EXIT; return UUCONF_CMDTABRET_KEEP; } uucp-1.07/uuconf/init.c0000664000076400007640000000373007665321761010536 /* init.c Initialize for reading UUCP configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_init_rcsid[] = "$Id: init.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif /* Initialize the UUCP configuration file reading routines. This is just a generic routine which calls the type specific routines. */ /*ARGSUSED*/ int uuconf_init (ppglobal, zprogram, zname) pointer *ppglobal; const char *zprogram; const char *zname; { struct sglobal **pqglob = (struct sglobal **) ppglobal; int iret; iret = UUCONF_NOT_FOUND; *pqglob = NULL; #if HAVE_TAYLOR_CONFIG iret = uuconf_taylor_init (ppglobal, zprogram, zname); if (iret != UUCONF_SUCCESS) return iret; #endif #if HAVE_V2_CONFIG if (*pqglob == NULL || (*pqglob)->qprocess->fv2) { iret = uuconf_v2_init (ppglobal); if (iret != UUCONF_SUCCESS) return iret; } #endif #if HAVE_HDB_CONFIG if (*pqglob == NULL || (*pqglob)->qprocess->fhdb) { iret = uuconf_hdb_init (ppglobal, zprogram); if (iret != UUCONF_SUCCESS) return iret; } #endif return iret; } uucp-1.07/uuconf/int.c0000664000076400007640000000340507665321761010364 /* int.c Parse a string into an int or a long. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_int_rcsid[] = "$Id: int.c,v 1.9 2002/03/05 19:10:42 ian Rel $"; #endif /* Parse a string into a variable. This is called by uuconf_cmd_args, as well as other functions. The parsing is done in a single place to make it easy to change. This should return an error code, including both UUCONF_CMDTABRET_KEEP and UUCONF_CMDTABRET_EXIT if appropriate. */ /*ARGSIGNORED*/ int _uuconf_iint (qglobal, zval, p, fint) struct sglobal *qglobal ATTRIBUTE_UNUSED; const char *zval; pointer p; boolean fint; { long i; char *zend; i = strtol ((char *) zval, &zend, 10); if (*zend != '\0') return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; if (fint) *(int *) p = (int) i; else *(long *) p = i; return UUCONF_CMDTABRET_CONTINUE; } uucp-1.07/uuconf/lckdir.c0000664000076400007640000000251207665321761011040 /* lckdir.c Get the name of the UUCP lock directory. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_lckdir_rcsid[] = "$Id: lckdir.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* Get the name of the UUCP lock directory. */ int uuconf_lockdir (pglobal, pzlock) pointer pglobal; const char **pzlock; { struct sglobal *qglobal = (struct sglobal *) pglobal; *pzlock = qglobal->qprocess->zlockdir; return UUCONF_SUCCESS; } uucp-1.07/uuconf/lineno.c0000664000076400007640000000243607665321761011061 /* lineno.c Return the saved line number. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_lineno_rcsid[] = "$Id: lineno.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* Return the saved line number. */ int uuconf_error_lineno (pglobal) pointer pglobal; { struct sglobal *qglobal = (struct sglobal *) pglobal; if (qglobal == NULL) return 0; else return qglobal->ilineno; } uucp-1.07/uuconf/llocnm.c0000664000076400007640000000374207665321761011062 /* llocnm.c Get the local name to use, given a login name. Copyright (C) 1992, 1993 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_llocnm_rcsid[] = "$Id: llocnm.c,v 1.9 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Get the local name to use, given a login name. */ int uuconf_login_localname (pglobal, zlogin, pzname) pointer pglobal; const char *zlogin; char **pzname; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; #if HAVE_TAYLOR_CONFIG iret = uuconf_taylor_login_localname (pglobal, zlogin, pzname); if (iret != UUCONF_NOT_FOUND) return iret; #endif #if HAVE_HDB_CONFIG if (qglobal->qprocess->fhdb) { iret = uuconf_hdb_login_localname (pglobal, zlogin, pzname); if (iret != UUCONF_NOT_FOUND) return iret; } #endif if (qglobal->qprocess->zlocalname != NULL) { *pzname = strdup ((char *) qglobal->qprocess->zlocalname); if (*pzname == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } return UUCONF_SUCCESS; } *pzname = NULL; return UUCONF_NOT_FOUND; } uucp-1.07/uuconf/local.c0000664000076400007640000000417107665321761010665 /* local.c Get default information for the local system. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_local_rcsid[] = "$Id: local.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Get default information about the local system. */ int uuconf_system_local (pglobal, qsys) pointer pglobal; struct uuconf_system *qsys; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; _uuconf_uclear_system (qsys); qsys->uuconf_palloc = uuconf_malloc_block (); if (qsys->uuconf_palloc == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } qsys->uuconf_zname = (char *) qglobal->qprocess->zlocalname; /* By default, we permit the local system to forward to and from any system. */ iret = _uuconf_iadd_string (qglobal, (char *) "ANY", FALSE, FALSE, &qsys->uuconf_pzforward_from, qsys->uuconf_palloc); if (iret == UUCONF_SUCCESS) iret = _uuconf_iadd_string (qglobal, (char *) "ANY", FALSE, FALSE, &qsys->uuconf_pzforward_to, qsys->uuconf_palloc); if (iret != UUCONF_SUCCESS) { uuconf_free_block (qsys->uuconf_palloc); return iret; } return _uuconf_isystem_basic_default (qglobal, qsys); } uucp-1.07/uuconf/locnm.c0000664000076400007640000000255007665321761010702 /* locnm.c Get the local node name. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_locnm_rcsid[] = "$Id: locnm.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif /* Get the local node name. */ int uuconf_localname (pglobal, pzname) pointer pglobal; const char **pzname; { struct sglobal *qglobal = (struct sglobal *) pglobal; *pzname = qglobal->qprocess->zlocalname; if (*pzname != NULL) return UUCONF_SUCCESS; else return UUCONF_NOT_FOUND; } uucp-1.07/uuconf/logfil.c0000664000076400007640000000247307665321761011052 /* logfil.c Get the name of the UUCP log file. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_logfil_rcsid[] = "$Id: logfil.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* Get the name of the UUCP log file. */ int uuconf_logfile (pglobal, pzlog) pointer pglobal; const char **pzlog; { struct sglobal *qglobal = (struct sglobal *) pglobal; *pzlog = qglobal->qprocess->zlogfile; return UUCONF_SUCCESS; } uucp-1.07/uuconf/maxuxq.c0000664000076400007640000000446507665321761011124 /* maxuxq.c Get the maximum number of simultaneous uuxqt executions. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_maxuxq_rcsid[] = "$Id: maxuxq.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif /* Get the maximum number of simultaneous uuxqt executions. When using TAYLOR_CONFIG, this is from the ``max-uuxqts'' command in config. Otherwise, when using HDB_CONFIG, we read the file Maxuuxqts. */ int uuconf_maxuuxqts (pglobal, pcmax) pointer pglobal; int *pcmax; { #if HAVE_TAYLOR_CONFIG { struct sglobal *qglobal = (struct sglobal *) pglobal; *pcmax = qglobal->qprocess->cmaxuuxqts; return UUCONF_SUCCESS; } #else /* ! HAVE_TAYLOR_CONFIG */ #if HAVE_HDB_CONFIG { char ab[sizeof OLDCONFIGLIB + sizeof HDB_MAXUUXQTS - 1]; FILE *e; *pcmax = 0; memcpy ((pointer) ab, (constpointer) OLDCONFIGLIB, sizeof OLDCONFIGLIB - 1); memcpy ((pointer) (ab + sizeof OLDCONFIGLIB - 1), (constpointer) HDB_MAXUUXQTS, sizeof HDB_MAXUUXQTS); e = fopen (ab, "r"); if (e != NULL) { char *z; size_t c; z = NULL; c = 0; if (getline (&z, &c, e) > 0) { *pcmax = (int) strtol (z, (char **) NULL, 10); if (*pcmax < 0) *pcmax = 0; free ((pointer) z); } (void) fclose (e); } return UUCONF_SUCCESS; } #else /* ! HAVE_HDB_CONFIG */ *pcmax = 0; return UUCONF_SUCCESS; #endif /* ! HAVE_HDB_CONFIG */ #endif /* ! HAVE_TAYLOR_CONFIG */ } uucp-1.07/uuconf/mrgblk.c0000664000076400007640000000271007665321761011046 /* mrgblk.c Merge two memory blocks together. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_mrgblk_rcsid[] = "$Id: mrgblk.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif #include "alloc.h" /* Merge one memory block into another one, returning the combined memory block. */ pointer _uuconf_pmalloc_block_merge (p1, p2) pointer p1; pointer p2; { struct sblock *q1 = (struct sblock *) p1; struct sblock *q2 = (struct sblock *) p2; struct sblock **pq; for (pq = &q1; *pq != NULL; pq = &(*pq)->qnext) ; *pq = q2; return (pointer) q1; } uucp-1.07/uuconf/paramc.c0000664000076400007640000001104507665321761011034 /* paramc.c Handle protocol-parameter commands. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_paramc_rcsid[] = "$Id: paramc.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Handle protocol-parameter commands by inserting them into an array of structures. The return value may include UUCONF_CMDTABRET_KEEP and UUCONF_CMDTABRET_EXIT, if appropriate. */ int _uuconf_iadd_proto_param (qglobal, argc, argv, pqparam, pblock) struct sglobal *qglobal; int argc; char **argv; struct uuconf_proto_param **pqparam; pointer pblock; { struct uuconf_proto_param *q; size_t c; struct uuconf_proto_param_entry *qentry; if (argc < 2) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; /* The first argument is the protocol character. */ if (argv[0][1] != '\0') return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; if (*pqparam == NULL) { *pqparam = ((struct uuconf_proto_param *) uuconf_malloc (pblock, 2 * sizeof (struct uuconf_proto_param))); if (*pqparam == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } (*pqparam)[1].uuconf_bproto = '\0'; q = *pqparam; q->uuconf_bproto = argv[0][0]; q->uuconf_qentries = NULL; } else { c = 0; for (q = *pqparam; q->uuconf_bproto != '\0'; q++) { if (q->uuconf_bproto == argv[0][0]) break; ++c; } if (q->uuconf_bproto == '\0') { struct uuconf_proto_param *qnew; qnew = ((struct uuconf_proto_param *) uuconf_malloc (pblock, ((c + 2) * sizeof (struct uuconf_proto_param)))); if (qnew == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } memcpy ((pointer) qnew, (pointer) *pqparam, c * sizeof (struct uuconf_proto_param)); qnew[c + 1].uuconf_bproto = '\0'; uuconf_free (pblock, *pqparam); *pqparam = qnew; q = qnew + c; q->uuconf_bproto = argv[0][0]; q->uuconf_qentries = NULL; } } if (q->uuconf_qentries == NULL) { qentry = ((struct uuconf_proto_param_entry *) uuconf_malloc (pblock, 2 * sizeof (struct uuconf_proto_param_entry))); if (qentry == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } qentry[1].uuconf_cargs = 0; q->uuconf_qentries = qentry; } else { struct uuconf_proto_param_entry *qnewent; c = 0; for (qentry = q->uuconf_qentries; qentry->uuconf_cargs != 0; qentry++) ++c; qnewent = ((struct uuconf_proto_param_entry *) uuconf_malloc (pblock, ((c + 2) * sizeof (struct uuconf_proto_param_entry)))); if (qnewent == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } memcpy ((pointer) qnewent, (pointer) q->uuconf_qentries, c * sizeof (struct uuconf_proto_param_entry)); qnewent[c + 1].uuconf_cargs = 0; uuconf_free (pblock, q->uuconf_qentries); q->uuconf_qentries = qnewent; qentry = qnewent + c; } qentry->uuconf_cargs = argc - 1; qentry->uuconf_pzargs = (char **) uuconf_malloc (pblock, ((argc - 1) * sizeof (char *))); if (qentry->uuconf_pzargs == NULL) { qglobal->ierrno = errno; qentry->uuconf_cargs = 0; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } memcpy ((pointer) qentry->uuconf_pzargs, (pointer) (argv + 1), (argc - 1) * sizeof (char *)); return UUCONF_CMDTABRET_KEEP; } uucp-1.07/uuconf/port.c0000664000076400007640000000414207665321761010555 /* port.c Find a port. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_port_rcsid[] = "$Id: port.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif /* Find a port by name, baud rate, and special purpose function. */ int uuconf_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, qport) pointer pglobal; const char *zname; long ibaud; long ihighbaud; int (*pifn) P((struct uuconf_port *, pointer)); pointer pinfo; struct uuconf_port *qport; { #if HAVE_V2_CONFIG || HAVE_HDB_CONFIG struct sglobal *qglobal = (struct sglobal *) pglobal; #endif int iret; #if HAVE_TAYLOR_CONFIG iret = uuconf_taylor_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, qport); if (iret != UUCONF_NOT_FOUND) return iret; #endif #if HAVE_V2_CONFIG if (qglobal->qprocess->fv2) { iret = uuconf_v2_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, qport); if (iret != UUCONF_NOT_FOUND) return iret; } #endif #if HAVE_HDB_CONFIG if (qglobal->qprocess->fhdb) { iret = uuconf_hdb_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, qport); if (iret != UUCONF_NOT_FOUND) return iret; } #endif return UUCONF_NOT_FOUND; } uucp-1.07/uuconf/prtsub.c0000664000076400007640000000344307665321761011113 /* prtsub.c Port information subroutines. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_prtsub_rcsid[] = "$Id: prtsub.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif /* Clear the information in a port. This can only clear the type independent information; the port type specific information is cleared when the type of the port is set. */ void _uuconf_uclear_port (qport) struct uuconf_port *qport; { qport->uuconf_zname = NULL; qport->uuconf_ttype = UUCONF_PORTTYPE_UNKNOWN; qport->uuconf_zprotocols = NULL; qport->uuconf_qproto_params = NULL; /* Note that we do not set RELIABLE_SPECIFIED; this just sets defaults, so that ``seven-bit true'' does not imply ``reliable false''. */ qport->uuconf_ireliable = (UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX); qport->uuconf_zlockname = NULL; qport->uuconf_palloc = NULL; } uucp-1.07/uuconf/pubdir.c0000664000076400007640000000251107665321761011054 /* pubdir.c Get the name of the UUCP public directory. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_pubdir_rcsid[] = "$Id: pubdir.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif /* Get the name of the UUCP public directory. */ int uuconf_pubdir (pglobal, pzpub) pointer pglobal; const char **pzpub; { struct sglobal *qglobal = (struct sglobal *) pglobal; *pzpub = qglobal->qprocess->zpubdir; return UUCONF_SUCCESS; } uucp-1.07/uuconf/rdlocs.c0000664000076400007640000001775107665321761011071 /* rdlocs.c Get the locations of systems in the Taylor UUCP configuration files. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_rdlocs_rcsid[] = "$Id: rdlocs.c,v 1.10 2002/03/05 19:10:42 ian Rel $"; #endif #include static int itsystem P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int itcalled_login P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int itmyname P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); /* This code scans through the Taylor UUCP system files in order to locate each system and to gather the login restrictions (since this information is held in additional arguments to the "called-login" command, it can appear anywhere in the systems files). It also records whether any "myname" appears, as an optimization for uuconf_taylor_localname. This table is used to dispatch the appropriate commands. Most commands are simply ignored. Note that this is a uuconf_cmdtab, not a cmdtab_offset. */ static const struct uuconf_cmdtab asTcmds[] = { { "system", UUCONF_CMDTABTYPE_FN | 2, NULL, itsystem }, { "alias", UUCONF_CMDTABTYPE_FN | 2, (pointer) asTcmds, itsystem }, { "called-login", UUCONF_CMDTABTYPE_FN | 0, NULL, itcalled_login }, { "myname", UUCONF_CMDTABTYPE_FN | 2, NULL, itmyname }, { NULL, 0, NULL, NULL } }; /* This structure is used to pass information into the command table functions. */ struct sinfo { /* The sys file name. */ const char *zname; /* The open sys file. */ FILE *e; /* The list of locations we are building. */ struct stsysloc *qlocs; /* The list of validation restrictions we are building. */ struct svalidate *qvals; }; /* Look through the sys files to find the location and names of all the systems. Since we're scanning the sys files, we also record the validation information specified by the additional arguments to the called-login command. We don't use uuconf_cmd_file to avoid the overhead of breaking the line up into arguments if not necessary. */ int _uuconf_iread_locations (qglobal) struct sglobal *qglobal; { char *zline; size_t cline; struct sinfo si; int iret; char **pz; if (qglobal->qprocess->fread_syslocs) return UUCONF_SUCCESS; zline = NULL; cline = 0; si.qlocs = NULL; si.qvals = NULL; iret = UUCONF_SUCCESS; for (pz = qglobal->qprocess->pzsysfiles; *pz != NULL; pz++) { FILE *e; int cchars; qglobal->ilineno = 0; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } #ifdef CLOSE_ON_EXEC CLOSE_ON_EXEC (e); #endif si.zname = *pz; si.e = e; while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) { char *zcmd; ++qglobal->ilineno; zcmd = zline + strspn (zline, " \t"); if (strncasecmp (zcmd, "system", sizeof "system" - 1) == 0 || strncasecmp (zcmd, "alias", sizeof "alias" - 1) == 0 || strncasecmp (zcmd, "called-login", sizeof "called-login" - 1) == 0 || strncasecmp (zcmd, "myname", sizeof "myname" - 1) == 0) { iret = uuconf_cmd_line ((pointer) qglobal, zline, asTcmds, (pointer) &si, (uuconf_cmdtabfn) NULL, 0, qglobal->pblock); if ((iret & UUCONF_CMDTABRET_KEEP) != 0) { iret &=~ UUCONF_CMDTABRET_KEEP; zline = NULL; cline = 0; } if (iret != UUCONF_SUCCESS) { iret &=~ UUCONF_CMDTABRET_EXIT; break; } } } if (iret != UUCONF_SUCCESS) break; } if (zline != NULL) free ((pointer) zline); if (iret != UUCONF_SUCCESS) { qglobal->zfilename = *pz; iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; if (UUCONF_ERROR_VALUE (iret) != UUCONF_MALLOC_FAILED) qglobal->qprocess->fread_syslocs = TRUE; } else { qglobal->qprocess->qsyslocs = si.qlocs; qglobal->qprocess->qvalidate = si.qvals; qglobal->qprocess->fread_syslocs = TRUE; } return iret; } /* Handle a "system" or "alias" command by recording the file and location. If pvar is not NULL, this is an "alias" command. */ /*ARGSUSED*/ static int itsystem (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; struct stsysloc *q; size_t csize; q = (struct stsysloc *) uuconf_malloc (qglobal->pblock, sizeof (struct stsysloc)); if (q == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } csize = strlen (argv[1]) + 1; q->zname = uuconf_malloc (qglobal->pblock, csize); if (q->zname == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } q->qnext = qinfo->qlocs; memcpy ((pointer) q->zname, (pointer) argv[1], csize); q->falias = pvar != NULL; q->zfile = qinfo->zname; q->e = qinfo->e; q->iloc = ftell (qinfo->e); q->ilineno = qglobal->ilineno; qinfo->qlocs = q; return UUCONF_CMDTABRET_CONTINUE; } /* Handle the "called-login" command. This just records any extra arguments, so that uuconf_validate can check them later if necessary. */ /*ARGSUSED*/ static int itcalled_login (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; register struct svalidate *qval; int i; if (argc <= 2) return UUCONF_CMDTABRET_CONTINUE; for (qval = qinfo->qvals; qval != NULL; qval = qval->qnext) if (strcmp (argv[1], qval->zlogname) == 0) break; if (qval == NULL) { qval = (struct svalidate *) uuconf_malloc (qglobal->pblock, sizeof (struct svalidate)); if (qval == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } qval->qnext = qinfo->qvals; qval->zlogname = argv[1]; qval->pzmachines = NULL; qinfo->qvals = qval; } for (i = 2; i < argc; i++) { int iret; iret = _uuconf_iadd_string (qglobal, argv[i], FALSE, TRUE, &qval->pzmachines, qglobal->pblock); if (iret != UUCONF_SUCCESS) return iret | UUCONF_CMDTABRET_EXIT; } return UUCONF_CMDTABRET_KEEP; } /* Handle the "myname" command by simply recording that it appears. This information is used by uuconf_taylor_localname. */ /*ARGSUSED*/ static int itmyname (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; qglobal->qprocess->fuses_myname = TRUE; return UUCONF_CMDTABRET_CONTINUE; } uucp-1.07/uuconf/rdperm.c0000664000076400007640000002723407665321762011072 /* rdperm.c Read the HDB Permissions file. Copyright (C) 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_rdperm_rcsid[] = "$Id: rdperm.c,v 1.12 2002/03/05 19:10:42 ian Rel $"; #endif #include #include static int ihcolon P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int ihsendfiles P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int ihunknownperm P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int ihadd_norw P((struct sglobal *qglobal, char ***ppz, char **pzno)); /* These routines reads in the HDB Permissions file. We store the entries in a linked list of shpermissions structures, so we only have to actually read the file once. */ /* This command table and static structure are used to parse a line from Permissions. The entries are parsed as follows: Multiple strings separated by colons: LOGNAME, MACHINE, READ, WRITE, NOREAD, NOWRITE, COMMANDS, VALIDATE, ALIAS. Boolean values: REQUEST, CALLBACK. Simple strings: MYNAME, PUBDIR. "Yes" or "call": SENDFILES. The NOREAD and NOWRITE entries are merged into the READ and WRITE entries, rather than being permanently stored. They are handled specially in the uuconf_cmdtab table. */ static const struct cmdtab_offset asHperm_cmds[] = { { "NOREAD", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, ihcolon }, { "NOWRITE", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, ihcolon }, { "LOGNAME", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct shpermissions, pzlogname), ihcolon }, { "MACHINE", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct shpermissions, pzmachine), ihcolon }, { "REQUEST", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct shpermissions, frequest), NULL }, { "SENDFILES", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct shpermissions, fsendfiles), ihsendfiles }, { "READ", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct shpermissions, pzread), ihcolon }, { "WRITE", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct shpermissions, pzwrite), ihcolon }, { "CALLBACK", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct shpermissions, fcallback), NULL }, { "COMMANDS", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct shpermissions, pzcommands), ihcolon }, { "VALIDATE", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct shpermissions, pzvalidate), ihcolon }, { "MYNAME", UUCONF_CMDTABTYPE_STRING, offsetof (struct shpermissions, zmyname), NULL }, { "PUBDIR", UUCONF_CMDTABTYPE_STRING, offsetof (struct shpermissions, zpubdir), NULL }, { "ALIAS", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct shpermissions, pzalias), ihcolon }, { NULL, 0, 0, NULL } }; #define CHPERM_CMDS (sizeof asHperm_cmds / sizeof asHperm_cmds[0]) /* Actually read the Permissions file into a linked list of structures. */ int _uuconf_ihread_permissions (qglobal) struct sglobal *qglobal; { char *zperm; FILE *e; int iret; struct uuconf_cmdtab as[CHPERM_CMDS]; char **pznoread, **pznowrite; struct shpermissions shperm; char *zline; size_t cline; char **pzsplit; size_t csplit; int cchars; struct shpermissions *qlist, **pq; if (qglobal->qprocess->fhdb_read_permissions) return UUCONF_SUCCESS; zperm = (char *) uuconf_malloc (qglobal->pblock, (sizeof OLDCONFIGLIB + sizeof HDB_PERMISSIONS - 1)); if (zperm == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) zperm, (pointer) OLDCONFIGLIB, sizeof OLDCONFIGLIB - 1); memcpy ((pointer) (zperm + sizeof OLDCONFIGLIB - 1), (pointer) HDB_PERMISSIONS, sizeof HDB_PERMISSIONS); e = fopen (zperm, "r"); if (e == NULL) { uuconf_free (qglobal->pblock, zperm); qglobal->qprocess->fhdb_read_permissions = TRUE; return UUCONF_SUCCESS; } _uuconf_ucmdtab_base (asHperm_cmds, CHPERM_CMDS, (char *) &shperm, as); as[0].uuconf_pvar = (pointer) &pznoread; as[1].uuconf_pvar = (pointer) &pznowrite; zline = NULL; cline = 0; pzsplit = NULL; csplit = 0; qlist = NULL; pq = &qlist; qglobal->ilineno = 0; iret = UUCONF_SUCCESS; while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) { int centries; struct shpermissions *qnew; int i; ++qglobal->ilineno; --cchars; if (zline[cchars] == '\n') zline[cchars] = '\0'; if (zline[0] == '#') continue; centries = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); if (centries < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } if (centries == 0) continue; shperm.pzlogname = (char **) &_uuconf_unset; shperm.pzmachine = (char **) &_uuconf_unset; shperm.frequest = -1; shperm.fsendfiles = -1; shperm.pzread = (char **) &_uuconf_unset; shperm.pzwrite = (char **) &_uuconf_unset; shperm.fcallback = -1; shperm.pzcommands = (char **) &_uuconf_unset; shperm.pzvalidate = (char **) &_uuconf_unset; shperm.zmyname = (char *) &_uuconf_unset; shperm.zpubdir = (char *) &_uuconf_unset; shperm.pzalias = (char **) &_uuconf_unset; pznoread = (char **) &_uuconf_unset; pznowrite = (char **) &_uuconf_unset; for (i = 0; i < centries; i++) { char *zeq; char *azargs[2]; zeq = strchr (pzsplit[i], '='); if (zeq == NULL) { iret = UUCONF_SYNTAX_ERROR; qglobal->qprocess->fhdb_read_permissions = TRUE; break; } *zeq = '\0'; azargs[0] = pzsplit[i]; azargs[1] = zeq + 1; iret = uuconf_cmd_args (qglobal, 2, azargs, as, (pointer) NULL, ihunknownperm, 0, qglobal->pblock); if ((iret & UUCONF_CMDTABRET_KEEP) != 0) { iret &=~ UUCONF_CMDTABRET_KEEP; if (uuconf_add_block (qglobal->pblock, zline) != 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } zline = NULL; cline = 0; } if ((iret & UUCONF_CMDTABRET_EXIT) != 0) { iret &=~ UUCONF_CMDTABRET_EXIT; break; } } if (iret != UUCONF_SUCCESS) break; if (shperm.pzmachine == (char **) &_uuconf_unset && shperm.pzlogname == (char **) &_uuconf_unset) { iret = UUCONF_SYNTAX_ERROR; qglobal->qprocess->fhdb_read_permissions = TRUE; break; } /* Attach any NOREAD or NOWRITE entries to the corresponding READ or WRITE entries in the format expected for the pzlocal_receive, etc., fields in uuconf_system. */ if (pznoread != NULL) { iret = ihadd_norw (qglobal, &shperm.pzread, pznoread); if (iret != UUCONF_SUCCESS) break; uuconf_free (qglobal->pblock, pznoread); } if (pznowrite != NULL) { iret = ihadd_norw (qglobal, &shperm.pzwrite, pznowrite); if (iret != UUCONF_SUCCESS) break; uuconf_free (qglobal->pblock, pznowrite); } qnew = ((struct shpermissions *) uuconf_malloc (qglobal->pblock, sizeof (struct shpermissions))); if (qnew == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } *qnew = shperm; *pq = qnew; pq = &qnew->qnext; *pq = NULL; } (void) fclose (e); if (zline != NULL) free ((pointer) zline); if (pzsplit != NULL) free ((pointer) pzsplit); if (iret == UUCONF_SUCCESS) { qglobal->qprocess->qhdb_permissions = qlist; qglobal->qprocess->fhdb_read_permissions = TRUE; } else { qglobal->zfilename = zperm; iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; } return iret; } /* Split the argument into colon separated strings, and assign a NULL terminated array of strings to pvar. */ /*ARGSUSED*/ static int ihcolon (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; char ***ppz = (char ***) pvar; char **pzsplit; size_t csplit; int centries; int i; int iret; *ppz = NULL; pzsplit = NULL; csplit = 0; centries = _uuconf_istrsplit (argv[1], ':', &pzsplit, &csplit); if (centries < 0) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } if (centries == 0) { if (pzsplit != NULL) free ((pointer) pzsplit); return UUCONF_CMDTABRET_CONTINUE; } iret = UUCONF_SUCCESS; for (i = 0; i < centries; i++) { iret = _uuconf_iadd_string (qglobal, pzsplit[i], FALSE, FALSE, ppz, qglobal->pblock); if (iret != UUCONF_SUCCESS) { iret |= UUCONF_CMDTABRET_EXIT; break; } } free ((pointer) pzsplit); return UUCONF_CMDTABRET_KEEP; } /* Handle the SENDFILES parameter, which can take "yes" or "call" or "no" as an argument. The string "call" is equivalent to "no". */ /*ARGSUSED*/ static int ihsendfiles (pglobal, argc, argv, pvar, pinfo) pointer pglobal ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { int *pi = (int *) pvar; switch (argv[1][0]) { case 'C': case 'c': case 'N': case 'n': *pi = FALSE; break; case 'Y': case 'y': *pi = TRUE; break; default: return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; } return UUCONF_CMDTABRET_CONTINUE; } /* If there is an unknown Permissions entry, return a syntax error. This should probably be more clever. */ /*ARGSUSED*/ static int ihunknownperm (pglobal, argc, argv, pvar, pinfo) pointer pglobal ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; } /* Add a NOREAD or NOWRITE entry to a READ or WRITE entry. */ static int ihadd_norw (qglobal, ppz, pzno) struct sglobal *qglobal; char ***ppz; char **pzno; { register char **pz; if (pzno == (char **) &_uuconf_unset) return UUCONF_SUCCESS; for (pz = pzno; *pz != NULL; pz++) { size_t csize; char *znew; int iret; /* Ignore an attempt to say NOREAD or NOWRITE with an empty string, since it will be interpreted as an attempt to deny everything. */ if (**pz != '\0') { csize = strlen (*pz) + 1; znew = (char *) uuconf_malloc (qglobal->pblock, csize + 1); if (znew == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } znew[0] = '!'; memcpy ((pointer) (znew + 1), (pointer) *pz, csize); iret = _uuconf_iadd_string (qglobal, znew, FALSE, FALSE, ppz, qglobal->pblock); if (iret != UUCONF_SUCCESS) return iret; } } return UUCONF_SUCCESS; } uucp-1.07/uuconf/reliab.c0000664000076400007640000000644307665321762011036 /* reliab.c Subroutines to handle reliability commands for ports and dialers. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_reliab_rcsid[] = "$Id: reliab.c,v 1.9 2002/03/05 19:10:42 ian Rel $"; #endif /* Handle the "seven-bit" command for a port or a dialer. The pvar argument points to an integer which should be set to hold reliability information. */ /*ARGSUSED*/ int _uuconf_iseven_bit (pglobal,argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; int *pi = (int *) pvar; int fval; int iret; iret = _uuconf_iboolean (qglobal, argv[1], &fval); if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) return iret; *pi |= UUCONF_RELIABLE_SPECIFIED; if (fval) *pi &=~ UUCONF_RELIABLE_EIGHT; else *pi |= UUCONF_RELIABLE_EIGHT; return iret; } /* Handle the "reliable" command for a port or a dialer. The pvar argument points to an integer which should be set to hold reliability information. */ /*ARGSUSED*/ int _uuconf_ireliable (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; int *pi = (int *) pvar; int fval; int iret; iret = _uuconf_iboolean (qglobal, argv[1], &fval); if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) return iret; *pi |= UUCONF_RELIABLE_SPECIFIED; if (fval) *pi |= UUCONF_RELIABLE_RELIABLE; else *pi &=~ UUCONF_RELIABLE_RELIABLE; return iret; } /* Handle the "half-duplex" command for a port or a dialer. The pvar argument points to an integer which should be set to hold reliability information. */ /*ARGSUSED*/ int _uuconf_ihalf_duplex (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; int *pi = (int *) pvar; int fval; int iret; iret = _uuconf_iboolean (qglobal, argv[1], &fval); if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) return iret; *pi |= UUCONF_RELIABLE_SPECIFIED; if (fval) *pi &=~ UUCONF_RELIABLE_FULLDUPLEX; else *pi |= UUCONF_RELIABLE_FULLDUPLEX; return iret; } uucp-1.07/uuconf/remunk.c0000664000076400007640000000331107665321762011070 /* remunk.c Get the name of the remote.unknown shell script. Copyright (C) 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_remunk_rcsid[] = "$Id: remunk.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif /* Get the name of the remote.unknown shell script. */ /*ARGSUSED*/ int uuconf_remote_unknown (pglobal, pzname) pointer pglobal ATTRIBUTE_UNUSED; char **pzname ATTRIBUTE_UNUSED; { #if ! HAVE_HDB_CONFIG return UUCONF_NOT_FOUND; #else #if HAVE_TAYLOR_CONFIG struct sglobal *qglobal = (struct sglobal *) pglobal; /* If ``unknown'' commands were used in the config file, then ignore any remote.unknown script. */ if (qglobal->qprocess->qunknown != NULL) return UUCONF_NOT_FOUND; #endif /* HAVE_TAYLOR_CONFIG */ return uuconf_hdb_remote_unknown (pglobal, pzname); #endif /* HAVE_HDB_CONFIG */ } uucp-1.07/uuconf/runuxq.c0000664000076400007640000000472107665321762011137 /* maxuxq.c Return how often to spawn a uuxqt process. Copyright (C) 1994 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_runuxq_rcsid[] = "$Id: runuxq.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* Return how often to spawn a uuxqt process. This is either a positive number representing the number of execution files to be received between spawns, or a special code. When using TAYLOR_CONFIG, this is from the ``run-uuxqt'' command in config (the default is UUCONF_RUNUUXQT_ONCE, for compatibility). Otherwise, we return UUCONF_RUNUUXQT_PERCALL for HDB_CONFIG and 10 for V2_CONFIG, to emulate traditional HDB and V2 emulations. */ int uuconf_runuuxqt (pglobal, pirunuuxqt) pointer pglobal; int *pirunuuxqt; { #if HAVE_TAYLOR_CONFIG { struct sglobal *qglobal = (struct sglobal *) pglobal; const char *zrun; zrun = qglobal->qprocess->zrunuuxqt; if (zrun == NULL || strcasecmp (zrun, "once") == 0) *pirunuuxqt = UUCONF_RUNUUXQT_ONCE; else if (strcasecmp (zrun, "never") == 0) *pirunuuxqt = UUCONF_RUNUUXQT_NEVER; else if (strcasecmp (zrun, "percall") == 0) *pirunuuxqt = UUCONF_RUNUUXQT_PERCALL; else { char *zend; *pirunuuxqt = strtol ((char *) qglobal->qprocess->zrunuuxqt, &zend, 10); if (*zend != '\0' || *pirunuuxqt <= 0) *pirunuuxqt = UUCONF_RUNUUXQT_ONCE; } } #else /* ! HAVE_TAYLOR_CONFIG */ #if HAVE_HDB_CONFIG *pirunuuxqt = UUCONF_RUNUUXQT_PERCALL; #else /* ! HAVE_HDB_CONFIG */ *pirunuuxqt = 10; #endif /* ! HAVE_HDB_CONFIG */ #endif /* ! HAVE_TAYLOR_CONFIG */ return UUCONF_SUCCESS; } uucp-1.07/uuconf/sinfo.c0000664000076400007640000000537007665321762010714 /* sinfo.c Get information about a system. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_sinfo_rcsid[] = "$Id: sinfo.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* Get information about a particular system. We combine the definitions for this system from each type of configuration file, by passing what we have so far into each one. */ int uuconf_system_info (pglobal, zsystem, qsys) pointer pglobal; const char *zsystem; struct uuconf_system *qsys; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; boolean fgot; fgot = FALSE; #if HAVE_TAYLOR_CONFIG iret = _uuconf_itaylor_system_internal (qglobal, zsystem, qsys); if (iret == UUCONF_SUCCESS) fgot = TRUE; else if (iret != UUCONF_NOT_FOUND) return iret; #endif #if HAVE_V2_CONFIG if (qglobal->qprocess->fv2) { struct uuconf_system *q; struct uuconf_system sv2; if (fgot) q = &sv2; else q = qsys; iret = _uuconf_iv2_system_internal (qglobal, zsystem, q); if (iret == UUCONF_SUCCESS) { if (fgot) { iret = _uuconf_isystem_default (qglobal, qsys, &sv2, TRUE); if (iret != UUCONF_SUCCESS) return iret; } fgot = TRUE; } else if (iret != UUCONF_NOT_FOUND) return iret; } #endif #if HAVE_HDB_CONFIG if (qglobal->qprocess->fhdb) { struct uuconf_system *q; struct uuconf_system shdb; if (fgot) q = &shdb; else q = qsys; iret = _uuconf_ihdb_system_internal (qglobal, zsystem, q); if (iret == UUCONF_SUCCESS) { if (fgot) { iret = _uuconf_isystem_default (qglobal, qsys, &shdb, TRUE); if (iret != UUCONF_SUCCESS) return iret; } fgot = TRUE; } else if (iret != UUCONF_NOT_FOUND) return iret; } #endif if (! fgot) return UUCONF_NOT_FOUND; return _uuconf_isystem_basic_default (qglobal, qsys); } uucp-1.07/uuconf/snams.c0000664000076400007640000000630507665321762010716 /* snams.c Get all known system names. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_snams_rcsid[] = "$Id: snams.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* Get all known system names. */ int uuconf_system_names (pglobal, ppzsystems, falias) pointer pglobal; char ***ppzsystems; int falias; { struct sglobal *qglobal = (struct sglobal *) pglobal; char **pztaylor; char **pzv2; char **pzhdb; int iret; *ppzsystems = NULL; pztaylor = NULL; pzv2 = NULL; pzhdb = NULL; #if HAVE_TAYLOR_CONFIG iret = uuconf_taylor_system_names (pglobal, &pztaylor, falias); if (iret != UUCONF_SUCCESS) return iret; #endif #if HAVE_V2_CONFIG if (qglobal->qprocess->fv2) { iret = uuconf_v2_system_names (pglobal, &pzv2, falias); if (iret != UUCONF_SUCCESS) return iret; } #endif #if HAVE_HDB_CONFIG if (qglobal->qprocess->fhdb) { iret = uuconf_hdb_system_names (pglobal, &pzhdb, falias); if (iret != UUCONF_SUCCESS) return iret; } #endif if (pzv2 == NULL && pzhdb == NULL) *ppzsystems = pztaylor; else if (pztaylor == NULL && pzhdb == NULL) *ppzsystems = pzv2; else if (pztaylor == NULL && pzv2 == NULL) *ppzsystems = pzhdb; else { char **pz; iret = UUCONF_SUCCESS; if (pztaylor != NULL) { for (pz = pztaylor; *pz != NULL; pz++) { iret = _uuconf_iadd_string (qglobal, *pz, FALSE, TRUE, ppzsystems, (pointer) NULL); if (iret != UUCONF_SUCCESS) break; } } if (pzv2 != NULL && iret == UUCONF_SUCCESS) { for (pz = pzv2; *pz != NULL; pz++) { iret = _uuconf_iadd_string (qglobal, *pz, FALSE, TRUE, ppzsystems, (pointer) NULL); if (iret != UUCONF_SUCCESS) break; } } if (pzhdb != NULL && iret == UUCONF_SUCCESS) { for (pz = pzhdb; *pz != NULL; pz++) { iret = _uuconf_iadd_string (qglobal, *pz, FALSE, TRUE, ppzsystems, (pointer) NULL); if (iret != UUCONF_SUCCESS) break; } } if (pztaylor != NULL) free ((pointer) pztaylor); if (pzv2 != NULL) free ((pointer) pzv2); if (pzhdb != NULL) free ((pointer) pzhdb); } if (iret == UUCONF_SUCCESS && *ppzsystems == NULL) iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, ppzsystems, (pointer) NULL); return iret; } uucp-1.07/uuconf/split.c0000664000076400007640000000500307665321762010722 /* split.c Split a string into tokens. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_split_rcsid[] = "$Id: split.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif #include /* Split a string into tokens. The bsep argument is the separator to use. If it is the null byte, white space is used as the separator, and leading white space is discarded. Otherwise, each occurrence of the separator character delimits a field (and thus some fields may be empty). The array and size arguments may be used to reuse the same memory. This function is not tied to uuconf; the only way it can fail is if malloc or realloc fails. */ int _uuconf_istrsplit (zline, bsep, ppzsplit, pcsplit) register char *zline; int bsep; char ***ppzsplit; size_t *pcsplit; { size_t i; i = 0; while (TRUE) { if (bsep == '\0') { while (isspace (BUCHAR (*zline))) ++zline; if (*zline == '\0') break; } if (i >= *pcsplit) { char **pznew; size_t cnew; if (*pcsplit == 0) { cnew = 8; pznew = (char **) malloc (cnew * sizeof (char *)); } else { cnew = *pcsplit * 2; pznew = (char **) realloc ((pointer) *ppzsplit, cnew * sizeof (char *)); } if (pznew == NULL) return -1; *ppzsplit = pznew; *pcsplit = cnew; } (*ppzsplit)[i] = zline; ++i; if (bsep == '\0') { while (*zline != '\0' && ! isspace (BUCHAR (*zline))) ++zline; } else { while (*zline != '\0' && *zline != bsep) ++zline; } if (*zline == '\0') break; *zline++ = '\0'; } return i; } uucp-1.07/uuconf/spool.c0000664000076400007640000000251607665321762010731 /* spool.c Get the name of the UUCP spool directory. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_spool_rcsid[] = "$Id: spool.c,v 1.7 2002/03/05 19:10:42 ian Rel $"; #endif /* Get the name of the UUCP spool directory. */ int uuconf_spooldir (pglobal, pzspool) pointer pglobal; const char **pzspool; { struct sglobal *qglobal = (struct sglobal *) pglobal; *pzspool = qglobal->qprocess->zspooldir; return UUCONF_SUCCESS; } uucp-1.07/uuconf/stafil.c0000664000076400007640000000252307665321762011055 /* stafil.c Get the name of the UUCP statistics file. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_stafil_rcsid[] = "$Id: stafil.c,v 1.6 2002/03/05 19:10:42 ian Rel $"; #endif /* Get the name of the UUCP statistics file. */ int uuconf_statsfile (pglobal, pzstats) pointer pglobal; const char **pzstats; { struct sglobal *qglobal = (struct sglobal *) pglobal; *pzstats = qglobal->qprocess->zstatsfile; return UUCONF_SUCCESS; } uucp-1.07/uuconf/strip.c0000664000076400007640000000325407665321762010736 /* maxuxq.c Get information about what things should be stripped. Copyright (C) 1995 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_strip_rcsid[] = "$Id: strip.c,v 1.3 2002/03/05 19:10:42 ian Rel $"; #endif /* Get information about what types of global information should be stripped. There are currently only two, which we return as a couple of bits. We store them as two separate variables, so we don't need to have a special function to set the values correctly. */ int uuconf_strip (pglobal, pistrip) pointer pglobal; int *pistrip; { struct sglobal *qglobal = (struct sglobal *) pglobal; *pistrip = 0; if (qglobal->qprocess->fstrip_login) *pistrip |= UUCONF_STRIP_LOGIN; if (qglobal->qprocess->fstrip_proto) *pistrip |= UUCONF_STRIP_PROTO; return UUCONF_SUCCESS; } uucp-1.07/uuconf/syssub.c0000664000076400007640000003725507665321762011135 /* syssub.c System information subroutines. Copyright (C) 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_syssub_rcsid[] = "$Id: syssub.c,v 1.17 2002/03/05 19:10:42 ian Rel $"; #endif #include /* This macro operates on every string (char *) field in struct uuconf_system. */ #define SYSTEM_STRINGS(OP) \ do \ { \ OP (uuconf_zname); \ OP (uuconf_zalternate); \ OP (uuconf_zdebug); \ OP (uuconf_zmax_remote_debug); \ OP (uuconf_zphone); \ OP (uuconf_zcall_login); \ OP (uuconf_zcall_password); \ OP (uuconf_zcalled_login); \ OP (uuconf_zprotocols); \ OP (uuconf_zpubdir); \ OP (uuconf_zlocalname); \ } \ while (0) /* This macro operates on every string array (char **) field in struct uuconf_system. */ #define SYSTEM_STRING_ARRAYS(OP) \ do \ { \ OP (uuconf_pzalias); \ OP (uuconf_pzlocal_send); \ OP (uuconf_pzremote_send); \ OP (uuconf_pzlocal_receive); \ OP (uuconf_pzremote_receive); \ OP (uuconf_pzpath); \ OP (uuconf_pzcmds); \ OP (uuconf_pzforward_from); \ OP (uuconf_pzforward_to); \ OP (uuconf_schat.uuconf_pzchat); \ OP (uuconf_schat.uuconf_pzprogram); \ OP (uuconf_schat.uuconf_pzfail); \ OP (uuconf_scalled_chat.uuconf_pzchat); \ OP (uuconf_scalled_chat.uuconf_pzprogram); \ OP (uuconf_scalled_chat.uuconf_pzfail); \ } \ while (0) /* This macro operations on every timespan pointer (struct uuconf_timespan *) in struct uuconf_system. */ #define SYSTEM_TIMESPANS(OP) \ do \ { \ OP (uuconf_qtimegrade); \ OP (uuconf_qcalltimegrade); \ OP (uuconf_qcalledtimegrade); \ OP (uuconf_qcall_local_size); \ OP (uuconf_qcall_remote_size); \ OP (uuconf_qcalled_local_size); \ OP (uuconf_qcalled_remote_size); \ } \ while (0) /* This macro operates on every boolean value (of type int, although some type int are not boolean) field in uuconf_system. */ #define SYSTEM_BOOLEANS(OP) \ do \ { \ OP (uuconf_fcall); \ OP (uuconf_fcalled); \ OP (uuconf_fcallback); \ OP (uuconf_fsequence); \ OP (uuconf_fsend_request); \ OP (uuconf_frec_request); \ OP (uuconf_fcall_transfer); \ OP (uuconf_fcalled_transfer); \ OP (uuconf_schat.uuconf_fstrip); \ OP (uuconf_scalled_chat.uuconf_fstrip); \ } \ while (0) /* This macro operates on every generic integer (type int or long) in uuconf_system. */ #define SYSTEM_INTEGERS(OP) \ do \ { \ OP (uuconf_cmax_retries); \ OP (uuconf_csuccess_wait); \ OP (uuconf_ibaud); \ OP (uuconf_ihighbaud); \ OP (uuconf_cfree_space); \ OP (uuconf_schat.uuconf_ctimeout); \ OP (uuconf_scalled_chat.uuconf_ctimeout); \ OP (uuconf_cmax_file_time); \ } \ while (0) /* There is no macro for uuconf_qalternate, uuconf_zport, uuconf_qport, uuconf_qproto_params, or uuconf_palloc. */ /* Clear the contents of a struct uuconf_system. */ void _uuconf_uclear_system (q) struct uuconf_system *q; { #define CLEAR(x) q->x = (char *) &_uuconf_unset SYSTEM_STRINGS (CLEAR); #undef CLEAR #define CLEAR(x) q->x = (char **) &_uuconf_unset SYSTEM_STRING_ARRAYS (CLEAR); #undef CLEAR #define CLEAR(x) q->x = (struct uuconf_timespan *) &_uuconf_unset SYSTEM_TIMESPANS (CLEAR); #undef CLEAR #define CLEAR(x) q->x = -1 SYSTEM_BOOLEANS (CLEAR); SYSTEM_INTEGERS (CLEAR); #undef CLEAR q->uuconf_qalternate = NULL; q->uuconf_zport = (char *) &_uuconf_unset; q->uuconf_qport = (struct uuconf_port *) &_uuconf_unset; q->uuconf_qproto_params = (struct uuconf_proto_param *) &_uuconf_unset; q->uuconf_palloc = NULL; } /* Default the contents of one struct uuconf_system to the contents of another. This default alternate by alternate. Any additional alternates in q default to the last alternate of qdefault. If the faddalternates arguments is TRUE, additional alternates or qdefault are added to q; these alternates are copies of the first alternate of q, and defaults are set from the additional alternates of qdefault. */ int _uuconf_isystem_default (qglobal, qset, qdefault, faddalternates) struct sglobal *qglobal; struct uuconf_system *qset; struct uuconf_system *qdefault; boolean faddalternates; { struct uuconf_system *qalt; if (qset->uuconf_palloc != qdefault->uuconf_palloc) qset->uuconf_palloc = _uuconf_pmalloc_block_merge (qset->uuconf_palloc, qdefault->uuconf_palloc); /* If we are adding alternates from the default, make sure we have at least as many alternates in qset as we do in qdefault. Each new alternate we create gets initialized to the first alternate of the system. */ if (faddalternates) { struct uuconf_system **pq, *qdef; for (qdef = qdefault, pq = &qset; qdef != NULL; qdef = qdef->uuconf_qalternate, pq = &(*pq)->uuconf_qalternate) { if (*pq == NULL) { *pq = ((struct uuconf_system *) uuconf_malloc (qset->uuconf_palloc, sizeof (struct uuconf_system))); if (*pq == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } **pq = *qset; (*pq)->uuconf_qalternate = NULL; } } } for (qalt = qset; qalt != NULL; qalt = qalt->uuconf_qalternate) { #define DEFAULT(x) \ if (qalt->x == (char *) &_uuconf_unset) qalt->x = qdefault->x SYSTEM_STRINGS (DEFAULT); #undef DEFAULT #define DEFAULT(x) \ if (qalt->x == (char **) &_uuconf_unset) qalt->x = qdefault->x SYSTEM_STRING_ARRAYS (DEFAULT); #undef DEFAULT #define DEFAULT(x) \ if (qalt->x == (struct uuconf_timespan *) &_uuconf_unset) \ qalt->x = qdefault->x SYSTEM_TIMESPANS (DEFAULT); #undef DEFAULT #define DEFAULT(x) if (qalt->x < 0) qalt->x = qdefault->x SYSTEM_BOOLEANS (DEFAULT); SYSTEM_INTEGERS (DEFAULT); #undef DEFAULT /* We only copy over zport if both zport and qport are NULL, because otherwise a default zport would override a specific qport. */ if (qalt->uuconf_zport == (char *) &_uuconf_unset && qalt->uuconf_qport == (struct uuconf_port *) &_uuconf_unset) qalt->uuconf_zport = qdefault->uuconf_zport; if (qalt->uuconf_qport == (struct uuconf_port *) &_uuconf_unset) qalt->uuconf_qport = qdefault->uuconf_qport; if (qalt->uuconf_qproto_params == (struct uuconf_proto_param *) &_uuconf_unset) qalt->uuconf_qproto_params = qdefault->uuconf_qproto_params; else if (qdefault->uuconf_qproto_params != NULL) { int cnew, ca; struct uuconf_proto_param *qd, *qa; /* Merge in the default protocol parameters, so that a system with 'g' protocol parameters won't lose the default 'i' protocol parameters. */ ca = 0; cnew = 0; for (qd = qdefault->uuconf_qproto_params; qd->uuconf_bproto != '\0'; qd++) { int c; c = 0; for (qa = qalt->uuconf_qproto_params; (qa->uuconf_bproto != '\0' && qa->uuconf_bproto != qd->uuconf_bproto); qa++) ++c; if (qa->uuconf_bproto == '\0') { ++cnew; ca = c; } } if (cnew > 0) { struct uuconf_proto_param *qnew; qnew = ((struct uuconf_proto_param *) uuconf_malloc (qset->uuconf_palloc, ((ca + cnew + 1) * sizeof (struct uuconf_proto_param)))); if (qnew == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) qnew, (pointer) qalt->uuconf_qproto_params, ca * sizeof (struct uuconf_proto_param)); cnew = 0; for (qd = qdefault->uuconf_qproto_params; qd->uuconf_bproto != '\0'; qd++) { for (qa = qalt->uuconf_qproto_params; (qa->uuconf_bproto != '\0' && qa->uuconf_bproto != qd->uuconf_bproto); qa++) ; if (qa->uuconf_bproto == '\0') { qnew[ca + cnew] = *qd; ++cnew; } } qnew[ca + cnew].uuconf_bproto = '\0'; uuconf_free (qset->uuconf_palloc, qalt->uuconf_qproto_params); qalt->uuconf_qproto_params = qnew; } } if (qdefault->uuconf_qalternate != NULL) qdefault = qdefault->uuconf_qalternate; } return UUCONF_SUCCESS; } /* Put in the basic defaults. This ensures that the fields are valid on every uuconf_system structure. */ int _uuconf_isystem_basic_default (qglobal, q) struct sglobal *qglobal; register struct uuconf_system *q; { int iret; iret = UUCONF_SUCCESS; for (; q != NULL && iret == UUCONF_SUCCESS; q = q->uuconf_qalternate) { /* The default of 26 allowable retries is traditional. */ if (q->uuconf_cmax_retries < 0) q->uuconf_cmax_retries = 26; if (q->uuconf_schat.uuconf_pzchat == (char **) &_uuconf_unset) { q->uuconf_schat.uuconf_pzchat = NULL; iret = _uuconf_iadd_string (qglobal, (char *) "\"\"", FALSE, FALSE, &q->uuconf_schat.uuconf_pzchat, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; iret = _uuconf_iadd_string (qglobal, (char *) "\\r\\c", FALSE, FALSE, &q->uuconf_schat.uuconf_pzchat, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; iret = _uuconf_iadd_string (qglobal, (char *) "ogin:", FALSE, FALSE, &q->uuconf_schat.uuconf_pzchat, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; iret = _uuconf_iadd_string (qglobal, (char *) "-BREAK", FALSE, FALSE, &q->uuconf_schat.uuconf_pzchat, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; iret = _uuconf_iadd_string (qglobal, (char *) "-ogin:", FALSE, FALSE, &q->uuconf_schat.uuconf_pzchat, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; iret = _uuconf_iadd_string (qglobal, (char *) "-BREAK", FALSE, FALSE, &q->uuconf_schat.uuconf_pzchat, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; iret = _uuconf_iadd_string (qglobal, (char *) "-ogin:", FALSE, FALSE, &q->uuconf_schat.uuconf_pzchat, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; iret = _uuconf_iadd_string (qglobal, (char *) "\\L", FALSE, FALSE, &q->uuconf_schat.uuconf_pzchat, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; iret = _uuconf_iadd_string (qglobal, (char *) "word:", FALSE, FALSE, &q->uuconf_schat.uuconf_pzchat, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; iret = _uuconf_iadd_string (qglobal, (char *) "\\P", FALSE, FALSE, &q->uuconf_schat.uuconf_pzchat, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; } if (q->uuconf_schat.uuconf_ctimeout < 0) q->uuconf_schat.uuconf_ctimeout = 10; if (q->uuconf_schat.uuconf_fstrip < 0) q->uuconf_schat.uuconf_fstrip = TRUE; if (q->uuconf_scalled_chat.uuconf_ctimeout < 0) q->uuconf_scalled_chat.uuconf_ctimeout = 60; if (q->uuconf_scalled_chat.uuconf_fstrip < 0) q->uuconf_scalled_chat.uuconf_fstrip = TRUE; if (q->uuconf_fsend_request < 0) q->uuconf_fsend_request = TRUE; if (q->uuconf_frec_request < 0) q->uuconf_frec_request = TRUE; if (q->uuconf_fcall_transfer < 0) q->uuconf_fcall_transfer = TRUE; if (q->uuconf_fcalled_transfer < 0) q->uuconf_fcalled_transfer = TRUE; if (q->uuconf_pzlocal_send == (char **) &_uuconf_unset) { q->uuconf_pzlocal_send = NULL; iret = _uuconf_iadd_string (qglobal, (char *) ZROOTDIR, FALSE, FALSE, &q->uuconf_pzlocal_send, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; } if (q->uuconf_pzremote_send == (char **) &_uuconf_unset) { q->uuconf_pzremote_send = NULL; iret = _uuconf_iadd_string (qglobal, (char *) "~", FALSE, FALSE, &q->uuconf_pzremote_send, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; } if (q->uuconf_pzlocal_receive == (char **) &_uuconf_unset) { q->uuconf_pzlocal_receive = NULL; iret = _uuconf_iadd_string (qglobal, (char *) "~", FALSE, FALSE, &q->uuconf_pzlocal_receive, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; } if (q->uuconf_pzremote_receive == (char **) &_uuconf_unset) { q->uuconf_pzremote_receive = NULL; iret = _uuconf_iadd_string (qglobal, (char *) "~", FALSE, FALSE, &q->uuconf_pzremote_receive, q->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret; } if (q->uuconf_pzpath == (char **) &_uuconf_unset) { char *zdup; char **pz; size_t csplit; int c; zdup = (char *) uuconf_malloc (q->uuconf_palloc, sizeof CMDPATH); if (zdup == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) zdup, (pointer) CMDPATH, sizeof CMDPATH); pz = NULL; csplit = 0; if ((c = _uuconf_istrsplit (zdup, '\0', &pz, &csplit)) < 0) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } q->uuconf_pzpath = (char **) uuconf_malloc (q->uuconf_palloc, ((c + 1) * sizeof (char *))); if (q->uuconf_pzpath == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) q->uuconf_pzpath, (pointer) pz, c * sizeof (char *)); q->uuconf_pzpath[c] = NULL; free ((pointer) pz); } if (q->uuconf_pzcmds == (char **) &_uuconf_unset) { q->uuconf_pzcmds = ((char **) uuconf_malloc (q->uuconf_palloc, 3 * sizeof (const char *))); if (q->uuconf_pzcmds == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } q->uuconf_pzcmds[0] = (char *) "rnews"; q->uuconf_pzcmds[1] = (char *) "rmail"; q->uuconf_pzcmds[2] = NULL; } if (q->uuconf_cfree_space < 0) q->uuconf_cfree_space = DEFAULT_FREE_SPACE; if (q->uuconf_zpubdir == (const char *) &_uuconf_unset) q->uuconf_zpubdir = qglobal->qprocess->zpubdir; #define SET(x) if (q->x == (char *) &_uuconf_unset) q->x = NULL SYSTEM_STRINGS(SET); #undef SET #define SET(x) if (q->x == (char **) &_uuconf_unset) q->x = NULL SYSTEM_STRING_ARRAYS(SET); #undef SET #define SET(x) \ if (q->x == (struct uuconf_timespan *) &_uuconf_unset) q->x = NULL SYSTEM_TIMESPANS (SET); #undef SET #define SET(x) if (q->x < 0) q->x = 0 SYSTEM_BOOLEANS (SET); SYSTEM_INTEGERS (SET); #undef SET if (q->uuconf_zport == (char *) &_uuconf_unset) q->uuconf_zport = NULL; if (q->uuconf_qport == (struct uuconf_port *) &_uuconf_unset) q->uuconf_qport = NULL; if (q->uuconf_qproto_params == (struct uuconf_proto_param *) &_uuconf_unset) q->uuconf_qproto_params = NULL; } return iret; } uucp-1.07/uuconf/tcalou.c0000664000076400007640000001125107665321762011060 /* tcalou.c Find callout login name and password from Taylor UUCP configuration files. Copyright (C) 1992, 1993 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tcalou_rcsid[] = "$Id: tcalou.c,v 1.9 2002/03/05 19:10:42 ian Rel $"; #endif #include static int icsys P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); /* Find the callout login name and password for a system from the Taylor UUCP configuration files. */ int uuconf_taylor_callout (pglobal, qsys, pzlog, pzpass) pointer pglobal; const struct uuconf_system *qsys; char **pzlog; char **pzpass; { struct sglobal *qglobal = (struct sglobal *) pglobal; boolean flookup; struct uuconf_cmdtab as[2]; char **pz; int iret; pointer pinfo; *pzlog = NULL; *pzpass = NULL; flookup = FALSE; if (qsys->uuconf_zcall_login != NULL) { if (strcmp (qsys->uuconf_zcall_login, "*") == 0) flookup = TRUE; else { *pzlog = strdup (qsys->uuconf_zcall_login); if (*pzlog == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } } } if (qsys->uuconf_zcall_password != NULL) { if (strcmp (qsys->uuconf_zcall_password, "*") == 0) flookup = TRUE; else { *pzpass = strdup (qsys->uuconf_zcall_password); if (*pzpass == NULL) { qglobal->ierrno = errno; if (*pzlog != NULL) { free ((pointer) *pzlog); *pzlog = NULL; } return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } } } if (! flookup) { if (*pzlog == NULL && *pzpass == NULL) return UUCONF_NOT_FOUND; return UUCONF_SUCCESS; } as[0].uuconf_zcmd = qsys->uuconf_zname; as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 0; if (*pzlog == NULL) as[0].uuconf_pvar = (pointer) pzlog; else as[0].uuconf_pvar = NULL; as[0].uuconf_pifn = icsys; as[1].uuconf_zcmd = NULL; if (*pzpass == NULL) pinfo = (pointer) pzpass; else pinfo = NULL; iret = UUCONF_SUCCESS; for (pz = qglobal->qprocess->pzcallfiles; *pz != NULL; pz++) { FILE *e; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } iret = uuconf_cmd_file (pglobal, e, as, pinfo, (uuconf_cmdtabfn) NULL, 0, qsys->uuconf_palloc); (void) fclose (e); if (iret != UUCONF_SUCCESS) break; if (*pzlog != NULL) break; } if (iret != UUCONF_SUCCESS) { qglobal->zfilename = *pz; return iret | UUCONF_ERROR_FILENAME; } if (*pzlog == NULL && *pzpass == NULL) return UUCONF_NOT_FOUND; return UUCONF_SUCCESS; } /* Copy the login name and password onto the heap and set the pointers. The pzlog argument is passed in pvar, and the pzpass argument is passed in pinfo. */ static int icsys (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; char **pzlog = (char **) pvar; char **pzpass = (char **) pinfo; if (argc < 2 || argc > 3) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; if (pzlog != NULL) { *pzlog = strdup (argv[1]); if (*pzlog == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } } if (pzpass != NULL) { if (argc < 3) *pzpass = strdup (""); else *pzpass = strdup (argv[2]); if (*pzpass == NULL) { qglobal->ierrno = errno; if (pzlog != NULL) { free ((pointer) *pzlog); *pzlog = NULL; } return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } } return UUCONF_CMDTABRET_EXIT; } uucp-1.07/uuconf/tdial.c0000664000076400007640000001375507665321762010701 /* tdial.c Find a dialer in the Taylor UUCP configuration files. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tdial_rcsid[] = "$Id: tdial.c,v 1.9 2002/03/05 19:10:43 ian Rel $"; #endif #include static int iddialer P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int idunknown P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); /* Find a dialer in the Taylor UUCP configuration files by name. */ int uuconf_taylor_dialer_info (pglobal, zname, qdialer) pointer pglobal; const char *zname; struct uuconf_dialer *qdialer; { struct sglobal *qglobal = (struct sglobal *) pglobal; FILE *e; pointer pblock; int iret; char **pz; e = NULL; pblock = NULL; iret = UUCONF_NOT_FOUND; for (pz = qglobal->qprocess->pzdialfiles; *pz != NULL; pz++) { struct uuconf_cmdtab as[2]; char *zdialer; struct uuconf_dialer sdefault; int ilineno; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } qglobal->ilineno = 0; /* Gather the default information from the top of the file. We do this by handling the "dialer" command ourselves and passing every other command to _uuconf_idialer_cmd via idunknown. The value of zdialer will be an malloc block. */ as[0].uuconf_zcmd = "dialer"; as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 2; as[0].uuconf_pvar = (pointer) &zdialer; as[0].uuconf_pifn = iddialer; as[1].uuconf_zcmd = NULL; pblock = uuconf_malloc_block (); if (pblock == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } _uuconf_uclear_dialer (&sdefault); sdefault.uuconf_palloc = pblock; zdialer = NULL; iret = uuconf_cmd_file (pglobal, e, as, (pointer) &sdefault, idunknown, UUCONF_CMDTABFLAG_BACKSLASH, pblock); /* Now skip until we find a dialer with a matching name. */ while (iret == UUCONF_SUCCESS && zdialer != NULL && strcmp (zname, zdialer) != 0) { free ((pointer) zdialer); zdialer = NULL; ilineno = qglobal->ilineno; iret = uuconf_cmd_file (pglobal, e, as, (pointer) NULL, (uuconf_cmdtabfn) NULL, UUCONF_CMDTABFLAG_BACKSLASH, pblock); qglobal->ilineno += ilineno; } if (iret != UUCONF_SUCCESS) { if (zdialer != NULL) free ((pointer) zdialer); break; } if (zdialer != NULL) { size_t csize; /* We've found the dialer we're looking for. Read the rest of the commands for it. */ as[0].uuconf_pvar = NULL; *qdialer = sdefault; csize = strlen (zdialer) + 1; qdialer->uuconf_zname = uuconf_malloc (pblock, csize); if (qdialer->uuconf_zname == NULL) { qglobal->ierrno = errno; free ((pointer) zdialer); iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } memcpy ((pointer) qdialer->uuconf_zname, (pointer) zdialer, csize); free ((pointer) zdialer); ilineno = qglobal->ilineno; iret = uuconf_cmd_file (pglobal, e, as, qdialer, idunknown, UUCONF_CMDTABFLAG_BACKSLASH, pblock); qglobal->ilineno += ilineno; break; } (void) fclose (e); e = NULL; uuconf_free_block (pblock); pblock = NULL; iret = UUCONF_NOT_FOUND; } if (e != NULL) (void) fclose (e); if (iret != UUCONF_SUCCESS && pblock != NULL) uuconf_free_block (pblock); if (iret != UUCONF_SUCCESS && iret != UUCONF_NOT_FOUND) { qglobal->zfilename = *pz; iret |= UUCONF_ERROR_FILENAME; } return iret; } /* Handle a "dialer" command. This copies the string onto the heap and returns the pointer in *pvar, unless pvar is NULL. It returns UUCONF_CMDTABRET_EXIT to force _uuconf_icmd_file_internal to stop reading and return to the code above, which will then check the dialer name just read to see if it matches. */ /*ARGSUSED*/ static int iddialer (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; char **pz = (char **) pvar; if (pz != NULL) { size_t csize; csize = strlen (argv[1]) + 1; *pz = malloc (csize); if (*pz == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } memcpy ((pointer) *pz, (pointer) argv[1], csize); } return UUCONF_CMDTABRET_EXIT; } /* Handle an unknown command by passing it on to _uuconf_idialer_cmd, which will parse it into the dialer structure. */ /*ARGSUSED*/ static int idunknown (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_dialer *qdialer = (struct uuconf_dialer *) pinfo; return _uuconf_idialer_cmd (qglobal, argc, argv, qdialer); } uucp-1.07/uuconf/tdialc.c0000664000076400007640000001565307665321762011043 /* tdialc.c Handle a Taylor UUCP dialer command. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tdialc_rcsid[] = "$Id: tdialc.c,v 1.9 2002/03/05 19:10:43 ian Rel $"; #endif static int idchat P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int iddtr_toggle P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int idcomplete P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int idproto_param P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int idcunknown P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); /* The command table for dialer commands. The "dialer" command is handled specially. */ static const struct cmdtab_offset asDialer_cmds[] = { { "chat", UUCONF_CMDTABTYPE_PREFIX | 0, offsetof (struct uuconf_dialer, uuconf_schat), idchat }, { "dialtone", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_dialer, uuconf_zdialtone), NULL }, { "pause", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_dialer, uuconf_zpause), NULL }, { "carrier", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_dialer, uuconf_fcarrier), NULL }, { "carrier-wait", UUCONF_CMDTABTYPE_INT, offsetof (struct uuconf_dialer, uuconf_ccarrier_wait), NULL }, { "dtr-toggle", UUCONF_CMDTABTYPE_FN | 0, (size_t) -1, iddtr_toggle }, { "complete", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct uuconf_dialer, uuconf_scomplete), idcomplete }, { "complete-chat", UUCONF_CMDTABTYPE_PREFIX, offsetof (struct uuconf_dialer, uuconf_scomplete), idchat }, { "abort", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct uuconf_dialer, uuconf_sabort), idcomplete }, { "abort-chat", UUCONF_CMDTABTYPE_PREFIX, offsetof (struct uuconf_dialer, uuconf_sabort), idchat }, { "protocol-parameter", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct uuconf_dialer, uuconf_qproto_params), idproto_param }, { "seven-bit", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct uuconf_dialer, uuconf_ireliable), _uuconf_iseven_bit }, { "reliable", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct uuconf_dialer, uuconf_ireliable), _uuconf_ireliable }, { "half-duplex", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct uuconf_dialer, uuconf_ireliable), _uuconf_ihalf_duplex }, { NULL, 0, 0, NULL } }; #define CDIALER_CMDS (sizeof asDialer_cmds / sizeof asDialer_cmds[0]) /* Handle a command passed to a dialer from a Taylor UUCP configuration file. This can be called when reading the dialer file, the port file, or the sys file. The return value may have UUCONF_CMDTABRET_KEEP set, but not UUCONF_CMDTABRET_EXIT. It assigns values to the elements of qdialer. The first time this is called, qdialer->uuconf_palloc should be set. This will not set qdialer->uuconf_zname. */ int _uuconf_idialer_cmd (qglobal, argc, argv, qdialer) struct sglobal *qglobal; int argc; char **argv; struct uuconf_dialer *qdialer; { struct uuconf_cmdtab as[CDIALER_CMDS]; int iret; _uuconf_ucmdtab_base (asDialer_cmds, CDIALER_CMDS, (char *) qdialer, as); iret = uuconf_cmd_args ((pointer) qglobal, argc, argv, as, (pointer) qdialer, idcunknown, 0, qdialer->uuconf_palloc); return iret &~ UUCONF_CMDTABRET_EXIT; } /* Reroute a chat script command. */ static int idchat (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_chat *qchat = (struct uuconf_chat *) pvar; struct uuconf_dialer *qdialer = (struct uuconf_dialer *) pinfo; return _uuconf_ichat_cmd (qglobal, argc, argv, qchat, qdialer->uuconf_palloc); } /* Handle the "dtr-toggle" command, which may take two arguments. */ /*ARGSUSED*/ static int iddtr_toggle (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_dialer *qdialer = (struct uuconf_dialer *) pinfo; int iret; if (argc < 2 || argc > 3) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; iret = _uuconf_iboolean (qglobal, argv[1], &qdialer->uuconf_fdtr_toggle); if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) return iret; if (argc < 3) return iret; iret |= _uuconf_iboolean (qglobal, argv[2], &qdialer->uuconf_fdtr_toggle_wait); return iret; } /* Handle the "complete" and "abort" commands. These just turn a string into a trivial chat script. */ /*ARGSUSED*/ static int idcomplete (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_chat *qchat = (struct uuconf_chat *) pvar; struct uuconf_dialer *qdialer = (struct uuconf_dialer *) pinfo; char *azargs[3]; azargs[0] = (char *) "complete-chat"; azargs[1] = (char *) "\"\""; azargs[2] = (char *) argv[1]; return _uuconf_ichat_cmd (qglobal, 3, azargs, qchat, qdialer->uuconf_palloc); } /* Handle the "protocol-parameter" command. */ static int idproto_param (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_proto_param **pqparam = (struct uuconf_proto_param **) pvar; struct uuconf_dialer *qdialer = (struct uuconf_dialer *) pinfo; return _uuconf_iadd_proto_param (qglobal, argc - 1, argv + 1, pqparam, qdialer->uuconf_palloc); } /* Give an error for an unknown dialer command. */ /*ARGSUSED*/ static int idcunknown (pglobal, argc, argv, pvar, pinfo) pointer pglobal ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; } uucp-1.07/uuconf/tdnams.c0000664000076400007640000000603307665321762011061 /* tdnams.c Get all known dialer names from the Taylor UUCP configuration files. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tdnams_rcsid[] = "$Id: tdnams.c,v 1.9 2002/03/05 19:10:43 ian Rel $"; #endif #include static int indialer P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); /* Get the names of all the dialers from the Taylor UUCP configuration files. */ int uuconf_taylor_dialer_names (pglobal, ppzdialers) pointer pglobal; char ***ppzdialers; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_cmdtab as[2]; char **pz; int iret; *ppzdialers = NULL; as[0].uuconf_zcmd = "dialer"; as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 2; as[0].uuconf_pvar = (pointer) ppzdialers; as[0].uuconf_pifn = indialer; as[1].uuconf_zcmd = NULL; iret = UUCONF_SUCCESS; for (pz = qglobal->qprocess->pzdialfiles; *pz != NULL; pz++) { FILE *e; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } iret = uuconf_cmd_file (pglobal, e, as, (pointer) NULL, (uuconf_cmdtabfn) NULL, UUCONF_CMDTABFLAG_BACKSLASH, (pointer) NULL); (void) fclose (e); if (iret != UUCONF_SUCCESS) break; } if (iret != UUCONF_SUCCESS) { qglobal->zfilename = *pz; return iret | UUCONF_ERROR_FILENAME; } if (*ppzdialers == NULL) iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, ppzdialers, (pointer) NULL); return UUCONF_SUCCESS; } /* Add a dialer name to the list. */ /*ARGSUSED*/ static int indialer (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; char ***ppzdialers = (char ***) pvar; int iret; iret = _uuconf_iadd_string (qglobal, argv[1], TRUE, TRUE, ppzdialers, (pointer) NULL); if (iret != UUCONF_SUCCESS) iret |= UUCONF_CMDTABRET_EXIT; return iret; } uucp-1.07/uuconf/tgcmp.c0000664000076400007640000000261507665321762010707 /* tgcmp.c A comparison function for grades for _uuconf_time_parse. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tgcmp_rcsid[] = "$Id: tgcmp.c,v 1.6 2002/03/05 19:10:43 ian Rel $"; #endif /* A comparison function to pass to _uuconf_itime_parse. This compares grades. We can't just pass uuconf_grade_cmp, since _uuconf_itime_parse wants a function takes longs as arguments. */ int _uuconf_itime_grade_cmp (i1, i2) long i1; long i2; { return UUCONF_GRADE_CMP ((int) i1, (int) i2); } uucp-1.07/uuconf/thread.c0000664000076400007640000000365407665321762011050 /* thread.c Initialize for a new thread. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_thread_rcsid[] = "$Id: thread.c,v 1.6 2002/03/05 19:10:43 ian Rel $"; #endif #include /* Initialize for a new thread, by allocating a new sglobal structure which points to the same sprocess structure. */ int uuconf_init_thread (ppglobal) pointer *ppglobal; { struct sglobal **pqglob = (struct sglobal **) ppglobal; pointer pblock; struct sglobal *qnew; pblock = uuconf_malloc_block (); if (pblock == NULL) { (*pqglob)->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } qnew = (struct sglobal *) uuconf_malloc (pblock, sizeof (struct sglobal)); if (qnew == NULL) { (*pqglob)->ierrno = errno; uuconf_free_block (pblock); return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } qnew->pblock = pblock; qnew->ierrno = 0; qnew->ilineno = 0; qnew->zfilename = NULL; qnew->qprocess = (*pqglob)->qprocess; *pqglob = qnew; return UUCONF_SUCCESS; } uucp-1.07/uuconf/time.c0000664000076400007640000002531407665321762010534 /* time.c Parse a time string into a uuconf_timespan structure. Copyright (C) 1992, 1993 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_time_rcsid[] = "$Id: time.c,v 1.14 2002/03/05 19:10:43 ian Rel $"; #endif #include #include static int itadd_span P((struct sglobal *qglobal, int istart, int iend, long ival, int cretry, int (*picmp) P((long, long)), struct uuconf_timespan **pqspan, pointer pblock)); static int itnew P((struct sglobal *qglobal, struct uuconf_timespan **pqset, struct uuconf_timespan *qnext, int istart, int iend, long ival, int cretry, pointer pblock)); /* An array of weekday abbreviations. The code below assumes that each one starts with a lower case letter. */ static const struct { const char *zname; int imin; int imax; } asTdays[] = { { "any", 0, 6 }, { "wk", 1, 5 }, { "su", 0, 0 }, { "mo", 1, 1 }, { "tu", 2, 2 }, { "we", 3, 3 }, { "th", 4, 4 }, { "fr", 5, 5 }, { "sa", 6, 6 }, { "never", -1, -2 }, { "none", -1, -2 }, { NULL, 0, 0 } }; /* Parse a time string and add it to a span list. This function is given the value, the retry time, and the comparison function to use. */ int _uuconf_itime_parse (qglobal, ztime, ival, cretry, picmp, pqspan, pblock) struct sglobal *qglobal; char *ztime; long ival; int cretry; int (*picmp) P((long, long)); struct uuconf_timespan **pqspan; pointer pblock; { struct uuconf_timespan *qlist; char bfirst; const char *z; qlist = *pqspan; if (qlist == (struct uuconf_timespan *) &_uuconf_unset) qlist = NULL; /* Expand the string using a timetable. Keep rechecking the string until it does not match. */ while (TRUE) { char **pz; char *zfound; bfirst = *ztime; if (isupper (BUCHAR (bfirst))) bfirst = tolower (BUCHAR (bfirst)); zfound = NULL; pz = qglobal->qprocess->pztimetables; /* We want the last timetable to have been defined with this name, so we always look through the entire table. */ while (*pz != NULL) { if ((bfirst == (*pz)[0] || (isupper (BUCHAR ((*pz)[0])) && (int) bfirst == (int) tolower (BUCHAR ((*pz)[0])))) && strcasecmp (ztime, *pz) == 0) zfound = pz[1]; pz += 2; } if (zfound == NULL) break; ztime = zfound; } /* Look through the time string. */ z = ztime; while (*z != '\0') { int iday; boolean afday[7]; int istart, iend; if (*z == ',' || *z == '|') ++z; if (*z == '\0' || *z == ';') break; for (iday = 0; iday < 7; iday++) afday[iday] = FALSE; /* Get the days. */ do { bfirst = *z; if (isupper (BUCHAR (bfirst))) bfirst = tolower (BUCHAR (bfirst)); for (iday = 0; asTdays[iday].zname != NULL; iday++) { size_t clen; if (bfirst != asTdays[iday].zname[0]) continue; clen = strlen (asTdays[iday].zname); if (strncasecmp (z, asTdays[iday].zname, clen) == 0) { int iset; for (iset = asTdays[iday].imin; iset <= asTdays[iday].imax; iset++) afday[iset] = TRUE; z += clen; break; } } if (asTdays[iday].zname == NULL) return UUCONF_SYNTAX_ERROR; } while (isalpha (BUCHAR (*z))); /* Get the hours. */ if (! isdigit (BUCHAR (*z))) { istart = 0; iend = 24 * 60; } else { char *zendnum; istart = (int) strtol ((char *) z, &zendnum, 10); if (*zendnum != '-' || ! isdigit (BUCHAR (zendnum[1]))) return UUCONF_SYNTAX_ERROR; z = zendnum + 1; iend = (int) strtol ((char *) z, &zendnum, 10); z = zendnum; istart = (istart / 100) * 60 + istart % 100; iend = (iend / 100) * 60 + iend % 100; } /* Add the times we've found onto the list. */ for (iday = 0; iday < 7; iday++) { if (afday[iday]) { int iminute, iret; iminute = iday * 24 * 60; if (istart < iend) iret = itadd_span (qglobal, iminute + istart, iminute + iend, ival, cretry, picmp, &qlist, pblock); else { /* Wrap around midnight. */ iret = itadd_span (qglobal, iminute, iminute + iend, ival, cretry, picmp, &qlist, pblock); if (iret == UUCONF_SUCCESS) iret = itadd_span (qglobal, iminute + istart, iminute + 24 * 60, ival, cretry, picmp, &qlist, pblock); } if (iret != UUCONF_SUCCESS) return iret; } } } *pqspan = qlist; return UUCONF_SUCCESS; } /* Add a time span to an existing list of time spans. We keep the list sorted by time to make this operation easier. This modifies the existing list, and returns the modified version. It takes a comparison function which should return < 0 if the first argument should take precedence over the second argument and == 0 if they are the same (for grades this is igradecmp; for sizes it is minus (the binary operator)). */ static int itadd_span (qglobal, istart, iend, ival, cretry, picmp, pqspan, pblock) struct sglobal *qglobal; int istart; int iend; long ival; int cretry; int (*picmp) P((long, long)); struct uuconf_timespan **pqspan; pointer pblock; { struct uuconf_timespan **pq; int iret; /* istart < iend */ for (pq = pqspan; *pq != NULL; pq = &(*pq)->uuconf_qnext) { int icmp; /* Invariant: PREV (*pq) == NULL || PREV (*pq)->iend <= istart */ /* istart < iend && (*pq)->istart < (*pq)->iend */ if (iend <= (*pq)->uuconf_istart) { /* istart < iend <= (*pq)->istart < (*pq)->iend */ /* No overlap, and we're at the right spot. See if we can combine these spans. */ if (iend == (*pq)->uuconf_istart && cretry == (*pq)->uuconf_cretry && (*picmp) (ival, (*pq)->uuconf_ival) == 0) { (*pq)->uuconf_istart = istart; return UUCONF_SUCCESS; } /* We couldn't combine them. */ break; } if ((*pq)->uuconf_iend <= istart) { /* (*pq)->istart < (*pq)->iend <= istart < iend */ /* No overlap. Try attaching this span. */ if ((*pq)->uuconf_iend == istart && (*pq)->uuconf_cretry == cretry && ((*pq)->uuconf_qnext == NULL || iend <= (*pq)->uuconf_qnext->uuconf_istart) && (*picmp) (ival, (*pq)->uuconf_ival) == 0) { (*pq)->uuconf_iend = iend; return UUCONF_SUCCESS; } /* Couldn't attach; keep looking for the right spot. We might be able to combine part of the new span onto an existing span, but it's probably not worth it. */ continue; } /* istart < iend && (*pq)->istart < (*pq)->iend && istart < (*pq)->iend && (*pq)->istart < iend */ /* Overlap. */ icmp = (*picmp) (ival, (*pq)->uuconf_ival); if (icmp == 0) { /* Just expand the old span to include the new span. */ if (istart < (*pq)->uuconf_istart) (*pq)->uuconf_istart = istart; if ((*pq)->uuconf_iend >= iend) return UUCONF_SUCCESS; if ((*pq)->uuconf_qnext == NULL || iend <= (*pq)->uuconf_qnext->uuconf_istart) { (*pq)->uuconf_iend = iend; return UUCONF_SUCCESS; } /* The span we are adding overlaps the next span as well. Expand the old span up to the next old span, and keep trying to add the new span. */ (*pq)->uuconf_iend = (*pq)->uuconf_qnext->uuconf_istart; istart = (*pq)->uuconf_iend; } else if (icmp < 0) { /* Replace the old span with the new span. */ if ((*pq)->uuconf_istart < istart) { /* Save the initial portion of the old span. */ iret = itnew (qglobal, pq, *pq, (*pq)->uuconf_istart, istart, (*pq)->uuconf_ival, (*pq)->uuconf_cretry, pblock); if (iret != UUCONF_SUCCESS) return iret; pq = &(*pq)->uuconf_qnext; } if (iend < (*pq)->uuconf_iend) { /* Save the final portion of the old span. */ iret = itnew (qglobal, &(*pq)->uuconf_qnext, (*pq)->uuconf_qnext, iend, (*pq)->uuconf_iend, (*pq)->uuconf_ival, (*pq)->uuconf_cretry, pblock); if (iret != UUCONF_SUCCESS) return iret; } (*pq)->uuconf_ival = ival; (*pq)->uuconf_istart = istart; (*pq)->uuconf_cretry = cretry; if ((*pq)->uuconf_qnext == NULL || iend <= (*pq)->uuconf_qnext->uuconf_istart) { (*pq)->uuconf_iend = iend; return UUCONF_SUCCESS; } /* Move this span up to the next one, and keep trying to add the new span. */ (*pq)->uuconf_iend = (*pq)->uuconf_qnext->uuconf_istart; istart = (*pq)->uuconf_iend; } else { /* Leave the old span untouched. */ if (istart < (*pq)->uuconf_istart) { /* Put in the initial portion of the new span. */ iret = itnew (qglobal, pq, *pq, istart, (*pq)->uuconf_istart, ival, cretry, pblock); if (iret != UUCONF_SUCCESS) return iret; pq = &(*pq)->uuconf_qnext; } if (iend <= (*pq)->uuconf_iend) return UUCONF_SUCCESS; /* Keep trying to add the new span. */ istart = (*pq)->uuconf_iend; } } /* This is the spot for the new span, and there's no overlap. */ return itnew (qglobal, pq, *pq, istart, iend, ival, cretry, pblock); } /* A utility function to create a new uuconf_timespan structure. */ static int itnew (qglobal, pqset, qnext, istart, iend, ival, cretry, pblock) struct sglobal *qglobal; struct uuconf_timespan **pqset; struct uuconf_timespan *qnext; int istart; int iend; long ival; int cretry; pointer pblock; { register struct uuconf_timespan *qnew; qnew = ((struct uuconf_timespan *) uuconf_malloc (pblock, sizeof (struct uuconf_timespan))); if (qnew == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } qnew->uuconf_qnext = qnext; qnew->uuconf_istart = istart; qnew->uuconf_iend = iend; qnew->uuconf_ival = ival; qnew->uuconf_cretry = cretry; *pqset = qnew; return UUCONF_SUCCESS; } uucp-1.07/uuconf/tinit.c0000664000076400007640000003142607665321762010726 /* tinit.c Initialize for reading Taylor UUCP configuration files. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tinit_rcsid[] = "$Id: tinit.c,v 1.16 2002/03/05 19:10:43 ian Rel $"; #endif #include /* Local functions. */ static int itset_default P((struct sglobal *qglobal, char ***ppzvar, const char *zfile)); static int itdebug P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int itaddfile P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int itunknown P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int itprogram P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static const struct cmdtab_offset asCmds[] = { { "nodename", UUCONF_CMDTABTYPE_STRING, offsetof (struct sprocess, zlocalname), NULL }, { "hostname", UUCONF_CMDTABTYPE_STRING, offsetof (struct sprocess, zlocalname), NULL }, { "uuname", UUCONF_CMDTABTYPE_STRING, offsetof (struct sprocess, zlocalname), NULL }, { "spool", UUCONF_CMDTABTYPE_STRING, offsetof (struct sprocess, zspooldir), NULL }, { "pubdir", UUCONF_CMDTABTYPE_STRING, offsetof (struct sprocess, zpubdir), NULL }, { "lockdir", UUCONF_CMDTABTYPE_STRING, offsetof (struct sprocess, zlockdir), NULL }, { "logfile", UUCONF_CMDTABTYPE_STRING, offsetof (struct sprocess, zlogfile), NULL }, { "statfile", UUCONF_CMDTABTYPE_STRING, offsetof (struct sprocess, zstatsfile), NULL }, { "debugfile", UUCONF_CMDTABTYPE_STRING, offsetof (struct sprocess, zdebugfile), NULL }, { "debug", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct sprocess, zdebug), itdebug }, { "strip-login", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct sprocess, fstrip_login), NULL }, { "strip-proto", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct sprocess, fstrip_proto), NULL }, { "max-uuxqts", UUCONF_CMDTABTYPE_INT, offsetof (struct sprocess, cmaxuuxqts), NULL }, { "run-uuxqt", UUCONF_CMDTABTYPE_STRING, offsetof (struct sprocess, zrunuuxqt), NULL }, { "sysfile", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct sprocess, pzsysfiles), itaddfile }, { "portfile", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct sprocess, pzportfiles), itaddfile }, { "dialfile", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct sprocess, pzdialfiles), itaddfile }, { "dialcodefile", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct sprocess, pzdialcodefiles), itaddfile }, { "callfile", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct sprocess, pzcallfiles), itaddfile }, { "passwdfile", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct sprocess, pzpwdfiles), itaddfile }, { "unknown", UUCONF_CMDTABTYPE_FN, offsetof (struct sprocess, qunknown), itunknown }, { "v2-files", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct sprocess, fv2), NULL }, { "hdb-files", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct sprocess, fhdb), NULL }, { "bnu-files", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct sprocess, fhdb), NULL }, { "timetable", UUCONF_CMDTABTYPE_FN | 3, offsetof (struct sprocess, pztimetables), _uuconf_itimetable }, { NULL, 0, 0, NULL } }; #define CCMDS (sizeof asCmds / sizeof asCmds[0]) /* This structure is used to pass information into the command table functions. */ struct sinfo { /* The program name. */ const char *zname; /* A pointer to the command table being used, passed to isystem so it can call uuconf_cmd_args. */ struct uuconf_cmdtab *qcmds; }; /* Initialize the routines which read the Taylor UUCP configuration files. */ int uuconf_taylor_init (ppglobal, zprogram, zname) pointer *ppglobal; const char *zprogram; const char *zname; { struct sglobal **pqglobal = (struct sglobal **) ppglobal; int iret; char *zcopy; struct sglobal *qglobal; boolean fdefault; FILE *e; struct sinfo si; if (*pqglobal == NULL) { iret = _uuconf_iinit_global (pqglobal); if (iret != UUCONF_SUCCESS) return iret; } qglobal = *pqglobal; if (zname != NULL) { size_t csize; csize = strlen (zname) + 1; zcopy = uuconf_malloc (qglobal->pblock, csize); if (zcopy == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) zcopy, (pointer) zname, csize); fdefault = FALSE; } else { zcopy = uuconf_malloc (qglobal->pblock, sizeof NEWCONFIGLIB + sizeof CONFIGFILE - 1); if (zcopy == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) zcopy, (pointer) NEWCONFIGLIB, sizeof NEWCONFIGLIB - 1); memcpy ((pointer) (zcopy + sizeof NEWCONFIGLIB - 1), (pointer) CONFIGFILE, sizeof CONFIGFILE); fdefault = TRUE; } qglobal->qprocess->zconfigfile = zcopy; e = fopen (zcopy, "r"); if (e == NULL) { if (! fdefault) { qglobal->ierrno = errno; qglobal->zfilename = zcopy; return (UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO | UUCONF_ERROR_FILENAME); } /* There is no config file, so just use the default values. */ } else { struct uuconf_cmdtab as[CCMDS]; _uuconf_ucmdtab_base (asCmds, CCMDS, (char *) qglobal->qprocess, as); if (zprogram == NULL) zprogram = "uucp"; si.zname = zprogram; si.qcmds = as; iret = uuconf_cmd_file (qglobal, e, as, (pointer) &si, itprogram, UUCONF_CMDTABFLAG_BACKSLASH, qglobal->pblock); (void) fclose (e); if (iret != UUCONF_SUCCESS) { qglobal->zfilename = zcopy; return iret | UUCONF_ERROR_FILENAME; } } /* Get the defaults for the file names. */ iret = itset_default (qglobal, &qglobal->qprocess->pzsysfiles, SYSFILE); if (iret != UUCONF_SUCCESS) return iret; iret = itset_default (qglobal, &qglobal->qprocess->pzportfiles, PORTFILE); if (iret != UUCONF_SUCCESS) return iret; iret = itset_default (qglobal, &qglobal->qprocess->pzdialfiles, DIALFILE); if (iret != UUCONF_SUCCESS) return iret; iret = itset_default (qglobal, &qglobal->qprocess->pzdialcodefiles, DIALCODEFILE); if (iret != UUCONF_SUCCESS) return iret; iret = itset_default (qglobal, &qglobal->qprocess->pzpwdfiles, PASSWDFILE); if (iret != UUCONF_SUCCESS) return iret; iret = itset_default (qglobal, &qglobal->qprocess->pzcallfiles, CALLFILE); if (iret != UUCONF_SUCCESS) return iret; return UUCONF_SUCCESS; } /* Local interface to the _uuconf_idebug_cmd function, which handles the "debug" command. */ static int itdebug (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; char **pzdebug = (char **) pvar; return _uuconf_idebug_cmd (qglobal, pzdebug, argc, argv, qglobal->pblock); } /* Add new filenames to a list of files. */ /*ARGSUSED*/ static int itaddfile (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; char ***ppz = (char ***) pvar; int i; int iret; if (argc == 1) { iret = _uuconf_iadd_string (qglobal, NULL, FALSE, FALSE, ppz, qglobal->pblock); if (iret != UUCONF_SUCCESS) return iret; } else { for (i = 1; i < argc; i++) { char *z; boolean fallocated; MAKE_ABSOLUTE (z, fallocated, argv[i], NEWCONFIGLIB, qglobal->pblock); if (z == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } iret = _uuconf_iadd_string (qglobal, z, ! fallocated, FALSE, ppz, qglobal->pblock); if (iret != UUCONF_SUCCESS) return iret; } } return UUCONF_CMDTABRET_CONTINUE; } /* Handle an "unknown" command. We accumulate this into a linked list, and only parse them later in uuconf_unknown_system_info. */ /*ARGSUSED*/ static int itunknown (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sunknown **pq = (struct sunknown **) pvar; struct sunknown *q; q = (struct sunknown *) uuconf_malloc (qglobal->pblock, sizeof (struct sunknown)); if (q == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } q->qnext = NULL; q->ilineno = qglobal->ilineno; q->cargs = argc - 1; q->pzargs = (char **) uuconf_malloc (qglobal->pblock, (argc - 1) * sizeof (char *)); if (q->pzargs == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } memcpy ((pointer) q->pzargs, (pointer) (argv + 1), (argc - 1) * sizeof (char *)); while (*pq != NULL) pq = &(*pq)->qnext; *pq = q; return UUCONF_CMDTABRET_KEEP; } /* If we encounter an unknown command, see if it is the program with which we were invoked. If it was, pass the remaining arguments back through the table. */ /*ARGSUSED*/ static int itprogram (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; if (argc <= 1 || strcasecmp (qinfo->zname, argv[0]) != 0) return UUCONF_CMDTABRET_CONTINUE; return uuconf_cmd_args (pglobal, argc - 1, argv + 1, qinfo->qcmds, (pointer) NULL, (uuconf_cmdtabfn) NULL, 0, qglobal->pblock); } /* If a filename was not set by the configuration file, add in the default value. */ static int itset_default (qglobal, ppzvar, zfile) struct sglobal *qglobal; char ***ppzvar; const char *zfile; { size_t clen; char *zadd; if (*ppzvar != NULL) return UUCONF_SUCCESS; clen = strlen (zfile); zadd = (char *) uuconf_malloc (qglobal->pblock, sizeof NEWCONFIGLIB + clen); if (zadd == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) zadd, (pointer) NEWCONFIGLIB, sizeof NEWCONFIGLIB - 1); memcpy ((pointer) (zadd + sizeof NEWCONFIGLIB - 1), (pointer) zfile, clen + 1); return _uuconf_iadd_string (qglobal, zadd, FALSE, FALSE, ppzvar, qglobal->pblock); } /* Handle the "debug" command which is documented to take multiple arguments. This is also called by the ``debug'' command in a sys file. It returns a CMDTABRET code. This should probably be in its own file, but the only other place it is called is from tsinfo.c, and any user of tsinfo.c it sure to link in this file as well. */ int _uuconf_idebug_cmd (qglobal, pzdebug, argc, argv, pblock) struct sglobal *qglobal; char **pzdebug; int argc; char **argv; pointer pblock; { if (argc == 1) { *pzdebug = NULL; return UUCONF_CMDTABRET_CONTINUE; } else if (argc == 2) { *pzdebug = argv[1]; return UUCONF_CMDTABRET_KEEP; } else { size_t cdebug; int i; char *zdebug; cdebug = 0; for (i = 1; i < argc; i++) cdebug += strlen (argv[i]) + 1; zdebug = (char *) uuconf_malloc (pblock, cdebug); if (zdebug == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } cdebug = 0; for (i = 1; i < argc; i++) { size_t clen; clen = strlen (argv[i]); memcpy (zdebug + cdebug, argv[i], clen); zdebug[cdebug + clen] = ' '; cdebug += clen + 1; } zdebug[cdebug - 1] = '\0'; *pzdebug = zdebug; return UUCONF_CMDTABRET_CONTINUE; } } uucp-1.07/uuconf/tlocnm.c0000664000076400007640000000574707665321762011102 /* tlocnm.c Get the local name to use from the Taylor UUCP configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tlocnm_rcsid[] = "$Id: tlocnm.c,v 1.7 2002/03/05 19:10:43 ian Rel $"; #endif #include /* Get the local name to use, based on the login name, from the Taylor UUCP configuration files. This could probably be done in a slightly more intelligent fashion, but no matter what it has to read the systems files. */ int uuconf_taylor_login_localname (pglobal, zlogin, pzname) pointer pglobal; const char *zlogin; char **pzname; { struct sglobal *qglobal = (struct sglobal *) pglobal; char **pznames, **pz; int iret; if (! qglobal->qprocess->fread_syslocs) { iret = _uuconf_iread_locations (qglobal); if (iret != UUCONF_SUCCESS) return iret; } /* As a simple optimization, if there is no "myname" command we can simply return immediately. */ if (! qglobal->qprocess->fuses_myname) { *pzname = NULL; return UUCONF_NOT_FOUND; } iret = uuconf_taylor_system_names (pglobal, &pznames, 0); if (iret != UUCONF_SUCCESS) return iret; *pzname = NULL; iret = UUCONF_NOT_FOUND; for (pz = pznames; *pz != NULL; pz++) { struct uuconf_system ssys; struct uuconf_system *qsys; iret = uuconf_system_info (pglobal, *pz, &ssys); if (iret != UUCONF_SUCCESS) break; for (qsys = &ssys; qsys != NULL; qsys = qsys->uuconf_qalternate) { if (qsys->uuconf_zlocalname != NULL && qsys->uuconf_fcalled && qsys->uuconf_zcalled_login != NULL && strcmp (qsys->uuconf_zcalled_login, zlogin) == 0) { *pzname = strdup (qsys->uuconf_zlocalname); if (*pzname != NULL) iret = UUCONF_SUCCESS; else { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } break; } } (void) uuconf_system_free (pglobal, &ssys); if (qsys != NULL) break; iret = UUCONF_NOT_FOUND; } for (pz = pznames; *pz != NULL; pz++) free ((pointer) *pz); free ((pointer) pznames); return iret; } uucp-1.07/uuconf/tport.c0000664000076400007640000001656607665321762010757 /* tport.c Find a port in the Taylor UUCP configuration files. Copyright (C) 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tport_rcsid[] = "$Id: tport.c,v 1.12 2002/03/05 19:10:43 ian Rel $"; #endif #include static int ipport P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int ipunknown P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); /* Find a port in the Taylor UUCP configuration files by name, baud rate, and special purpose function. */ int uuconf_taylor_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, qport) pointer pglobal; const char *zname; long ibaud; long ihighbaud; int (*pifn) P((struct uuconf_port *, pointer)); pointer pinfo; struct uuconf_port *qport; { struct sglobal *qglobal = (struct sglobal *) pglobal; FILE *e; pointer pblock; char *zfree; int iret; char **pz; if (ihighbaud == 0L) ihighbaud = ibaud; e = NULL; pblock = NULL; zfree = NULL; iret = UUCONF_NOT_FOUND; for (pz = qglobal->qprocess->pzportfiles; *pz != NULL; pz++) { struct uuconf_cmdtab as[2]; char *zport; struct uuconf_port sdefault; int ilineno; e = fopen (*pz, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) continue; qglobal->ierrno = errno; iret = UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO; break; } qglobal->ilineno = 0; /* Gather the default information from the top of the file. We do this by handling the "port" command ourselves and passing every other command to _uuconf_iport_cmd via ipunknown. The value of zport will be an malloc block. */ as[0].uuconf_zcmd = "port"; as[0].uuconf_itype = UUCONF_CMDTABTYPE_FN | 2; as[0].uuconf_pvar = (pointer) &zport; as[0].uuconf_pifn = ipport; as[1].uuconf_zcmd = NULL; pblock = uuconf_malloc_block (); if (pblock == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } _uuconf_uclear_port (&sdefault); sdefault.uuconf_palloc = pblock; zport = NULL; iret = uuconf_cmd_file (pglobal, e, as, (pointer) &sdefault, ipunknown, UUCONF_CMDTABFLAG_BACKSLASH, pblock); if (iret != UUCONF_SUCCESS) { zfree = zport; break; } /* Now skip until we find a port with a matching name. If the zname argument is NULL, we will have to read every port. */ iret = UUCONF_NOT_FOUND; while (zport != NULL) { uuconf_cmdtabfn piunknown; boolean fmatch; if (zname == NULL || strcmp (zname, zport) == 0) { piunknown = ipunknown; *qport = sdefault; qport->uuconf_zname = zport; zfree = zport; fmatch = TRUE; } else { piunknown = NULL; free ((pointer) zport); fmatch = FALSE; } zport = NULL; ilineno = qglobal->ilineno; iret = uuconf_cmd_file (pglobal, e, as, (pointer) qport, piunknown, UUCONF_CMDTABFLAG_BACKSLASH, pblock); qglobal->ilineno += ilineno; if (iret != UUCONF_SUCCESS) break; iret = UUCONF_NOT_FOUND; /* We may have just gathered information about a port. See if it matches the name, the baud rate and the special function. */ if (fmatch) { if (ibaud != 0) { if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM) { long imbaud, imhigh, imlow; imbaud = qport->uuconf_u.uuconf_smodem.uuconf_ibaud; imhigh = qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud; imlow = qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud; if (imbaud == 0 && imlow == 0) ; else if (ibaud <= imbaud && imbaud <= ihighbaud) ; else if (imlow != 0 && imlow <= ihighbaud && imhigh >= ibaud) ; else fmatch = FALSE; } else if (qport->uuconf_ttype == UUCONF_PORTTYPE_DIRECT) { long idbaud; idbaud = qport->uuconf_u.uuconf_sdirect.uuconf_ibaud; if (idbaud != 0 && idbaud != ibaud) fmatch = FALSE; } } } if (fmatch) { if (pifn != NULL) { iret = (*pifn) (qport, pinfo); if (iret == UUCONF_NOT_FOUND) fmatch = FALSE; else if (iret != UUCONF_SUCCESS) break; } } if (fmatch) { if (uuconf_add_block (pblock, zfree) == 0) { zfree = NULL; iret = UUCONF_SUCCESS; } else { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } break; } if (zfree != NULL) { free ((pointer) zfree); zfree = NULL; } } (void) fclose (e); e = NULL; if (iret != UUCONF_NOT_FOUND) break; uuconf_free_block (pblock); pblock = NULL; } if (e != NULL) (void) fclose (e); if (zfree != NULL) free ((pointer) zfree); if (iret != UUCONF_SUCCESS && pblock != NULL) uuconf_free_block (pblock); if (iret != UUCONF_SUCCESS && iret != UUCONF_NOT_FOUND) { qglobal->zfilename = *pz; iret |= UUCONF_ERROR_FILENAME; } return iret; } /* Handle a "port" command. This copies the string onto the heap and returns the pointer in *pvar. It returns UUCONF_CMDTABRET_EXIT to force uuconf_cmd_file to stop reading and return to the code above, which will then check the port just read to see if it matches. */ /*ARGSUSED*/ static int ipport (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; char **pz = (char **) pvar; size_t csize; csize = strlen (argv[1]) + 1; *pz = malloc (csize); if (*pz == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } memcpy ((pointer) *pz, (pointer) argv[1], csize); return UUCONF_CMDTABRET_EXIT; } /* Handle an unknown command by passing it on to _uuconf_iport_cmd, which will parse it into the port structure. */ /*ARGSUSED*/ static int ipunknown (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_port *qport = (struct uuconf_port *) pinfo; int iret; iret = _uuconf_iport_cmd (qglobal, argc, argv, qport); if (UUCONF_ERROR_VALUE (iret) != UUCONF_SUCCESS) iret |= UUCONF_CMDTABRET_EXIT; return iret; } uucp-1.07/uuconf/tportc.c0000664000076400007640000003714207665321762011113 /* tportc.c Handle a Taylor UUCP port command. Copyright (C) 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tportc_rcsid[] = "$Id: tportc.c,v 1.18 2002/03/05 19:10:43 ian Rel $"; #endif #include static int ipproto_param P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int ipbaud_range P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int ipdialer P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); static int ipcunknown P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); /* The string names of the port types. This array corresponds to the uuconf_porttype enumeration. */ static const char * const azPtype_names[] = { NULL, "stdin", "modem", "direct", "tcp", "tli", "pipe" }; #define CPORT_TYPES (sizeof azPtype_names / sizeof azPtype_names[0]) /* The command table for generic port commands. The "port" and "type" commands are handled specially. */ static const struct cmdtab_offset asPort_cmds[] = { { "protocol", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_port, uuconf_zprotocols), NULL }, { "protocol-parameter", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct uuconf_port, uuconf_qproto_params), ipproto_param }, { "seven-bit", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct uuconf_port, uuconf_ireliable), _uuconf_iseven_bit }, { "reliable", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct uuconf_port, uuconf_ireliable), _uuconf_ireliable }, { "half-duplex", UUCONF_CMDTABTYPE_FN | 2, offsetof (struct uuconf_port, uuconf_ireliable), _uuconf_ihalf_duplex }, { "lockname", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_port, uuconf_zlockname), NULL }, { NULL, 0, 0, NULL } }; #define CPORT_CMDS (sizeof asPort_cmds / sizeof asPort_cmds[0]) /* The stdin port command table. */ static const struct cmdtab_offset asPstdin_cmds[] = { { NULL, 0, 0, NULL } }; #define CSTDIN_CMDS (sizeof asPstdin_cmds / sizeof asPstdin_cmds[0]) /* The modem port command table. */ static const struct cmdtab_offset asPmodem_cmds[] = { { "device", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_zdevice), NULL }, { "baud", UUCONF_CMDTABTYPE_LONG, offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_ibaud), NULL }, { "speed", UUCONF_CMDTABTYPE_LONG, offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_ibaud), NULL }, { "baud-range", UUCONF_CMDTABTYPE_FN | 3, offsetof (struct uuconf_port, uuconf_u.uuconf_smodem), ipbaud_range }, { "speed-range", UUCONF_CMDTABTYPE_FN | 3, offsetof (struct uuconf_port, uuconf_u.uuconf_smodem), ipbaud_range }, { "carrier", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_fcarrier), NULL }, { "hardflow", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_fhardflow), NULL }, { "dial-device", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_zdial_device), NULL }, { "dialer", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct uuconf_port, uuconf_u.uuconf_smodem), ipdialer }, { "dialer-sequence", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_port, uuconf_u.uuconf_smodem.uuconf_pzdialer), NULL }, { NULL, 0, 0, NULL } }; #define CMODEM_CMDS (sizeof asPmodem_cmds / sizeof asPmodem_cmds[0]) /* The direct port command table. */ static const struct cmdtab_offset asPdirect_cmds[] = { { "device", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_port, uuconf_u.uuconf_sdirect.uuconf_zdevice), NULL }, { "baud", UUCONF_CMDTABTYPE_LONG, offsetof (struct uuconf_port, uuconf_u.uuconf_sdirect.uuconf_ibaud), NULL }, { "speed", UUCONF_CMDTABTYPE_LONG, offsetof (struct uuconf_port, uuconf_u.uuconf_sdirect.uuconf_ibaud), NULL }, { "carrier", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_port, uuconf_u.uuconf_sdirect.uuconf_fcarrier), NULL }, { "hardflow", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_port, uuconf_u.uuconf_sdirect.uuconf_fhardflow), NULL }, { NULL, 0, 0, NULL } }; #define CDIRECT_CMDS (sizeof asPdirect_cmds / sizeof asPdirect_cmds[0]) /* The TCP port command table. */ static const struct cmdtab_offset asPtcp_cmds[] = { { "service", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_port, uuconf_u.uuconf_stcp.uuconf_zport), NULL }, { "version", UUCONF_CMDTABTYPE_INT, offsetof (struct uuconf_port, uuconf_u.uuconf_stcp.uuconf_iversion), NULL }, { "dialer-sequence", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_port, uuconf_u.uuconf_stcp.uuconf_pzdialer), NULL }, { NULL, 0, 0, NULL } }; #define CTCP_CMDS (sizeof asPtcp_cmds / sizeof asPtcp_cmds[0]) /* The TLI port command table. */ static const struct cmdtab_offset asPtli_cmds[] = { { "device", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_port, uuconf_u.uuconf_stli.uuconf_zdevice), NULL }, { "stream", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_port, uuconf_u.uuconf_stli.uuconf_fstream), NULL }, { "push", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_port, uuconf_u.uuconf_stli.uuconf_pzpush), NULL }, { "dialer-sequence", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_port, uuconf_u.uuconf_stli.uuconf_pzdialer), NULL }, { "server-address", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_port, uuconf_u.uuconf_stli.uuconf_zservaddr), NULL }, { NULL, 0, 0, NULL } }; #define CTLI_CMDS (sizeof asPtli_cmds / sizeof asPtli_cmds[0]) /* The pipe port command table. */ static const struct cmdtab_offset asPpipe_cmds[] = { { "command", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_port, uuconf_u.uuconf_spipe.uuconf_pzcmd), NULL }, { NULL, 0, 0, NULL} }; #define CPIPE_CMDS (sizeof asPpipe_cmds / sizeof asPpipe_cmds[0]) #undef max #define max(i1, i2) ((i1) > (i2) ? (i1) : (i2)) #define CCMDS \ max (max (max (CPORT_CMDS, CSTDIN_CMDS), CMODEM_CMDS), \ max (max (CDIRECT_CMDS, CTCP_CMDS), max (CTLI_CMDS, CPIPE_CMDS))) /* Handle a command passed to a port from a Taylor UUCP configuration file. This can be called when reading either the port file or the sys file. The return value may have UUCONF_CMDTABRET_KEEP set, but not UUCONF_CMDTABRET_EXIT. It assigns values to the elements of qport. The first time this is called, qport->uuconf_zname and qport->uuconf_palloc should be set and qport->uuconf_ttype should be UUCONF_PORTTYPE_UNKNOWN. */ int _uuconf_iport_cmd (qglobal, argc, argv, qport) struct sglobal *qglobal; int argc; char **argv; struct uuconf_port *qport; { boolean fgottype; const struct cmdtab_offset *qcmds; size_t ccmds; struct uuconf_cmdtab as[CCMDS]; size_t i; int iret; fgottype = strcasecmp (argv[0], "type") == 0; if (fgottype || qport->uuconf_ttype == UUCONF_PORTTYPE_UNKNOWN) { enum uuconf_porttype ttype; /* We either just got a "type" command, or this is an uninitialized port. If the first command to a port is not "type", it is assumed to be a modem port. This implementation will actually permit "type" at any point, but will effectively discard any type specific information that appears before the "type" command. This supports defaults, in that the default may be of a specific type while future ports in the same file may be of other types. */ if (! fgottype) ttype = UUCONF_PORTTYPE_MODEM; else { if (argc != 2) return UUCONF_SYNTAX_ERROR; for (i = 0; i < CPORT_TYPES; i++) if (azPtype_names[i] != NULL && strcasecmp (argv[1], azPtype_names[i]) == 0) break; if (i >= CPORT_TYPES) return UUCONF_SYNTAX_ERROR; ttype = (enum uuconf_porttype) i; } qport->uuconf_ttype = ttype; switch (ttype) { default: case UUCONF_PORTTYPE_STDIN: break; case UUCONF_PORTTYPE_MODEM: qport->uuconf_u.uuconf_smodem.uuconf_zdevice = NULL; qport->uuconf_u.uuconf_smodem.uuconf_zdial_device = NULL; qport->uuconf_u.uuconf_smodem.uuconf_ibaud = 0L; qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud = 0L; qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud = 0L; qport->uuconf_u.uuconf_smodem.uuconf_fcarrier = TRUE; qport->uuconf_u.uuconf_smodem.uuconf_fhardflow = TRUE; qport->uuconf_u.uuconf_smodem.uuconf_pzdialer = NULL; qport->uuconf_u.uuconf_smodem.uuconf_qdialer = NULL; break; case UUCONF_PORTTYPE_DIRECT: qport->uuconf_u.uuconf_sdirect.uuconf_zdevice = NULL; qport->uuconf_u.uuconf_sdirect.uuconf_ibaud = -1; qport->uuconf_u.uuconf_sdirect.uuconf_fcarrier = FALSE; qport->uuconf_u.uuconf_sdirect.uuconf_fhardflow = TRUE; break; case UUCONF_PORTTYPE_TCP: qport->uuconf_u.uuconf_stcp.uuconf_zport = (char *) "uucp"; qport->uuconf_u.uuconf_stcp.uuconf_iversion = 0; qport->uuconf_u.uuconf_stcp.uuconf_pzdialer = NULL; qport->uuconf_ireliable = (UUCONF_RELIABLE_SPECIFIED | UUCONF_RELIABLE_ENDTOEND | UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX); break; case UUCONF_PORTTYPE_TLI: qport->uuconf_u.uuconf_stli.uuconf_zdevice = NULL; qport->uuconf_u.uuconf_stli.uuconf_fstream = FALSE; qport->uuconf_u.uuconf_stli.uuconf_pzpush = NULL; qport->uuconf_u.uuconf_stli.uuconf_pzdialer = NULL; qport->uuconf_u.uuconf_stli.uuconf_zservaddr = NULL; qport->uuconf_ireliable = (UUCONF_RELIABLE_SPECIFIED | UUCONF_RELIABLE_ENDTOEND | UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX); break; case UUCONF_PORTTYPE_PIPE: qport->uuconf_u.uuconf_spipe.uuconf_pzcmd = NULL; break; } if (fgottype) return UUCONF_CMDTABRET_CONTINUE; } /* See if this command is one of the generic ones. */ qcmds = asPort_cmds; ccmds = CPORT_CMDS; for (i = 0; i < CPORT_CMDS - 1; i++) if (strcasecmp (argv[0], asPort_cmds[i].zcmd) == 0) break; if (i >= CPORT_CMDS - 1) { /* It's not a generic command, so we must check the type specific commands. */ switch (qport->uuconf_ttype) { case UUCONF_PORTTYPE_STDIN: qcmds = asPstdin_cmds; ccmds = CSTDIN_CMDS; break; case UUCONF_PORTTYPE_MODEM: qcmds = asPmodem_cmds; ccmds = CMODEM_CMDS; break; case UUCONF_PORTTYPE_DIRECT: qcmds = asPdirect_cmds; ccmds = CDIRECT_CMDS; break; case UUCONF_PORTTYPE_TCP: qcmds = asPtcp_cmds; ccmds = CTCP_CMDS; break; case UUCONF_PORTTYPE_TLI: qcmds = asPtli_cmds; ccmds = CTLI_CMDS; break; case UUCONF_PORTTYPE_PIPE: qcmds = asPpipe_cmds; ccmds = CPIPE_CMDS; break; default: return UUCONF_SYNTAX_ERROR; } } /* Copy the command table onto the stack and modify it to point to qport. */ _uuconf_ucmdtab_base (qcmds, ccmds, (char *) qport, as); iret = uuconf_cmd_args ((pointer) qglobal, argc, argv, as, (pointer) qport, ipcunknown, 0, qport->uuconf_palloc); return iret &~ UUCONF_CMDTABRET_EXIT; } /* Handle the "protocol-parameter" command. */ static int ipproto_param (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_proto_param **pqparam = (struct uuconf_proto_param **) pvar; struct uuconf_port *qport = (struct uuconf_port *) pinfo; return _uuconf_iadd_proto_param (qglobal, argc - 1, argv + 1, pqparam, qport->uuconf_palloc); } /* Handle the "baud-range" command. */ /*ARGSUSED*/ static int ipbaud_range (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_modem_port *qmodem = (struct uuconf_modem_port *) pvar; int iret; iret = _uuconf_iint (qglobal, argv[1], (pointer) &qmodem->uuconf_ilowbaud, FALSE); if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) return iret; iret |= _uuconf_iint (qglobal, argv[2], (pointer) &qmodem->uuconf_ihighbaud, FALSE); return iret; } /* Handle the "dialer" command. If there is one argument, this names a dialer. Otherwise, the remaining arguments form a command describing the dialer. */ static int ipdialer (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_modem_port *qmodem = (struct uuconf_modem_port *) pvar; struct uuconf_port *qport = (struct uuconf_port *) pinfo; int iret; if (argc < 2) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; if (argc > 2) { if (qmodem->uuconf_qdialer == NULL) { struct uuconf_dialer *qnew; size_t clen; qnew = ((struct uuconf_dialer *) uuconf_malloc (qport->uuconf_palloc, sizeof (struct uuconf_dialer))); if (qnew == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } _uuconf_uclear_dialer (qnew); if (qport->uuconf_zname == NULL) qnew->uuconf_zname = (char *) "default port file dialer"; else { clen = strlen (qport->uuconf_zname); qnew->uuconf_zname = (char *) uuconf_malloc (qport->uuconf_palloc, clen + sizeof " dialer"); if (qnew->uuconf_zname == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } memcpy ((pointer) qnew->uuconf_zname, (pointer) qport->uuconf_zname, clen); memcpy ((pointer) (qnew->uuconf_zname + clen), (pointer) " dialer", sizeof " dialer"); } qnew->uuconf_palloc = qport->uuconf_palloc; qmodem->uuconf_qdialer = qnew; } iret = _uuconf_idialer_cmd (qglobal, argc - 1, argv + 1, qmodem->uuconf_qdialer); if ((iret &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) iret |= UUCONF_CMDTABRET_EXIT; return iret; } else { qmodem->uuconf_pzdialer = NULL; iret = _uuconf_iadd_string (qglobal, argv[1], TRUE, FALSE, &qmodem->uuconf_pzdialer, qport->uuconf_palloc); if (iret != UUCONF_SUCCESS) iret |= UUCONF_CMDTABRET_EXIT; return iret; } } /* Give an error for an unknown port command. */ /*ARGSUSED*/ static int ipcunknown (pglobal, argc, argv, pvar, pinfo) pointer pglobal ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; } uucp-1.07/uuconf/tsinfo.c0000664000076400007640000007143707665321762011107 /* tsinfo.c Get information about a system from the Taylor UUCP configuration files. Copyright (C) 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tsinfo_rcsid[] = "$Id: tsinfo.c,v 1.21 2002/03/05 19:10:43 ian Rel $"; #endif #include #include #ifndef SEEK_SET #define SEEK_SET 0 #endif static void uiset_call P((struct uuconf_system *qsys)); static int iisizecmp P((long i1, long i2)); /* Local functions needed to parse the system information file. */ #define CMDTABFN(z) \ static int z P((pointer, int, char **, pointer, pointer)) CMDTABFN (iisystem); CMDTABFN (iialias); CMDTABFN (iialternate); CMDTABFN (iidefault_alternates); CMDTABFN (iitime); CMDTABFN (iitimegrade); CMDTABFN (iisize); CMDTABFN (iibaud_range); CMDTABFN (iiport); CMDTABFN (iichat); CMDTABFN (iidebug); CMDTABFN (iicalled_login); CMDTABFN (iiproto_param); CMDTABFN (iirequest); CMDTABFN (iitransfer); CMDTABFN (iiforward); CMDTABFN (iiunknown); #undef CMDTABFN /* We have to pass a fair amount of information in and out of the various system commands. Using global variables would make the code non-reentrant, so we instead pass a pointer to single structure as the pinfo argument to the system commands. */ struct sinfo { /* The system information we're building up. */ struct uuconf_system *qsys; /* Whether any alternates have been used. */ boolean falternates; /* A list of the previous alternates. */ struct uuconf_system salternate; /* Whether to use extra alternates from the file wide defaults. */ int fdefault_alternates; }; /* The command table for system commands. */ static const struct cmdtab_offset asIcmds[] = { { "system", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, iisystem }, { "alias", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, iialias }, { "alternate", UUCONF_CMDTABTYPE_FN | 0, (size_t) -1, iialternate }, { "default-alternates", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, iidefault_alternates }, { "time", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct uuconf_system, uuconf_qtimegrade), iitime }, { "timegrade", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct uuconf_system, uuconf_qtimegrade), iitimegrade }, { "max-retries", UUCONF_CMDTABTYPE_INT, offsetof (struct uuconf_system, uuconf_cmax_retries), NULL }, { "success-wait", UUCONF_CMDTABTYPE_INT, offsetof (struct uuconf_system, uuconf_csuccess_wait), NULL }, { "call-timegrade", UUCONF_CMDTABTYPE_FN | 3, offsetof (struct uuconf_system, uuconf_qcalltimegrade), iitimegrade }, { "called-timegrade", UUCONF_CMDTABTYPE_FN | 3, offsetof (struct uuconf_system, uuconf_qcalledtimegrade), iitimegrade }, { "call-local-size", UUCONF_CMDTABTYPE_FN | 3, offsetof (struct uuconf_system, uuconf_qcall_local_size), iisize }, { "call-remote-size", UUCONF_CMDTABTYPE_FN | 3, offsetof (struct uuconf_system, uuconf_qcall_remote_size), iisize }, { "called-local-size", UUCONF_CMDTABTYPE_FN | 3, offsetof (struct uuconf_system, uuconf_qcalled_local_size), iisize }, { "called-remote-size", UUCONF_CMDTABTYPE_FN | 3, offsetof (struct uuconf_system, uuconf_qcalled_remote_size), iisize }, { "timetable", UUCONF_CMDTABTYPE_FN | 3, (size_t) -1, _uuconf_itimetable }, { "baud", UUCONF_CMDTABTYPE_LONG, offsetof (struct uuconf_system, uuconf_ibaud), NULL }, { "speed", UUCONF_CMDTABTYPE_LONG, offsetof (struct uuconf_system, uuconf_ibaud), NULL }, { "baud-range", UUCONF_CMDTABTYPE_FN | 3, 0, iibaud_range }, { "speed-range", UUCONF_CMDTABTYPE_FN | 3, 0, iibaud_range }, { "port", UUCONF_CMDTABTYPE_FN | 0, (size_t) -1, iiport }, { "phone", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_system, uuconf_zphone), NULL }, { "address", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_system, uuconf_zphone), NULL }, { "chat", UUCONF_CMDTABTYPE_PREFIX | 0, offsetof (struct uuconf_system, uuconf_schat), iichat }, { "call-login", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_system, uuconf_zcall_login), NULL }, { "call-password", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_system, uuconf_zcall_password), NULL }, { "called-login", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct uuconf_system, uuconf_zcalled_login), iicalled_login }, { "callback", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_system, uuconf_fcallback), NULL }, { "sequence", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_system, uuconf_fsequence), NULL }, { "protocol", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_system, uuconf_zprotocols), NULL }, { "protocol-parameter", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct uuconf_system, uuconf_qproto_params), iiproto_param }, { "called-chat", UUCONF_CMDTABTYPE_PREFIX | 0, offsetof (struct uuconf_system, uuconf_scalled_chat), iichat }, { "debug", UUCONF_CMDTABTYPE_FN | 0, offsetof (struct uuconf_system, uuconf_zdebug), iidebug }, { "max-remote-debug", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_system, uuconf_zmax_remote_debug), NULL }, { "send-request", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_system, uuconf_fsend_request), NULL }, { "receive-request", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_system, uuconf_frec_request), NULL }, { "request", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, iirequest }, { "call-transfer", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_system, uuconf_fcall_transfer), NULL }, { "called-transfer", UUCONF_CMDTABTYPE_BOOLEAN, offsetof (struct uuconf_system, uuconf_fcalled_transfer), NULL }, { "transfer", UUCONF_CMDTABTYPE_FN | 2, (size_t) -1, iitransfer }, { "local-send", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_system, uuconf_pzlocal_send), NULL }, { "remote-send", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_system, uuconf_pzremote_send), NULL }, { "local-receive", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_system, uuconf_pzlocal_receive), NULL }, { "remote-receive", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_system, uuconf_pzremote_receive), NULL }, { "command-path", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_system, uuconf_pzpath), NULL }, { "commands", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_system, uuconf_pzcmds), NULL }, { "free-space", UUCONF_CMDTABTYPE_LONG, offsetof (struct uuconf_system, uuconf_cfree_space), NULL }, { "forward-from", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_system, uuconf_pzforward_from), NULL }, { "forward-to", UUCONF_CMDTABTYPE_FULLSTRING, offsetof (struct uuconf_system, uuconf_pzforward_to), NULL }, { "forward", UUCONF_CMDTABTYPE_FN | 0, (size_t) -1, iiforward }, { "pubdir", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_system, uuconf_zpubdir), NULL }, { "myname", UUCONF_CMDTABTYPE_STRING, offsetof (struct uuconf_system, uuconf_zlocalname), NULL }, { "max-file-time", UUCONF_CMDTABTYPE_LONG, offsetof (struct uuconf_system, uuconf_cmax_file_time), NULL }, { NULL, 0, 0, NULL } }; #define CSYSTEM_CMDS (sizeof asIcmds / sizeof asIcmds[0]) /* Get information about the system zsystem from the Taylor UUCP configuration files. Sets *qsys. This does not ensure that all default information is set. */ int _uuconf_itaylor_system_internal (qglobal, zsystem, qsys) struct sglobal *qglobal; const char *zsystem; struct uuconf_system *qsys; { int iret; struct stsysloc *qloc; struct uuconf_cmdtab as[CSYSTEM_CMDS]; struct sinfo si; struct uuconf_system sdefaults; if (! qglobal->qprocess->fread_syslocs) { iret = _uuconf_iread_locations (qglobal); if (iret != UUCONF_SUCCESS) return iret; } /* Find the system in the list of locations. */ for (qloc = qglobal->qprocess->qsyslocs; qloc != NULL; qloc = qloc->qnext) if (qloc->zname[0] == zsystem[0] && strcmp (qloc->zname, zsystem) == 0) break; if (qloc == NULL) return UUCONF_NOT_FOUND; /* If this is an alias, then the real system is the next non-alias in the list. */ while (qloc->falias) { qloc = qloc->qnext; if (qloc == NULL) return UUCONF_NOT_FOUND; } _uuconf_ucmdtab_base (asIcmds, CSYSTEM_CMDS, (char *) qsys, as); rewind (qloc->e); /* Read the file wide defaults from the start of the file. */ _uuconf_uclear_system (qsys); si.qsys = qsys; si.falternates = FALSE; si.fdefault_alternates = TRUE; qsys->uuconf_palloc = uuconf_malloc_block (); if (qsys->uuconf_palloc == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } iret = uuconf_cmd_file ((pointer) qglobal, qloc->e, as, (pointer) &si, iiunknown, UUCONF_CMDTABFLAG_BACKSLASH, qsys->uuconf_palloc); if (iret != UUCONF_SUCCESS) { qglobal->zfilename = qloc->zfile; return iret | UUCONF_ERROR_FILENAME; } if (! si.falternates) uiset_call (qsys); else { /* Attach the final alternate. */ iret = iialternate ((pointer) qglobal, 0, (char **) NULL, (pointer) NULL, (pointer) &si); if (iret != UUCONF_SUCCESS) return iret; } /* Save off the defaults. */ sdefaults = *qsys; /* Advance to the information for the system we want. */ if (fseek (qloc->e, qloc->iloc, SEEK_SET) != 0) { qglobal->ierrno = errno; qglobal->zfilename = qloc->zfile; return (UUCONF_FSEEK_FAILED | UUCONF_ERROR_ERRNO | UUCONF_ERROR_FILENAME); } /* Read in the system we want. */ _uuconf_uclear_system (qsys); qsys->uuconf_zname = (char *) qloc->zname; qsys->uuconf_palloc = sdefaults.uuconf_palloc; si.falternates = FALSE; iret = uuconf_cmd_file (qglobal, qloc->e, as, (pointer) &si, iiunknown, UUCONF_CMDTABFLAG_BACKSLASH, qsys->uuconf_palloc); qglobal->ilineno += qloc->ilineno; if (iret == UUCONF_SUCCESS) { if (! si.falternates) uiset_call (qsys); else iret = iialternate ((pointer) qglobal, 0, (char **) NULL, (pointer) NULL, (pointer) &si); } /* Merge in the defaults. */ if (iret == UUCONF_SUCCESS) iret = _uuconf_isystem_default (qglobal, qsys, &sdefaults, si.fdefault_alternates); /* The first alternate is always available for calling in. It is always available for calling out if it has some way to choose a port (this would normally be set by uiset_call anyhow, but it won't be if all the port information comes from the defaults). */ if (iret == UUCONF_SUCCESS) { qsys->uuconf_fcalled = TRUE; if (qsys->uuconf_zport != (char *) &_uuconf_unset || qsys->uuconf_qport != (struct uuconf_port *) &_uuconf_unset || qsys->uuconf_ibaud >= 0 || qsys->uuconf_zphone != (char *) &_uuconf_unset) qsys->uuconf_fcall = TRUE; } if (iret != UUCONF_SUCCESS) { qglobal->zfilename = qloc->zfile; iret |= UUCONF_ERROR_FILENAME; } return iret; } /* Set the fcall and fcalled field for the system. This marks a particular alternate for use when calling out or calling in. This is where we implement the semantics described in the documentation: a change to a relevant field implies that the alternate is used. If all the relevant fields are unchanged, the alternate is not used. */ static void uiset_call (qsys) struct uuconf_system *qsys; { qsys->uuconf_fcall = (qsys->uuconf_qtimegrade != (struct uuconf_timespan *) &_uuconf_unset || qsys->uuconf_zport != (char *) &_uuconf_unset || qsys->uuconf_qport != (struct uuconf_port *) &_uuconf_unset || qsys->uuconf_ibaud >= 0 || qsys->uuconf_zphone != (char *) &_uuconf_unset || qsys->uuconf_schat.uuconf_pzchat != (char **) &_uuconf_unset || qsys->uuconf_schat.uuconf_pzprogram != (char **) &_uuconf_unset); qsys->uuconf_fcalled = qsys->uuconf_zcalled_login != (char *) &_uuconf_unset; } /* Handle the "system" command. Because we skip directly to the system we want to read, a "system" command means we've reached the end of it. */ static int iisystem (pglobal, argc, argv, pvar, pinfo) pointer pglobal ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { return UUCONF_CMDTABRET_EXIT; } /* Handle the "alias" command. */ /*ARGSUSED*/ static int iialias (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; int iret; iret = _uuconf_iadd_string (qglobal, argv[1], TRUE, FALSE, &qinfo->qsys->uuconf_pzalias, qinfo->qsys->uuconf_palloc); if (iret != UUCONF_SUCCESS) iret |= UUCONF_CMDTABRET_EXIT; return iret; } /* Handle the "alternate" command. The information just read is in sIhold. If this is the first "alternate" command for this system, we save off the current information in sIalternate. Otherwise we default this information to sIalternate, and then add it to the end of the list of alternates in sIalternate. */ static int iialternate (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; uiset_call (qinfo->qsys); if (! qinfo->falternates) { qinfo->salternate = *qinfo->qsys; qinfo->falternates = TRUE; } else { int iret; struct uuconf_system *qnew, **pq; iret = _uuconf_isystem_default (qglobal, qinfo->qsys, &qinfo->salternate, FALSE); if (iret != UUCONF_SUCCESS) return iret | UUCONF_CMDTABRET_EXIT; qnew = ((struct uuconf_system *) uuconf_malloc (qinfo->qsys->uuconf_palloc, sizeof (struct uuconf_system))); if (qnew == NULL) { qglobal->ierrno = errno;; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } *qnew = *qinfo->qsys; for (pq = &qinfo->salternate.uuconf_qalternate; *pq != NULL; pq = &(*pq)->uuconf_qalternate) ; *pq = qnew; } /* If this is the last alternate command, move the information back to qinfo->qsys. */ if (argc == 0) *qinfo->qsys = qinfo->salternate; else { _uuconf_uclear_system (qinfo->qsys); qinfo->qsys->uuconf_zname = qinfo->salternate.uuconf_zname; qinfo->qsys->uuconf_palloc = qinfo->salternate.uuconf_palloc; if (argc > 1) { qinfo->qsys->uuconf_zalternate = argv[1]; return UUCONF_CMDTABRET_KEEP; } } return UUCONF_CMDTABRET_CONTINUE; } /* Handle the "default-alternates" command. This just takes a boolean argument which is used to set the fdefault_alternates field of the sinfo structure. */ /*ARGSUSED*/ static int iidefault_alternates (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; return _uuconf_iboolean (qglobal, argv[1], &qinfo->fdefault_alternates); } /* Handle the "time" command. We do this by turning it into a "timegrade" command with a grade of BGRADE_LOW. The first argument is a time string, and the optional second argument is the retry time. */ /*ARGSUSED*/ static int iitime (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { char *aznew[4]; char ab[2]; if (argc != 2 && argc != 3) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; aznew[0] = argv[0]; ab[0] = UUCONF_GRADE_LOW; ab[1] = '\0'; aznew[1] = ab; aznew[2] = argv[1]; if (argc > 2) aznew[3] = argv[2]; return iitimegrade (pglobal, argc + 1, aznew, pvar, pinfo); } /* Handle the "timegrade" command by calling _uuconf_itime_parse with appropriate ival (the work grade) and cretry (the retry time) arguments. */ static int iitimegrade (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_timespan **pqspan = (struct uuconf_timespan **) pvar; struct sinfo *qinfo = (struct sinfo *) pinfo; int cretry; int iret; if (argc < 3 || argc > 4) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; if (argv[1][1] != '\0' || ! UUCONF_GRADE_LEGAL (argv[1][0])) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; if (argc == 3) cretry = 0; else { iret = _uuconf_iint (qglobal, argv[3], (pointer) &cretry, TRUE); if (iret != UUCONF_SUCCESS) return iret; } iret = _uuconf_itime_parse (qglobal, argv[2], (long) argv[1][0], cretry, _uuconf_itime_grade_cmp, pqspan, qinfo->qsys->uuconf_palloc); if (iret != UUCONF_SUCCESS) iret |= UUCONF_CMDTABRET_EXIT; return iret; } /* Handle the "baud-range" command, also known as "speed-range". */ static int iibaud_range (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_system *qsys = (struct uuconf_system *) pvar; int iret; iret = _uuconf_iint (qglobal, argv[1], (pointer) &qsys->uuconf_ibaud, FALSE); if (iret != UUCONF_SUCCESS) return iret; return _uuconf_iint (qglobal, argv[2], (pointer) &qsys->uuconf_ihighbaud, FALSE); } /* Handle one of the size commands ("call-local-size", etc.). The first argument is a number of bytes, and the second argument is a time string. The pvar argument points to the string array to which we add this new string. */ /*ARGSUSED*/ static int iisize (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_timespan **pqspan = (struct uuconf_timespan **) pvar; struct sinfo *qinfo = (struct sinfo *) pinfo; long ival; int iret; iret = _uuconf_iint (qglobal, argv[1], (pointer) &ival, FALSE); if (iret != UUCONF_SUCCESS) return iret; iret = _uuconf_itime_parse (qglobal, argv[2], ival, 0, iisizecmp, pqspan, qinfo->qsys->uuconf_palloc); if (iret != UUCONF_SUCCESS) iret |= UUCONF_CMDTABRET_EXIT; return iret; } /* A comparison function for sizes to pass to _uuconf_itime_parse. */ static int iisizecmp (i1, i2) long i1; long i2; { /* We can't just return i1 - i2 because that would be a long. */ if (i1 < i2) return -1; else if (i1 == i2) return 0; else return 1; } /* Handle the "port" command. If there is one argument, this names a port. Otherwise, the remaining arguments form a command describing the port. */ /*ARGSUSED*/ static int iiport (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; if (argc < 2) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; else if (argc == 2) { qinfo->qsys->uuconf_zport = argv[1]; return UUCONF_CMDTABRET_KEEP; } else { int iret; if (qinfo->qsys->uuconf_qport == (struct uuconf_port *) &_uuconf_unset) { struct uuconf_port *qnew; qnew = ((struct uuconf_port *) uuconf_malloc (qinfo->qsys->uuconf_palloc, sizeof (struct uuconf_port))); if (qnew == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } _uuconf_uclear_port (qnew); if (qinfo->qsys->uuconf_zname == NULL) qnew->uuconf_zname = (char *) "default system file port"; else { char *zname; size_t clen; clen = strlen (qinfo->qsys->uuconf_zname); zname = (char *) uuconf_malloc (qinfo->qsys->uuconf_palloc, clen + sizeof "system port"); if (zname == NULL) { qglobal->ierrno = errno; return (UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO | UUCONF_CMDTABRET_EXIT); } memcpy ((pointer) zname, (pointer) "system ", sizeof "system " - 1); memcpy ((pointer) (zname + sizeof "system " - 1), (pointer) qinfo->qsys->uuconf_zname, clen); memcpy ((pointer) (zname + sizeof "system " - 1 + clen), (pointer) " port", sizeof " port"); qnew->uuconf_zname = zname; } qnew->uuconf_palloc = qinfo->qsys->uuconf_palloc; qinfo->qsys->uuconf_qport = qnew; } iret = _uuconf_iport_cmd (qglobal, argc - 1, argv + 1, qinfo->qsys->uuconf_qport); if (UUCONF_ERROR_VALUE (iret) != UUCONF_SUCCESS) iret |= UUCONF_CMDTABRET_EXIT; return iret; } } /* Handle the "chat" and "called-chat" set of commands. These just hand off to the generic chat script function. */ static int iichat (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; struct uuconf_chat *qchat = (struct uuconf_chat *) pvar; int iret; iret = _uuconf_ichat_cmd (qglobal, argc, argv, qchat, qinfo->qsys->uuconf_palloc); if (UUCONF_ERROR_VALUE (iret) != UUCONF_SUCCESS) iret |= UUCONF_CMDTABRET_EXIT; return iret; } /* Local interface to the _uuconf_idebug_cmd function, which handles the "debug" command. */ static int iidebug (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; char **pzdebug = (char **) pvar; return _uuconf_idebug_cmd (qglobal, pzdebug, argc, argv, qinfo->qsys->uuconf_palloc); } /* Handle the "called-login" command. This only needs to be in a function because there can be additional arguments listing the remote systems which are permitted to use this login name. The additional arguments are not actually handled here; they are handled by uuconf_taylor_system_names, which already has to go through all the system files. */ /*ARGSUSED*/ static int iicalled_login (pglobal, argc, argv, pvar, pinfo) pointer pglobal ATTRIBUTE_UNUSED; int argc; char **argv; pointer pvar; pointer pinfo ATTRIBUTE_UNUSED; { char **pz = (char **) pvar; if (argc < 2) return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; *pz = argv[1]; return UUCONF_CMDTABRET_KEEP; } /* Handle the "protocol-parameter" command. This just hands off to the generic protocol parameter handler. */ static int iiproto_param (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_proto_param **pqparam = (struct uuconf_proto_param **) pvar; struct sinfo *qinfo = (struct sinfo *) pinfo; if (*pqparam == (struct uuconf_proto_param *) &_uuconf_unset) *pqparam = NULL; return _uuconf_iadd_proto_param (qglobal, argc - 1, argv + 1, pqparam, qinfo->qsys->uuconf_palloc); } /* Handle the "request" command. This is equivalent to specifying both "call-request" and "called-request". */ /*ARGSUSED*/ static int iirequest (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; int iret; iret = _uuconf_iboolean (qglobal, argv[1], &qinfo->qsys->uuconf_fsend_request); if (UUCONF_ERROR_VALUE (iret) == UUCONF_SUCCESS) qinfo->qsys->uuconf_frec_request = qinfo->qsys->uuconf_fsend_request; return iret; } /* Handle the "transfer" command. This is equivalent to specifying both "call-transfer" and "called-transfer". */ /*ARGSUSED*/ static int iitransfer (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc ATTRIBUTE_UNUSED; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; int iret; iret = _uuconf_iboolean (qglobal, argv[1], &qinfo->qsys->uuconf_fcall_transfer); if (UUCONF_ERROR_VALUE (iret) == UUCONF_SUCCESS) qinfo->qsys->uuconf_fcalled_transfer = qinfo->qsys->uuconf_fcall_transfer; return iret; } /* Handle the "forward" command. This is equivalent to specifying both "forward-from" and "forward-to". */ /*ARGSUSED*/ static int iiforward (pglobal, argc, argv, pvar, pinfo) pointer pglobal; int argc; char **argv; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct sinfo *qinfo = (struct sinfo *) pinfo; struct uuconf_system *qsys; int i; int iret; qsys = qinfo->qsys; qsys->uuconf_pzforward_from = NULL; qsys->uuconf_pzforward_to = NULL; for (i = 1; i < argc; i++) { iret = _uuconf_iadd_string (qglobal, argv[i], FALSE, FALSE, &qsys->uuconf_pzforward_to, qsys->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret | UUCONF_CMDTABRET_KEEP | UUCONF_CMDTABRET_EXIT; iret = _uuconf_iadd_string (qglobal, argv[i], FALSE, FALSE, &qsys->uuconf_pzforward_from, qsys->uuconf_palloc); if (iret != UUCONF_SUCCESS) return iret | UUCONF_CMDTABRET_KEEP | UUCONF_CMDTABRET_EXIT; } return UUCONF_CMDTABRET_KEEP; } /* Handle an unknown command. This should probably be done more intelligently. */ /*ARGSUSED*/ static int iiunknown (pglobal, argc, argv, pvar, pinfo) pointer pglobal ATTRIBUTE_UNUSED; int argc ATTRIBUTE_UNUSED; char **argv ATTRIBUTE_UNUSED; pointer pvar ATTRIBUTE_UNUSED; pointer pinfo ATTRIBUTE_UNUSED; { return UUCONF_SYNTAX_ERROR | UUCONF_CMDTABRET_EXIT; } /* Return information for an unknown system. It would be better to put this in a different file, but it would require breaking several functions out of this file. Perhaps I will do it sometime. */ int uuconf_taylor_system_unknown (pglobal, qsys) pointer pglobal; struct uuconf_system *qsys; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct uuconf_cmdtab as[CSYSTEM_CMDS]; struct sinfo si; struct sunknown *q; int iret; if (qglobal->qprocess->qunknown == NULL) return UUCONF_NOT_FOUND; _uuconf_ucmdtab_base (asIcmds, CSYSTEM_CMDS, (char *) qsys, as); _uuconf_uclear_system (qsys); si.qsys = qsys; si.falternates = FALSE; si.fdefault_alternates = TRUE; qsys->uuconf_palloc = uuconf_malloc_block (); if (qsys->uuconf_palloc == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } for (q = qglobal->qprocess->qunknown; q != NULL; q = q->qnext) { iret = uuconf_cmd_args (pglobal, q->cargs, q->pzargs, as, (pointer) &si, iiunknown, UUCONF_CMDTABFLAG_BACKSLASH, qsys->uuconf_palloc); iret &=~ UUCONF_CMDTABRET_KEEP; if (UUCONF_ERROR_VALUE (iret) != UUCONF_SUCCESS) { qglobal->zfilename = qglobal->qprocess->zconfigfile; qglobal->ilineno = q->ilineno; return ((iret &~ UUCONF_CMDTABRET_EXIT) | UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO); } if ((iret & UUCONF_CMDTABRET_EXIT) != 0) break; } if (! si.falternates) uiset_call (qsys); else { iret = iialternate (pglobal, 0, (char **) NULL, (pointer) NULL, (pointer) &si); if (iret != UUCONF_SUCCESS) return iret; } /* The first alternate is always available for calling in. */ qsys->uuconf_fcalled = TRUE; return _uuconf_isystem_basic_default (qglobal, qsys); } uucp-1.07/uuconf/tsnams.c0000664000076400007640000000453407665321762011104 /* tsnams.c Get all known system names from the Taylor UUCP configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tsnams_rcsid[] = "$Id: tsnams.c,v 1.6 2002/03/05 19:10:43 ian Rel $"; #endif /* Get all the system names from the Taylor UUCP configuration files. These were actually already recorded by uuconf_taylor_init, so this function is pretty simple. */ int uuconf_taylor_system_names (pglobal, ppzsystems, falias) pointer pglobal; char ***ppzsystems; int falias; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; register struct stsysloc *q; char **pz; int c, i; if (! qglobal->qprocess->fread_syslocs) { iret = _uuconf_iread_locations (qglobal); if (iret != UUCONF_SUCCESS) return iret; } *ppzsystems = NULL; c = 0; for (q = qglobal->qprocess->qsyslocs; q != NULL; q = q->qnext) { if (! falias && q->falias) continue; iret = _uuconf_iadd_string (qglobal, (char *) q->zname, TRUE, FALSE, ppzsystems, (pointer) NULL); if (iret != UUCONF_SUCCESS) return iret; ++c; } /* The order of the qSyslocs list is reversed from the list in the configuration files. Reverse the returned list in order to make uuname output more intuitive. */ pz = *ppzsystems; for (i = c / 2 - 1; i >= 0; i--) { char *zhold; zhold = pz[i]; pz[i] = pz[c - i - 1]; pz[c - i - 1] = zhold; } return UUCONF_SUCCESS; } uucp-1.07/uuconf/tsys.c0000664000076400007640000000321407665321762010573 /* tsys.c User function to get a system from the Taylor UUCP configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tsys_rcsid[] = "$Id: tsys.c,v 1.6 2002/03/05 19:10:43 ian Rel $"; #endif /* Get system information from the Taylor UUCP configuration files. This is a wrapper for the internal function which makes sure that every field gets a default value. */ int uuconf_taylor_system_info (pglobal, zsystem, qsys) pointer pglobal; const char *zsystem; struct uuconf_system *qsys; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; iret = _uuconf_itaylor_system_internal (qglobal, zsystem, qsys); if (iret != UUCONF_SUCCESS) return iret; return _uuconf_isystem_basic_default (qglobal, qsys); } uucp-1.07/uuconf/tval.c0000664000076400007640000000407507665321762010545 /* tval.c Validate a login name for a system using Taylor UUCP files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_tval_rcsid[] = "$Id: tval.c,v 1.6 2002/03/05 19:10:43 ian Rel $"; #endif /* Validate a login name for a system using Taylor UUCP configuration files. This assumes that the zcalled_login field is either NULL or "ANY". If makes sure that the login name does not appear in some other "called-login" command listing systems not including this one. */ int uuconf_taylor_validate (pglobal, qsys, zlogin) pointer pglobal; const struct uuconf_system *qsys; const char *zlogin; { struct sglobal *qglobal = (struct sglobal *) pglobal; struct svalidate *q; if (! qglobal->qprocess->fread_syslocs) { int iret; iret = _uuconf_iread_locations (qglobal); if (iret != UUCONF_SUCCESS) return iret; } for (q = qglobal->qprocess->qvalidate; q != NULL; q = q->qnext) { if (strcmp (q->zlogname, zlogin) == 0) { char **pz; for (pz = q->pzmachines; *pz != NULL; pz++) if (strcmp (*pz, qsys->uuconf_zname) == 0) return UUCONF_SUCCESS; return UUCONF_NOT_FOUND; } } return UUCONF_SUCCESS; } uucp-1.07/uuconf/ugtlin.c0000664000076400007640000000476707665321762011111 /* ugtlin.c Read a line with backslash continuations. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_ugtlin_rcsid[] = "$Id: ugtlin.c,v 1.9 2002/03/05 19:10:43 ian Rel $"; #endif /* Read a line from a file with backslash continuations. This updates the qglobal->ilineno count for each additional line it reads. */ int _uuconf_getline (qglobal, pzline, pcline, e) struct sglobal *qglobal; char **pzline; size_t *pcline; FILE *e; { int ctot; char *zline; size_t cline; ctot = -1; zline = NULL; cline = 0; while (TRUE) { int cchars; if (ctot < 0) cchars = getline (pzline, pcline, e); else cchars = getline (&zline, &cline, e); if (cchars < 0) { if (zline != NULL) free ((pointer) zline); if (ctot >= 0) return ctot; else return cchars; } if (ctot < 0) ctot = cchars; else { if (*pcline <= (size_t) (ctot + cchars)) { char *znew; if (*pcline > 0) znew = (char *) realloc ((pointer) *pzline, (size_t) (ctot + cchars + 1)); else znew = (char *) malloc ((size_t) (ctot + cchars + 1)); if (znew == NULL) { free ((pointer) zline); return -1; } *pzline = znew; *pcline = ctot + cchars + 1; } memcpy ((pointer) ((*pzline) + ctot), (pointer) zline, (size_t) (cchars + 1)); ctot += cchars; } if (ctot < 2 || (*pzline)[ctot - 1] != '\n' || (*pzline)[ctot - 2] != '\\') { if (zline != NULL) free ((pointer) zline); return ctot; } ++qglobal->ilineno; ctot -= 2; (*pzline)[ctot] = '\0'; } } uucp-1.07/uuconf/unk.c0000664000076400007640000000445707665321762010400 /* unk.c Get information about an unknown system. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_unk_rcsid[] = "$Id: unk.c,v 1.6 2002/03/05 19:10:43 ian Rel $"; #endif #include /* Get information about an unknown system. If we are using HAVE_TAYLOR_CONFIG, we just use it. Otherwise if we are using HAVE_HDB_CONFIG, we use it. Otherwise we return a default system. This isn't right for HAVE_V2_CONFIG, because it is possible to specify default directories to read and write in USERFILE. However, I'm not going to bother to write that code unless somebody actually wants it. */ /*ARGSUSED*/ int uuconf_system_unknown (pglobal, qsys) pointer pglobal; struct uuconf_system *qsys; { #if HAVE_TAYLOR_CONFIG return uuconf_taylor_system_unknown (pglobal, qsys); #else /* ! HAVE_TAYLOR_CONFIG */ #if HAVE_HDB_CONFIG return uuconf_hdb_system_unknown (pglobal, qsys); #else /* ! HAVE_HDB_CONFIG */ #if HAVE_V2_CONFIG struct sglobal *qglobal = (struct sglobal *) pglobal; _uuconf_uclear_system (qsys); qsys->uuconf_palloc = uuconf_malloc_block (); if (qsys->uuconf_palloc == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } return _uuconf_isystem_basic_default (qglobal, qsys); #else /* ! HAVE_V2_CONFIG */ return UUCONF_NOT_FOUND; #endif /* ! HAVE_V2_CONFIG */ #endif /* ! HAVE_HDB_CONFIG */ #endif /* ! HAVE_TAYLOR_CONFIG */ } uucp-1.07/uuconf/val.c0000664000076400007640000000255407665321762010361 /* val.c Validate a login name for a system. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_val_rcsid[] = "$Id: val.c,v 1.6 2002/03/05 19:10:43 ian Rel $"; #endif /* Validate a login name for a system. */ /*ARGSUSED*/ int uuconf_validate (pglobal, qsys, zlogin) pointer pglobal; const struct uuconf_system *qsys; const char *zlogin; { #if HAVE_TAYLOR_CONFIG return uuconf_taylor_validate (pglobal, qsys, zlogin); #else return UUCONF_SUCCESS; #endif } uucp-1.07/uuconf/vinit.c0000664000076400007640000000624207665321762010726 /* vinit.c Initialize for reading V2 configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_vinit_rcsid[] = "$Id: vinit.c,v 1.6 2002/03/05 19:10:43 ian Rel $"; #endif #include static int ivinlib P((struct sglobal *qglobal, const char *z, size_t csize, char **pz)); /* Return an allocated buffer holding a file name in OLDCONFIGLIB. The c argument is the size of z including the trailing null byte, since this is convenient for both the caller and this function. */ static int ivinlib (qglobal, z, c, pz) struct sglobal *qglobal; const char *z; size_t c; char **pz; { char *zalc; zalc = uuconf_malloc (qglobal->pblock, sizeof OLDCONFIGLIB - 1 + c); if (zalc == NULL) { qglobal->ierrno = errno; return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } memcpy ((pointer) zalc, (pointer) OLDCONFIGLIB, sizeof OLDCONFIGLIB - 1); memcpy ((pointer) (zalc + sizeof OLDCONFIGLIB - 1), (pointer) z, c); *pz = zalc; return UUCONF_SUCCESS; } /* Initialize the routines which read V2 configuration files. The only thing we do here is allocate the file names. */ int uuconf_v2_init (ppglobal) pointer *ppglobal; { struct sglobal **pqglobal = (struct sglobal **) ppglobal; int iret; struct sglobal *qglobal; char *zdialcodes; if (*pqglobal == NULL) { iret = _uuconf_iinit_global (pqglobal); if (iret != UUCONF_SUCCESS) return iret; } qglobal = *pqglobal; iret = ivinlib (qglobal, V2_SYSTEMS, sizeof V2_SYSTEMS, &qglobal->qprocess->zv2systems); if (iret != UUCONF_SUCCESS) return iret; iret = ivinlib (qglobal, V2_DEVICES, sizeof V2_DEVICES, &qglobal->qprocess->zv2devices); if (iret != UUCONF_SUCCESS) return iret; iret = ivinlib (qglobal, V2_USERFILE, sizeof V2_USERFILE, &qglobal->qprocess->zv2userfile); if (iret != UUCONF_SUCCESS) return iret; iret = ivinlib (qglobal, V2_CMDS, sizeof V2_CMDS, &qglobal->qprocess->zv2cmds); if (iret != UUCONF_SUCCESS) return iret; iret = ivinlib (qglobal, V2_DIALCODES, sizeof V2_DIALCODES, &zdialcodes); if (iret != UUCONF_SUCCESS) return iret; return _uuconf_iadd_string (qglobal, zdialcodes, FALSE, FALSE, &qglobal->qprocess->pzdialcodefiles, qglobal->pblock); } uucp-1.07/uuconf/vport.c0000664000076400007640000001516707665321762010755 /* vport.c Find a port in the V2 configuration files. Copyright (C) 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_vport_rcsid[] = "$Id: vport.c,v 1.12 2002/03/05 19:10:43 ian Rel $"; #endif #include #include /* Find a port in the V2 configuration files by name, baud rate, and special purpose function. */ int uuconf_v2_find_port (pglobal, zname, ibaud, ihighbaud, pifn, pinfo, qport) pointer pglobal; const char *zname; long ibaud; long ihighbaud ATTRIBUTE_UNUSED; int (*pifn) P((struct uuconf_port *, pointer)); pointer pinfo; struct uuconf_port *qport; { struct sglobal *qglobal = (struct sglobal *) pglobal; FILE *e; char *zline; size_t cline; char **pzsplit; size_t csplit; int iret; int cchars; e = fopen (qglobal->qprocess->zv2devices, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) return UUCONF_NOT_FOUND; qglobal->ierrno = errno; qglobal->zfilename = qglobal->qprocess->zv2devices; return (UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO | UUCONF_ERROR_FILENAME); } zline = NULL; cline = 0; pzsplit = NULL; csplit = 0; iret = UUCONF_NOT_FOUND; qglobal->ilineno = 0; while ((cchars = getline (&zline, &cline, e)) > 0) { int ctoks; char *zend; long ilow, ihigh; pointer pblock; ++qglobal->ilineno; iret = UUCONF_NOT_FOUND; --cchars; if (zline[cchars] == '\n') zline[cchars] = '\0'; zline[strcspn (zline, "#")] = '\0'; ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); if (ctoks < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } /* An entry in L-devices is type device dial-device baud dialer The type (normally "ACU") is treated as the name. */ /* If there aren't enough entries, ignore the line; this should probably do something more useful. */ if (ctoks < 4) continue; /* Make sure the name matches any argument. */ if (zname != NULL && strcmp (pzsplit[0], zname) != 0) continue; /* Get the baud rate. */ ilow = strtol (pzsplit[3], &zend, 10); if (*zend == '-') ihigh = strtol (zend + 1, (char **) NULL, 10); else ihigh = ilow; /* Make sure the baud rate matches any argument. */ if (ibaud != 0 && ilow != 0 && (ilow > ibaud || ihigh < ibaud)) continue; /* Now we must construct the port information, so that we can pass it to pifn. The port type is determined by it's name, unfortunately. The name "DIR" is used for a direct port, and anything else for a modem port. */ pblock = NULL; _uuconf_uclear_port (qport); qport->uuconf_zname = pzsplit[0]; if (strcmp (pzsplit[0], "DIR") == 0) { qport->uuconf_ttype = UUCONF_PORTTYPE_DIRECT; qport->uuconf_u.uuconf_sdirect.uuconf_zdevice = pzsplit[1]; qport->uuconf_u.uuconf_sdirect.uuconf_ibaud = ilow; qport->uuconf_u.uuconf_sdirect.uuconf_fcarrier = FALSE; qport->uuconf_u.uuconf_sdirect.uuconf_fhardflow = TRUE; } else { qport->uuconf_ttype = UUCONF_PORTTYPE_MODEM; qport->uuconf_u.uuconf_smodem.uuconf_zdevice = pzsplit[1]; if (strcmp (pzsplit[2], "-") != 0) qport->uuconf_u.uuconf_smodem.uuconf_zdial_device = pzsplit[2]; else qport->uuconf_u.uuconf_smodem.uuconf_zdial_device = NULL; if (ilow == ihigh) { qport->uuconf_u.uuconf_smodem.uuconf_ibaud = ilow; qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud = 0L; qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud = 0L; } else { qport->uuconf_u.uuconf_smodem.uuconf_ibaud = 0L; qport->uuconf_u.uuconf_smodem.uuconf_ilowbaud = ilow; qport->uuconf_u.uuconf_smodem.uuconf_ihighbaud = ihigh; } qport->uuconf_u.uuconf_smodem.uuconf_fcarrier = TRUE; qport->uuconf_u.uuconf_smodem.uuconf_fhardflow = TRUE; if (ctoks < 5) qport->uuconf_u.uuconf_smodem.uuconf_pzdialer = NULL; else { size_t c; char **pzd; /* We support dialer/token pairs, although normal V2 doesn't. */ pblock = uuconf_malloc_block (); if (pblock == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } c = (ctoks - 4) * sizeof (char *); pzd = (char **) uuconf_malloc (pblock, c + sizeof (char *)); if (pzd == NULL) { qglobal->ierrno = errno; uuconf_free_block (pblock); iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } memcpy ((pointer) pzd, (pointer) (pzsplit + 4), c); pzd[ctoks - 4] = NULL; qport->uuconf_u.uuconf_smodem.uuconf_pzdialer = pzd; } qport->uuconf_u.uuconf_smodem.uuconf_qdialer = NULL; } if (pifn != NULL) { iret = (*pifn) (qport, pinfo); if (iret != UUCONF_SUCCESS) { if (pblock != NULL) uuconf_free_block (pblock); if (iret != UUCONF_NOT_FOUND) break; continue; } } /* This is the port we want. */ if (pblock == NULL) { pblock = uuconf_malloc_block (); if (pblock == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } } if (uuconf_add_block (pblock, zline) != 0) { qglobal->ierrno = errno; uuconf_free_block (pblock); iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } zline = NULL; qport->uuconf_palloc = pblock; break; } (void) fclose (e); if (zline != NULL) free ((pointer) zline); if (pzsplit != NULL) free ((pointer) pzsplit); if (iret != UUCONF_SUCCESS && iret != UUCONF_NOT_FOUND) { qglobal->zfilename = qglobal->qprocess->zv2devices; iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; } return iret; } uucp-1.07/uuconf/vsinfo.c0000664000076400007640000003607207665321762011105 /* vsinfo.c Get information about a system from the V2 configuration files. Copyright (C) 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_vsinfo_rcsid[] = "$Id: vsinfo.c,v 1.17 2002/03/05 19:10:43 ian Rel $"; #endif #include #include /* Get the information for a particular system from the V2 configuration files. This does not make sure that all the default values are set. */ int _uuconf_iv2_system_internal (qglobal, zsystem, qsys) struct sglobal *qglobal; const char *zsystem; struct uuconf_system *qsys; { char *zline; size_t cline; char **pzsplit; size_t csplit; char **pzcomma; size_t ccomma; FILE *e; int cchars; pointer pblock; int iret; e = fopen (qglobal->qprocess->zv2systems, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) return UUCONF_NOT_FOUND; qglobal->ierrno = errno; qglobal->zfilename = qglobal->qprocess->zv2systems; return (UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO | UUCONF_ERROR_FILENAME); } zline = NULL; cline = 0; pzsplit = NULL; csplit = 0; pzcomma = NULL; ccomma = 0; pblock = NULL; iret = UUCONF_SUCCESS; qglobal->ilineno = 0; while ((cchars = _uuconf_getline (qglobal, &zline, &cline, e)) > 0) { int ctoks, ctimes, i; struct uuconf_system *qset; char *z, *zretry; int cretry; ++qglobal->ilineno; --cchars; if (zline[cchars] == '\n') zline[cchars] = '\0'; zline[strcspn (zline, "#")] = '\0'; ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); if (ctoks < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } /* If this isn't the system we're looking for, keep reading the file. */ if (ctoks < 1 || strcmp (zsystem, pzsplit[0]) != 0) continue; /* If this is the first time we've found the system, we want to set *qsys directly. Otherwise, we allocate a new alternate. */ if (pblock == NULL) { pblock = uuconf_malloc_block (); if (pblock == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } _uuconf_uclear_system (qsys); qsys->uuconf_palloc = pblock; qset = qsys; } else { struct uuconf_system **pq; qset = ((struct uuconf_system *) uuconf_malloc (pblock, sizeof (struct uuconf_system))); if (qset == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } _uuconf_uclear_system (qset); for (pq = &qsys->uuconf_qalternate; *pq != NULL; pq = &(*pq)->uuconf_qalternate) ; *pq = qset; } /* Add this line to the memory block we are building for the system. */ if (uuconf_add_block (pblock, zline) != 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } zline = NULL; cline = 0; /* The format of a line in Systems is system time device speed phone chat For example, airs Any ACU 9600 5551212 ogin: foo pass: bar */ /* Get the system name. */ qset->uuconf_zname = pzsplit[0]; qset->uuconf_fcall = TRUE; qset->uuconf_fcalled = TRUE; if (ctoks < 2) continue; /* A time string is "time/grade,time/grade;retry". A missing grade is taken as BGRADE_LOW. On some versions the retry time is actually separated by a comma, which won't work right here. */ zretry = strchr (pzsplit[1], ';'); if (zretry == NULL) cretry = 55; else { *zretry = '\0'; cretry = (int) strtol (zretry + 1, (char **) NULL, 10); } ctimes = _uuconf_istrsplit (pzsplit[1], ',', &pzcomma, &ccomma); if (ctimes < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } for (i = 0; i < ctimes; i++) { char *zslash; char bgrade; z = pzcomma[i]; zslash = strchr (z, '/'); if (zslash == NULL) bgrade = UUCONF_GRADE_LOW; else { *zslash = '\0'; bgrade = zslash[1]; if (! UUCONF_GRADE_LEGAL (bgrade)) bgrade = UUCONF_GRADE_LOW; } iret = _uuconf_itime_parse (qglobal, z, (long) bgrade, cretry, _uuconf_itime_grade_cmp, &qset->uuconf_qtimegrade, pblock); /* We treat a syntax error in the time field as equivalent to ``never'', on the assumption that that is what V2 does. */ if (iret == UUCONF_SYNTAX_ERROR) iret = UUCONF_SUCCESS; if (iret != UUCONF_SUCCESS) break; /* Treat any time/grade setting as both a timegrade and a call-timegrade. */ if (bgrade != UUCONF_GRADE_LOW) qset->uuconf_qcalltimegrade = qset->uuconf_qtimegrade; } if (iret != UUCONF_SUCCESS) break; if (ctoks < 3) continue; /* Pick up the device name. It can be followed by a comma and a list of protocols (this is not actually supported by most V2 systems, but it should be compatible). */ qset->uuconf_zport = pzsplit[2]; z = strchr (pzsplit[2], ','); if (z != NULL) { qset->uuconf_zprotocols = z + 1; *z = '\0'; } /* If the port is "TCP", we set up a system specific port. The baud rate becomes the service number and the phone number becomes the address (still stored in qsys->zphone). */ if (strcmp (qset->uuconf_zport, "TCP") == 0) { qset->uuconf_zport = NULL; qset->uuconf_qport = ((struct uuconf_port *) uuconf_malloc (pblock, sizeof (struct uuconf_port))); if (qset->uuconf_qport == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } _uuconf_uclear_port (qset->uuconf_qport); qset->uuconf_qport->uuconf_zname = (char *) "TCP"; qset->uuconf_qport->uuconf_ttype = UUCONF_PORTTYPE_TCP; qset->uuconf_qport->uuconf_ireliable = (UUCONF_RELIABLE_ENDTOEND | UUCONF_RELIABLE_RELIABLE | UUCONF_RELIABLE_EIGHT | UUCONF_RELIABLE_FULLDUPLEX | UUCONF_RELIABLE_SPECIFIED); if (ctoks < 4) qset->uuconf_qport->uuconf_u.uuconf_stcp.uuconf_zport = (char *) "uucp"; else qset->uuconf_qport->uuconf_u.uuconf_stcp.uuconf_zport = pzsplit[3]; qset->uuconf_qport->uuconf_u.uuconf_stcp.uuconf_iversion = 0; qset->uuconf_qport->uuconf_u.uuconf_stcp.uuconf_pzdialer = NULL; } if (ctoks < 4) continue; qset->uuconf_ibaud = strtol (pzsplit[3], (char **) NULL, 10); if (ctoks < 5) continue; /* Get the phone number. */ qset->uuconf_zphone = pzsplit[4]; if (ctoks < 6) continue; /* Get the chat script. We just hand this off to the chat script processor, so that it will parse subsend and subexpect strings correctly. */ pzsplit[4] = (char *) "chat"; iret = _uuconf_ichat_cmd (qglobal, ctoks - 4, pzsplit + 4, &qset->uuconf_schat, pblock); iret &=~ UUCONF_CMDTABRET_KEEP; if (iret != UUCONF_SUCCESS) break; } (void) fclose (e); if (pzcomma != NULL) free ((pointer) pzcomma); if (iret != UUCONF_SUCCESS) { if (zline != NULL) free ((pointer) zline); if (pzsplit != NULL) free ((pointer) pzsplit); qglobal->zfilename = qglobal->qprocess->zv2systems; return iret | UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; } if (pblock == NULL) { if (zline != NULL) free ((pointer) zline); if (pzsplit != NULL) free ((pointer) pzsplit); return UUCONF_NOT_FOUND; } /* Now read USERFILE and L.cmds to get permissions. We can't fully handle USERFILE since that specifies permissions based on local users which we do not support. */ { e = fopen (qglobal->qprocess->zv2userfile, "r"); if (e != NULL) { char **pzlocal, **pzremote; boolean fdefault_callback; char *zdefault_login; struct uuconf_system *q; pzlocal = NULL; pzremote = NULL; fdefault_callback = FALSE; zdefault_login = NULL; qglobal->ilineno = 0; while ((cchars = getline (&zline, &cline, e)) > 0) { int ctoks; char *zcomma; boolean fcallback; char **pzlist, **pznew; ++qglobal->ilineno; --cchars; if (zline[cchars] == '\n') zline[cchars] = '\0'; zline[strcspn (zline, "#")] = '\0'; ctoks = _uuconf_istrsplit (zline, '\0', &pzsplit, &csplit); if (ctoks < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } if (ctoks == 0) continue; /* The first field is username,machinename */ zcomma = strchr (pzsplit[0], ','); if (zcomma == NULL) continue; *zcomma++ = '\0'; /* The rest of the line is the list of directories, except that if the first directory is "c" we must call the system back. */ fcallback = FALSE; pzlist = pzsplit + 1; --ctoks; if (ctoks > 0 && pzsplit[1][0] == 'c' && pzsplit[1][1] == '\0') { fcallback = TRUE; pzlist = pzsplit + 2; --ctoks; } /* Now pzsplit[0] is the user name, zcomma is the system name, fcallback indicates whether a call back is required, ctoks is the number of directories and pzlist points to the directories. If the system name matches, then the user name is the name that the system must use to log in, and the list of directories is what may be transferred in by either local or remote request. Otherwise, if no system name matches, then the first line with no user name gives the list of directories that may be transferred by local request, and the first line with no system name gives the list of directories that may be transferred by remote request. */ if ((pzsplit[0][0] != '\0' || pzlocal != NULL) && (zcomma[0] != '\0' || pzremote != NULL) && strcmp (zcomma, zsystem) != 0) continue; /* NULL terminate the list of directories. */ pznew = (char **) uuconf_malloc (pblock, (ctoks + 1) * sizeof (char *)); if (pznew == NULL) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } memcpy ((pointer) pznew, (pointer) pzlist, ctoks * sizeof (char *)); pznew[ctoks] = NULL; if (uuconf_add_block (pblock, zline) != 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; break; } zline = NULL; cline = 0; if (pzsplit[0][0] == '\0') { pzlocal = pznew; fdefault_callback = fcallback; } else if (zcomma[0] == '\0') { pzremote = pznew; zdefault_login = pzsplit[0]; } else { /* Both the login name and the machine name were listed; require the machine to be logged in under this name. This is not fully backward compatible, and perhaps should be changed. On the other hand, it is more useful. */ for (q = qsys; q != NULL; q = q->uuconf_qalternate) { q->uuconf_zcalled_login = pzsplit[0]; q->uuconf_fcallback = fcallback; q->uuconf_pzlocal_send = pznew; q->uuconf_pzlocal_receive = pznew; q->uuconf_pzremote_send = pznew; q->uuconf_pzremote_receive = pznew; } break; } } (void) fclose (e); if (iret != UUCONF_SUCCESS) { if (zline != NULL) free ((pointer) zline); if (pzsplit != NULL) free ((pointer) pzsplit); qglobal->zfilename = qglobal->qprocess->zv2userfile; return iret | UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; } if (qsys->uuconf_pzlocal_send == (char **) &_uuconf_unset && pzlocal != NULL) { for (q = qsys; q != NULL; q = q->uuconf_qalternate) { q->uuconf_fcallback = fdefault_callback; q->uuconf_pzlocal_send = pzlocal; q->uuconf_pzlocal_receive = pzlocal; } } if (qsys->uuconf_pzremote_send == (char **) &_uuconf_unset && pzremote != NULL) { for (q = qsys; q != NULL; q = q->uuconf_qalternate) { q->uuconf_zcalled_login = zdefault_login; q->uuconf_pzremote_send = pzremote; q->uuconf_pzremote_receive = pzremote; } } } } /* Now we must read L.cmds to determine which commands may be executed. */ { e = fopen (qglobal->qprocess->zv2cmds, "r"); if (e != NULL) { qglobal->ilineno = 0; if (getline (&zline, &cline, e) > 0) { ++qglobal->ilineno; zline[strcspn (zline, "#\n")] = '\0'; while (*zline == '\0') { if (getline (&zline, &cline, e) <= 0) { if (zline != NULL) { free ((pointer) zline); zline = NULL; } } else { ++qglobal->ilineno; zline[strcspn (zline, "#\n")] = '\0'; } } if (zline != NULL && strncmp (zline, "PATH=", sizeof "PATH=" - 1) == 0) { int ctoks; char **pznew; zline += sizeof "PATH=" - 1; ctoks = _uuconf_istrsplit (zline, ':', &pzsplit, &csplit); if (ctoks < 0) { qglobal->ierrno = errno; iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } pznew = NULL; if (iret == UUCONF_SUCCESS) { pznew = ((char **) uuconf_malloc (pblock, (ctoks + 1) * sizeof (char *))); if (pznew == NULL) iret = UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO; } if (iret == UUCONF_SUCCESS) { memcpy ((pointer) pznew, (pointer) pzsplit, ctoks * sizeof (char *)); pznew[ctoks] = NULL; qsys->uuconf_pzpath = pznew; zline = NULL; cline = 0; } if (getline (&zline, &cline, e) < 0) { if (zline != NULL) { free ((pointer) zline); zline = NULL; } } else ++qglobal->ilineno; } } if (iret == UUCONF_SUCCESS && zline != NULL) { while (TRUE) { zline[strcspn (zline, "#,\n")] = '\0'; if (*zline != '\0') { iret = _uuconf_iadd_string (qglobal, zline, TRUE, FALSE, &qsys->uuconf_pzcmds, pblock); if (iret != UUCONF_SUCCESS) break; } if (getline (&zline, &cline, e) < 0) break; ++qglobal->ilineno; } } (void) fclose (e); if (iret != UUCONF_SUCCESS) { qglobal->zfilename = qglobal->qprocess->zv2cmds; iret |= UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; } } } if (zline != NULL) free ((pointer) zline); if (pzsplit != NULL) free ((pointer) pzsplit); return iret; } uucp-1.07/uuconf/vsnams.c0000664000076400007640000000564107665321762011106 /* vsnams.c Get all known system names from the V2 configuration files. Copyright (C) 1992, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_vsnams_rcsid[] = "$Id: vsnams.c,v 1.11 2002/03/05 19:10:43 ian Rel $"; #endif #include /* Get all the system names from the V2 L.sys file. This code does not support aliases, although some V2 versions do have an L-aliases file. */ /*ARGSUSED*/ int uuconf_v2_system_names (pglobal, ppzsystems, falias) pointer pglobal; char ***ppzsystems; int falias ATTRIBUTE_UNUSED; { struct sglobal *qglobal = (struct sglobal *) pglobal; FILE *e; int iret; char *zline; size_t cline; *ppzsystems = NULL; e = fopen (qglobal->qprocess->zv2systems, "r"); if (e == NULL) { if (FNO_SUCH_FILE ()) return _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, ppzsystems, (pointer) NULL); qglobal->ierrno = errno; qglobal->zfilename = qglobal->qprocess->zv2systems; return (UUCONF_FOPEN_FAILED | UUCONF_ERROR_ERRNO | UUCONF_ERROR_FILENAME); } qglobal->ilineno = 0; iret = UUCONF_SUCCESS; zline = NULL; cline = 0; while (_uuconf_getline (qglobal, &zline, &cline, e) > 0) { char *zname; ++qglobal->ilineno; /* Skip leading whitespace to get to the system name. Then cut the system name off at the first whitespace, comment, or newline. */ zname = zline + strspn (zline, " \t"); zname[strcspn (zname, " \t#\n")] = '\0'; if (*zname == '\0') continue; iret = _uuconf_iadd_string (qglobal, zname, TRUE, TRUE, ppzsystems, (pointer) NULL); if (iret != UUCONF_SUCCESS) break; } (void) fclose (e); if (zline != NULL) free ((pointer) zline); if (iret != UUCONF_SUCCESS) { qglobal->zfilename = qglobal->qprocess->zv2systems; return iret | UUCONF_ERROR_FILENAME | UUCONF_ERROR_LINENO; } if (*ppzsystems == NULL) iret = _uuconf_iadd_string (qglobal, (char *) NULL, FALSE, FALSE, ppzsystems, (pointer) NULL); return iret; } uucp-1.07/uuconf/vsys.c0000664000076400007640000000316307665321762010600 /* vsys.c User function to get a system from the V2 configuration files. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucnfi.h" #if USE_RCS_ID const char _uuconf_vsys_rcsid[] = "$Id: vsys.c,v 1.6 2002/03/05 19:10:43 ian Rel $"; #endif /* Get system information from the V2 configuration files. This is a wrapper for the internal function which makes sure that every field gets a default value. */ int uuconf_v2_system_info (pglobal, zsystem, qsys) pointer pglobal; const char *zsystem; struct uuconf_system *qsys; { struct sglobal *qglobal = (struct sglobal *) pglobal; int iret; iret = _uuconf_iv2_system_internal (qglobal, zsystem, qsys); if (iret != UUCONF_SUCCESS) return iret; return _uuconf_isystem_basic_default (qglobal, qsys); } uucp-1.07/uuconf/alloc.h0000664000076400007640000000472107665321761010673 /* alloc.h Header file for uuconf memory allocation routines. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ /* This header file is private to the uuconf memory allocation routines, and should not be included by any other files. */ /* We want to be able to keep track of allocated memory blocks, so that we can free them up later. This will let us free up all the memory allocated to hold information for a system, for example. We do this by allocating large chunks and doling them out. Calling uuconf_malloc_block will return a pointer to a magic cookie which can then be passed to uuconf_malloc and uuconf_free. Passing the pointer to uuconf_free_block will free all memory allocated for that block. */ /* We allocate this much space in each block. On most systems, this will make the actual structure 1024 bytes, which may be convenient for some types of memory allocators. */ #define CALLOC_SIZE (1008) /* This is the actual structure of a block. */ struct sblock { /* Next block in linked list. */ struct sblock *qnext; /* Index of next free spot. */ size_t ifree; /* Last value returned by uuconf_malloc for this block. */ pointer plast; /* List of additional memory blocks. */ struct sadded *qadded; /* Buffer of data. We put it in a union with a double to make sure it is adequately aligned. */ union { char ab[CALLOC_SIZE]; double l; } u; }; /* There is a linked list of additional memory blocks inserted by uuconf_add_block. */ struct sadded { /* The next in the list. */ struct sadded *qnext; /* The added block. */ pointer padded; }; uucp-1.07/uuconf/syshdr.h0000664000076400007640000001030507665321762011111 /* syshdr.h -*- C -*- Unix system header for the uuconf library. Copyright (C) 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ /* The root directory (used when setting local-send and local-receive values). */ #define ZROOTDIR "/" /* The current directory (used by uuconv as a prefix for the newly created file names). */ #define ZCURDIR "." /* The names of the Taylor UUCP configuration files. These are appended to NEWCONFIGLIB which is defined in Makefile. */ #define CONFIGFILE "/config" #define SYSFILE "/sys" #define PORTFILE "/port" #define DIALFILE "/dial" #define CALLFILE "/call" #define PASSWDFILE "/passwd" #define DIALCODEFILE "/dialcode" /* The names of the various V2 configuration files. These are appended to OLDCONFIGLIB which is defined in Makefile. */ #define V2_SYSTEMS "/L.sys" #define V2_DEVICES "/L-devices" #define V2_USERFILE "/USERFILE" #define V2_CMDS "/L.cmds" #define V2_DIALCODES "/L-dialcodes" /* The names of the HDB configuration files. These are appended to OLDCONFIGLIB which is defined in Makefile. */ #define HDB_SYSFILES "/Sysfiles" #define HDB_SYSTEMS "/Systems" #define HDB_PERMISSIONS "/Permissions" #define HDB_DEVICES "/Devices" #define HDB_DIALERS "/Dialers" #define HDB_DIALCODES "/Dialcodes" #define HDB_MAXUUXQTS "/Maxuuxqts" #define HDB_REMOTE_UNKNOWN "/remote.unknown" /* A string which is inserted between the value of OLDCONFIGLIB (defined in the Makefile) and any names specified in the HDB Sysfiles file. */ #define HDB_SEPARATOR "/" /* A macro to check whether fopen failed because the file did not exist. */ #define FNO_SUCH_FILE() (errno == ENOENT) #if ! HAVE_STRERROR /* We need a definition for strerror; normally the function in the unix directory is used, but we want to be independent of that library. This macro evaluates its argument multiple times. */ extern int sys_nerr; extern char *sys_errlist[]; #define strerror(ierr) \ ((ierr) >= 0 && (ierr) < sys_nerr ? sys_errlist[ierr] : "unknown error") #endif /* ! HAVE_STRERROR */ /* This macro is used to make a filename found in a configuration file into an absolute path. The zdir argument is the directory to put it in. The zset argument is set to the new string. The fallocated argument is set to TRUE if the new string was allocated. */ #define MAKE_ABSOLUTE(zset, fallocated, zfile, zdir, pblock) \ do \ { \ if (*(zfile) == '/') \ { \ (zset) = (zfile); \ (fallocated) = FALSE; \ } \ else \ { \ size_t abs_cdir, abs_cfile; \ char *abs_zret; \ \ abs_cdir = strlen (zdir); \ abs_cfile = strlen (zfile); \ abs_zret = (char *) uuconf_malloc ((pblock), \ abs_cdir + abs_cfile + 2); \ (zset) = abs_zret; \ (fallocated) = TRUE; \ if (abs_zret != NULL) \ { \ memcpy ((pointer) abs_zret, (pointer) (zdir), abs_cdir); \ abs_zret[abs_cdir] = '/'; \ memcpy ((pointer) (abs_zret + abs_cdir + 1), \ (pointer) (zfile), abs_cfile + 1); \ } \ } \ } \ while (0) /* We want to be able to mark the Taylor UUCP system files as close on exec. */ #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif #define CLOSE_ON_EXEC(e) \ do \ { \ int cle_i = fileno (e); \ \ fcntl (cle_i, F_SETFD, fcntl (cle_i, F_GETFD, 0) | FD_CLOEXEC); \ } \ while (0) uucp-1.07/uuconf/uucnfi.h0000664000076400007640000003161407665321762011074 /* uucnfi.h Internal header file for the uuconf package. Copyright (C) 1992, 1993, 1994, 1995 Ian Lance Taylor This file is part of the Taylor UUCP uuconf library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ /* This is the internal header file for the uuconf package. It should not be included by anything other than the uuconf code itself. */ /* Get all the general definitions. */ #include "uucp.h" /* Get the uuconf header file itself. */ #include "uuconf.h" /* We need the system dependent header file. */ #include "syshdr.h" /* This is the generic information structure. This holds all the per-thread global information needed by the uuconf code. The per-process global information is held in an sprocess structure, which this structure points to. This permits the code to not have any global variables at all. */ struct sglobal { /* A pointer to the per-process global information. */ struct sprocess *qprocess; /* A memory block in which all the memory for these fields is allocated. */ pointer pblock; /* The value of errno after an error. */ int ierrno; /* The filename for which an error occurred. */ const char *zfilename; /* The line number at which an error occurred. */ int ilineno; }; /* This is the per-process information structure. This essentially holds all the global variables used by uuconf. */ struct sprocess { /* The name of the local machine. This will be NULL if it is not specified in a configuration file. */ const char *zlocalname; /* The spool directory. */ const char *zspooldir; /* The default public directory. */ const char *zpubdir; /* The lock directory. */ const char *zlockdir; /* The log file. */ const char *zlogfile; /* The statistics file. */ const char *zstatsfile; /* The debugging file. */ const char *zdebugfile; /* The default debugging level. */ const char *zdebug; /* Whether login information should be stripped. */ boolean fstrip_login; /* Whether protocol information should be stripped. */ boolean fstrip_proto; /* The maximum number of simultaneously executing uuxqts. */ int cmaxuuxqts; /* How often to spawn a uuxqt process. */ const char *zrunuuxqt; /* Whether we are reading the V2 configuration files. */ boolean fv2; /* Whether we are reading the HDB configuration files. */ boolean fhdb; /* The names of the dialcode files. */ char **pzdialcodefiles; /* Timetables. These are in pairs. The first element is the name, the second is the time string. */ char **pztimetables; /* Taylor UUCP config file name. */ char *zconfigfile; /* Taylor UUCP sys file names. */ char **pzsysfiles; /* Taylor UUCP port file names. */ char **pzportfiles; /* Taylor UUCP dial file names. */ char **pzdialfiles; /* Taylor UUCP passwd file names. */ char **pzpwdfiles; /* Taylor UUCP call file names. */ char **pzcallfiles; /* List of "unknown" commands from config file. */ struct sunknown *qunknown; /* Whether the Taylor UUCP system information locations have been read. */ boolean fread_syslocs; /* Taylor UUCP system information locations. */ struct stsysloc *qsyslocs; /* Taylor UUCP validation restrictions. */ struct svalidate *qvalidate; /* Whether the "myname" command is used in a Taylor UUCP file. */ boolean fuses_myname; /* V2 system file name (L.sys). */ char *zv2systems; /* V2 device file name (L-devices). */ char *zv2devices; /* V2 user permissions file name (USERFILE). */ char *zv2userfile; /* V2 user permitted commands file (L.cmds). */ char *zv2cmds; /* HDB system file names (Systems). */ char **pzhdb_systems; /* HDB device file names (Devices). */ char **pzhdb_devices; /* HDB dialer file names (Dialers). */ char **pzhdb_dialers; /* Whether the HDB Permissions file has been read. */ boolean fhdb_read_permissions; /* The HDB Permissions file entries. */ struct shpermissions *qhdb_permissions; }; /* This structure is used to hold the "unknown" commands from the Taylor UUCP config file before they have been parsed. */ struct sunknown { /* Next element in linked list. */ struct sunknown *qnext; /* Line number in config file. */ int ilineno; /* Number of arguments. */ int cargs; /* Arguments. */ char **pzargs; }; /* This structure is used to hold the locations of systems within the Taylor UUCP sys files. */ struct stsysloc { /* Next element in linked list. */ struct stsysloc *qnext; /* System name. */ const char *zname; /* Whether system is an alias or a real system. If this is an alias, the real system is the next entry in the linked list which is not an alias. */ boolean falias; /* File name (one of the sys files). */ const char *zfile; /* Open file. */ FILE *e; /* Location within file (from ftell). */ long iloc; /* Line number within file. */ int ilineno; }; /* This structure is used to hold validation restrictions. This is a list of machines which are permitted to use a particular login name. If a machine logs in, and there is no called login entry for it, the login name and machine name must be passed to uuconf_validate to confirm that either there is no entry for this login name or that the machine name appears on the entry. */ struct svalidate { /* Next element in linked list. */ struct svalidate *qnext; /* Login name. */ const char *zlogname; /* NULL terminated list of machine names. */ char **pzmachines; }; /* This structure is used to hold a linked list of HDB Permissions file entries. */ struct shpermissions { /* Next entry in linked list. */ struct shpermissions *qnext; /* NULL terminated array of LOGNAME values. */ char **pzlogname; /* NULL terminated array of MACHINE values. */ char **pzmachine; /* Boolean REQUEST value. */ int frequest; /* Boolean SENDFILES value ("call" is taken as "no"). */ int fsendfiles; /* NULL terminated array of READ values. */ char **pzread; /* NULL terminated array of WRITE values. */ char **pzwrite; /* Boolean CALLBACK value. */ int fcallback; /* NULL terminated array of COMMANDS values. */ char **pzcommands; /* NULL terminated array of VALIDATE values. */ char **pzvalidate; /* String MYNAME value. */ char *zmyname; /* String PUBDIR value. */ const char *zpubdir; /* NULL terminated array of ALIAS values. */ char **pzalias; }; /* This structure is used to build reentrant uuconf_cmdtab tables. The ioff field is either (size_t) -1 or an offsetof macro. The table is then copied into a uuconf_cmdtab, except that offsets of (size_t) -1 are converted to pvar elements of NULL, and other offsets are converted to an offset off some base address. */ struct cmdtab_offset { const char *zcmd; int itype; size_t ioff; uuconf_cmdtabfn pifn; }; /* A value in a uuconf_system structure which holds the address of this special variable is known to be uninitialized. */ extern char *_uuconf_unset; /* Internal function to read a system from the Taylor UUCP configuration files. This does not apply the basic defaults. */ extern int _uuconf_itaylor_system_internal P((struct sglobal *qglobal, const char *zsystem, struct uuconf_system *qsys)); /* Read the system locations and validation information from the Taylor UUCP configuration files. This sets the qsyslocs, qvalidate, and fread_syslocs elements of the global structure. */ extern int _uuconf_iread_locations P((struct sglobal *qglobal)); /* Process a command for a port from a Taylor UUCP file. */ extern int _uuconf_iport_cmd P((struct sglobal *qglobal, int argc, char **argv, struct uuconf_port *qport)); /* Process a command for a dialer from a Taylor UUCP file. */ extern int _uuconf_idialer_cmd P((struct sglobal *qglobal, int argc, char **argv, struct uuconf_dialer *qdialer)); /* Process a command for a chat script from a Taylor UUCP file; this is also called for HDB or V2 files, with a made up command. */ extern int _uuconf_ichat_cmd P((struct sglobal *qglobal, int argc, char **argv, struct uuconf_chat *qchat, pointer pblock)); /* Process a protocol-parameter command from a Taylor UUCP file. */ extern int _uuconf_iadd_proto_param P((struct sglobal *qglobal, int argc, char **argv, struct uuconf_proto_param **pq, pointer pblock)); /* Handle a "seven-bit", "reliable", or "half-duplex" command from a Taylor UUCP port or dialer file. The pvar field should point to the ireliable element of the structure. */ extern int _uuconf_iseven_bit P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); extern int _uuconf_ireliable P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); extern int _uuconf_ihalf_duplex P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); /* Internal function to read a system from the V2 configuration files. This does not apply the basic defaults. */ extern int _uuconf_iv2_system_internal P((struct sglobal *qglobal, const char *zsystem, struct uuconf_system *qsys)); /* Internal function to read a system from the HDB configuration files. This does not apply the basic defaults. */ extern int _uuconf_ihdb_system_internal P((struct sglobal *qglobal, const char *zsystem, struct uuconf_system *qsys)); /* Read the HDB Permissions file. */ extern int _uuconf_ihread_permissions P((struct sglobal *qglobal)); /* Initialize the global information structure. */ extern int _uuconf_iinit_global P((struct sglobal **pqglobal)); /* Clear system information. */ extern void _uuconf_uclear_system P((struct uuconf_system *qsys)); /* Default unset aspects of one system to the contents of another. */ extern int _uuconf_isystem_default P((struct sglobal *qglobal, struct uuconf_system *q, struct uuconf_system *qdefault, boolean faddalternates)); /* Put in the basic system defaults. */ extern int _uuconf_isystem_basic_default P((struct sglobal *qglobal, struct uuconf_system *qsys)); /* Clear port information. */ extern void _uuconf_uclear_port P((struct uuconf_port *qport)); /* Clear dialer information. */ extern void _uuconf_uclear_dialer P((struct uuconf_dialer *qdialer)); /* Add a timetable. */ extern int _uuconf_itimetable P((pointer pglobal, int argc, char **argv, pointer pvar, pointer pinfo)); /* Parse a time string. */ extern int _uuconf_itime_parse P((struct sglobal *qglobal, char *ztime, long ival, int cretry, int (*picmp) P((long, long)), struct uuconf_timespan **pqspan, pointer pblock)); /* A grade comparison function to pass to _uuconf_itime_parse. */ extern int _uuconf_itime_grade_cmp P((long, long)); /* Parse a debugging string. */ extern int _uuconf_idebug_cmd P((struct sglobal *qglobal, char **pzdebug, int argc, char **argv, pointer pblock)); /* Add a string to a NULL terminated list of strings. */ extern int _uuconf_iadd_string P((struct sglobal *qglobal, char *zadd, boolean fcopy, boolean fdupcheck, char ***ppzstrings, pointer pblock)); /* Parse a string into a boolean value. */ extern int _uuconf_iboolean P((struct sglobal *qglobal, const char *zval, int *pi)); /* Parse a string into an integer value. The argument p is either an int * or a long *, according to the argument fint. */ extern int _uuconf_iint P((struct sglobal *qglobal, const char *zval, pointer p, boolean fint)); /* Turn a cmdtab_offset table into a uuconf_cmdtab table. */ extern void _uuconf_ucmdtab_base P((const struct cmdtab_offset *qoff, size_t celes, char *pbase, struct uuconf_cmdtab *qset)); /* Merge two memory blocks into one. This cannot fail. */ extern pointer _uuconf_pmalloc_block_merge P((pointer, pointer)); /* A wrapper for getline that continues lines if they end in a backslash. It needs qglobal so that it can increment ilineno correctly. */ extern int _uuconf_getline P((struct sglobal *qglobal, char **, size_t *, FILE *)); /* Split a string into tokens. */ extern int _uuconf_istrsplit P((char *zline, int bsep, char ***ppzsplit, size_t *csplit)); uucp-1.07/unix/0000777000076400007640000000000007665533774007203 5uucp-1.07/unix/Makefile.am0000664000076400007640000000205407665321760011144 # This is the auto-Makefile for the unix subdirectory of Taylor UUCP. noinst_LIBRARIES = libunix.a libunix_a_SOURCES = access.c addbas.c app3.c app4.c basnam.c bytfre.c \ corrup.c chmod.c cohtty.c cusub.c cwd.c detach.c efopen.c epopen.c \ exists.c failed.c filnam.c fsusg.c indir.c init.c isdir.c \ isfork.c iswait.c jobid.c lcksys.c link.c locfil.c lock.c \ loctim.c mail.c mkdirs.c mode.c move.c opensr.c pause.c \ picksb.c pipe.c portnm.c priv.c proctm.c recep.c run.c seq.c \ serial.c signal.c sindir.c size.c sleep.c spawn.c splcmd.c \ splnam.c spool.c srmdir.c statsb.c status.c sync.c tcp.c \ time.c tli.c tmpfil.c trunc.c uacces.c ufopen.c uid.c ultspl.c \ umode.c unknwn.c uuto.c walk.c wldcrd.c work.c xqtfil.c xqtsub.c \ fsusg.h libunix_a_LIBADD = $(UNIXOBJS) EXTRA_libunix_a_SOURCES = getcwd.c mkdir.c rmdir.c dirent.c dup2.c ftw.c \ remove.c rename.c strerr.c if HAVE_MKDIR UUDIRFLAGS = else UUDIRFLAGS = -DUUDIR_PROGRAM=\"$(sbindir)/util/uudir\" endif AM_CFLAGS = -I.. -I$(srcdir)/.. $(WARN_CFLAGS) $(UUDIRFLAGS) -DSBINDIR=\"$(sbindir)\" uucp-1.07/unix/Makefile.in0000664000076400007640000004251307665532206011160 # Makefile.in generated automatically by automake 1.5 from Makefile.am. # Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 # 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@ # This is the auto-Makefile for the unix subdirectory of Taylor UUCP. SHELL = @SHELL@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ sharedstatedir = @sharedstatedir@ localstatedir = @localstatedir@ libdir = @libdir@ infodir = @infodir@ mandir = @mandir@ includedir = @includedir@ oldincludedir = /usr/include pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = .. ACLOCAL = @ACLOCAL@ AUTOCONF = @AUTOCONF@ AUTOMAKE = @AUTOMAKE@ AUTOHEADER = @AUTOHEADER@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_HEADER = $(INSTALL_DATA) transform = @program_transform_name@ NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : AMTAR = @AMTAR@ AR = @AR@ AWK = @AWK@ CC = @CC@ DEPDIR = @DEPDIR@ EXEEXT = @EXEEXT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LIBOBJS = @LIBOBJS@ LN_S = @LN_S@ MAINT = @MAINT@ NEWCONFIGDIR = @NEWCONFIGDIR@ OBJEXT = @OBJEXT@ OLDCONFIGDIR = @OLDCONFIGDIR@ OWNER = @OWNER@ PACKAGE = @PACKAGE@ POUNDBANG = @POUNDBANG@ RANLIB = @RANLIB@ UNIXOBJS = @UNIXOBJS@ VERSION = @VERSION@ WARN_CFLAGS = @WARN_CFLAGS@ am__include = @am__include@ am__quote = @am__quote@ install_sh = @install_sh@ noinst_LIBRARIES = libunix.a libunix_a_SOURCES = access.c addbas.c app3.c app4.c basnam.c bytfre.c \ corrup.c chmod.c cohtty.c cusub.c cwd.c detach.c efopen.c epopen.c \ exists.c failed.c filnam.c fsusg.c indir.c init.c isdir.c \ isfork.c iswait.c jobid.c lcksys.c link.c locfil.c lock.c \ loctim.c mail.c mkdirs.c mode.c move.c opensr.c pause.c \ picksb.c pipe.c portnm.c priv.c proctm.c recep.c run.c seq.c \ serial.c signal.c sindir.c size.c sleep.c spawn.c splcmd.c \ splnam.c spool.c srmdir.c statsb.c status.c sync.c tcp.c \ time.c tli.c tmpfil.c trunc.c uacces.c ufopen.c uid.c ultspl.c \ umode.c unknwn.c uuto.c walk.c wldcrd.c work.c xqtfil.c xqtsub.c \ fsusg.h libunix_a_LIBADD = $(UNIXOBJS) EXTRA_libunix_a_SOURCES = getcwd.c mkdir.c rmdir.c dirent.c dup2.c ftw.c \ remove.c rename.c strerr.c @HAVE_MKDIR_TRUE@UUDIRFLAGS = @HAVE_MKDIR_FALSE@UUDIRFLAGS = -DUUDIR_PROGRAM=\"$(sbindir)/util/uudir\" AM_CFLAGS = -I.. -I$(srcdir)/.. $(WARN_CFLAGS) $(UUDIRFLAGS) -DSBINDIR=\"$(sbindir)\" subdir = unix mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = LIBRARIES = $(noinst_LIBRARIES) libunix_a_AR = $(AR) cru libunix_a_DEPENDENCIES = am_libunix_a_OBJECTS = access.$(OBJEXT) addbas.$(OBJEXT) app3.$(OBJEXT) \ app4.$(OBJEXT) basnam.$(OBJEXT) bytfre.$(OBJEXT) \ corrup.$(OBJEXT) chmod.$(OBJEXT) cohtty.$(OBJEXT) \ cusub.$(OBJEXT) cwd.$(OBJEXT) detach.$(OBJEXT) efopen.$(OBJEXT) \ epopen.$(OBJEXT) exists.$(OBJEXT) failed.$(OBJEXT) \ filnam.$(OBJEXT) fsusg.$(OBJEXT) indir.$(OBJEXT) init.$(OBJEXT) \ isdir.$(OBJEXT) isfork.$(OBJEXT) iswait.$(OBJEXT) \ jobid.$(OBJEXT) lcksys.$(OBJEXT) link.$(OBJEXT) \ locfil.$(OBJEXT) lock.$(OBJEXT) loctim.$(OBJEXT) mail.$(OBJEXT) \ mkdirs.$(OBJEXT) mode.$(OBJEXT) move.$(OBJEXT) opensr.$(OBJEXT) \ pause.$(OBJEXT) picksb.$(OBJEXT) pipe.$(OBJEXT) \ portnm.$(OBJEXT) priv.$(OBJEXT) proctm.$(OBJEXT) \ recep.$(OBJEXT) run.$(OBJEXT) seq.$(OBJEXT) serial.$(OBJEXT) \ signal.$(OBJEXT) sindir.$(OBJEXT) size.$(OBJEXT) \ sleep.$(OBJEXT) spawn.$(OBJEXT) splcmd.$(OBJEXT) \ splnam.$(OBJEXT) spool.$(OBJEXT) srmdir.$(OBJEXT) \ statsb.$(OBJEXT) status.$(OBJEXT) sync.$(OBJEXT) tcp.$(OBJEXT) \ time.$(OBJEXT) tli.$(OBJEXT) tmpfil.$(OBJEXT) trunc.$(OBJEXT) \ uacces.$(OBJEXT) ufopen.$(OBJEXT) uid.$(OBJEXT) \ ultspl.$(OBJEXT) umode.$(OBJEXT) unknwn.$(OBJEXT) \ uuto.$(OBJEXT) walk.$(OBJEXT) wldcrd.$(OBJEXT) work.$(OBJEXT) \ xqtfil.$(OBJEXT) xqtsub.$(OBJEXT) libunix_a_OBJECTS = $(am_libunix_a_OBJECTS) DEFS = @DEFS@ DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ depcomp = $(SHELL) $(top_srcdir)/depcomp @AMDEP_TRUE@DEP_FILES = $(DEPDIR)/access.Po $(DEPDIR)/addbas.Po \ @AMDEP_TRUE@ $(DEPDIR)/app3.Po $(DEPDIR)/app4.Po \ @AMDEP_TRUE@ $(DEPDIR)/basnam.Po $(DEPDIR)/bytfre.Po \ @AMDEP_TRUE@ $(DEPDIR)/chmod.Po $(DEPDIR)/cohtty.Po \ @AMDEP_TRUE@ $(DEPDIR)/corrup.Po $(DEPDIR)/cusub.Po \ @AMDEP_TRUE@ $(DEPDIR)/cwd.Po $(DEPDIR)/detach.Po \ @AMDEP_TRUE@ $(DEPDIR)/dirent.Po $(DEPDIR)/dup2.Po \ @AMDEP_TRUE@ $(DEPDIR)/efopen.Po $(DEPDIR)/epopen.Po \ @AMDEP_TRUE@ $(DEPDIR)/exists.Po $(DEPDIR)/failed.Po \ @AMDEP_TRUE@ $(DEPDIR)/filnam.Po $(DEPDIR)/fsusg.Po \ @AMDEP_TRUE@ $(DEPDIR)/ftw.Po $(DEPDIR)/getcwd.Po \ @AMDEP_TRUE@ $(DEPDIR)/indir.Po $(DEPDIR)/init.Po \ @AMDEP_TRUE@ $(DEPDIR)/isdir.Po $(DEPDIR)/isfork.Po \ @AMDEP_TRUE@ $(DEPDIR)/iswait.Po $(DEPDIR)/jobid.Po \ @AMDEP_TRUE@ $(DEPDIR)/lcksys.Po $(DEPDIR)/link.Po \ @AMDEP_TRUE@ $(DEPDIR)/locfil.Po $(DEPDIR)/lock.Po \ @AMDEP_TRUE@ $(DEPDIR)/loctim.Po $(DEPDIR)/mail.Po \ @AMDEP_TRUE@ $(DEPDIR)/mkdir.Po $(DEPDIR)/mkdirs.Po \ @AMDEP_TRUE@ $(DEPDIR)/mode.Po $(DEPDIR)/move.Po \ @AMDEP_TRUE@ $(DEPDIR)/opensr.Po $(DEPDIR)/pause.Po \ @AMDEP_TRUE@ $(DEPDIR)/picksb.Po $(DEPDIR)/pipe.Po \ @AMDEP_TRUE@ $(DEPDIR)/portnm.Po $(DEPDIR)/priv.Po \ @AMDEP_TRUE@ $(DEPDIR)/proctm.Po $(DEPDIR)/recep.Po \ @AMDEP_TRUE@ $(DEPDIR)/remove.Po $(DEPDIR)/rename.Po \ @AMDEP_TRUE@ $(DEPDIR)/rmdir.Po $(DEPDIR)/run.Po \ @AMDEP_TRUE@ $(DEPDIR)/seq.Po $(DEPDIR)/serial.Po \ @AMDEP_TRUE@ $(DEPDIR)/signal.Po $(DEPDIR)/sindir.Po \ @AMDEP_TRUE@ $(DEPDIR)/size.Po $(DEPDIR)/sleep.Po \ @AMDEP_TRUE@ $(DEPDIR)/spawn.Po $(DEPDIR)/splcmd.Po \ @AMDEP_TRUE@ $(DEPDIR)/splnam.Po $(DEPDIR)/spool.Po \ @AMDEP_TRUE@ $(DEPDIR)/srmdir.Po $(DEPDIR)/statsb.Po \ @AMDEP_TRUE@ $(DEPDIR)/status.Po $(DEPDIR)/strerr.Po \ @AMDEP_TRUE@ $(DEPDIR)/sync.Po $(DEPDIR)/tcp.Po \ @AMDEP_TRUE@ $(DEPDIR)/time.Po $(DEPDIR)/tli.Po \ @AMDEP_TRUE@ $(DEPDIR)/tmpfil.Po $(DEPDIR)/trunc.Po \ @AMDEP_TRUE@ $(DEPDIR)/uacces.Po $(DEPDIR)/ufopen.Po \ @AMDEP_TRUE@ $(DEPDIR)/uid.Po $(DEPDIR)/ultspl.Po \ @AMDEP_TRUE@ $(DEPDIR)/umode.Po $(DEPDIR)/unknwn.Po \ @AMDEP_TRUE@ $(DEPDIR)/uuto.Po $(DEPDIR)/walk.Po \ @AMDEP_TRUE@ $(DEPDIR)/wldcrd.Po $(DEPDIR)/work.Po \ @AMDEP_TRUE@ $(DEPDIR)/xqtfil.Po $(DEPDIR)/xqtsub.Po COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ CFLAGS = @CFLAGS@ DIST_SOURCES = $(libunix_a_SOURCES) $(EXTRA_libunix_a_SOURCES) DIST_COMMON = Makefile.am Makefile.in SOURCES = $(libunix_a_SOURCES) $(EXTRA_libunix_a_SOURCES) all: all-am .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) cd $(top_srcdir) && \ $(AUTOMAKE) --gnu unix/Makefile Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status cd $(top_builddir) && \ CONFIG_HEADERS= CONFIG_LINKS= \ CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status AR = ar clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libunix.a: $(libunix_a_OBJECTS) $(libunix_a_DEPENDENCIES) -rm -f libunix.a $(libunix_a_AR) libunix.a $(libunix_a_OBJECTS) $(libunix_a_LIBADD) $(RANLIB) libunix.a mostlyclean-compile: -rm -f *.$(OBJEXT) core *.core distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/access.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/addbas.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/app3.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/app4.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/basnam.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/bytfre.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/chmod.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/cohtty.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/corrup.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/cusub.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/cwd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/detach.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/dirent.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/dup2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/efopen.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/epopen.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/exists.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/failed.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/filnam.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/fsusg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ftw.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getcwd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/indir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/init.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/isdir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/isfork.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/iswait.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/jobid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/lcksys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/link.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/locfil.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/lock.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/loctim.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/mail.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/mkdir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/mkdirs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/mode.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/move.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/opensr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/pause.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/picksb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/pipe.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/portnm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/priv.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/proctm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/recep.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/remove.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/rename.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/rmdir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/run.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/seq.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/serial.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/signal.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/sindir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/size.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/sleep.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/spawn.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/splcmd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/splnam.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/spool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/srmdir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/statsb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/status.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strerr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/sync.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tcp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/time.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tli.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/tmpfil.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/trunc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uacces.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ufopen.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ultspl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/umode.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/unknwn.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/uuto.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/walk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/wldcrd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/work.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/xqtfil.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/xqtsub.Po@am__quote@ distclean-depend: -rm -rf $(DEPDIR) .c.o: @AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< .c.obj: @AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(COMPILE) -c `cygpath -w $<` CCDEPMODE = @CCDEPMODE@ uninstall-info-am: tags: TAGS ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(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 $(LISP) TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(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 "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) GTAGS: here=`CDPATH=: && cd $(top_builddir) && pwd` \ && cd $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) $$here distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) top_distdir = .. distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) distdir: $(DISTFILES) @for file in $(DISTFILES); do \ if test -f $$file; then d=.; else d=$(srcdir); fi; \ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ if test "$$dir" != "$$file" && test "$$dir" != "."; then \ $(mkinstalldirs) "$(distdir)/$$dir"; \ fi; \ if test -d $$d/$$file; then \ cp -pR $$d/$$file $(distdir) \ || 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 $(LIBRARIES) installdirs: 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)" \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* 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-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am distclean-am: clean-am distclean-compile distclean-depend \ distclean-generic distclean-tags dvi: dvi-am dvi-am: info: info-am info-am: install-data-am: install-exec-am: install-info: install-info-am install-man: installcheck-am: maintainer-clean: maintainer-clean-am maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic uninstall-am: uninstall-info-am .PHONY: GTAGS all all-am check check-am clean clean-generic \ clean-noinstLIBRARIES distclean distclean-compile \ distclean-depend distclean-generic distclean-tags distdir dvi \ dvi-am info info-am install install-am install-data \ install-data-am install-exec install-exec-am install-info \ install-info-am install-man install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic tags uninstall uninstall-am \ uninstall-info-am # 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: uucp-1.07/unix/access.c0000664000076400007640000000526507665321760010524 /* access.c Check access to files by the user and by the daemon. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include /* See if the user has access to a file, to prevent the setuid uucp and uux programs handing out unauthorized access. */ boolean fsysdep_access (zfile) const char *zfile; { if (access (zfile, R_OK) == 0) return TRUE; ulog (LOG_ERROR, "%s: %s", zfile, strerror (errno)); return FALSE; } /* See if the daemon has access to a file. This is called if a file is not being transferred to the spool directory, since if the daemon does not have access the later transfer will fail. We assume that the daemon will have the same euid (or egid) as the one we are running under. If our uid (gid) and euid (egid) are the same, we assume that we have access. Note that is not important for security, since the check will be (implicitly) done again when the daemon tries to transfer the file. This routine should work whether the UUCP programs are installed setuid or setgid. */ boolean fsysdep_daemon_access (zfile) const char *zfile; { struct stat s; uid_t ieuid, iuid, iegid, igid; boolean fok; ieuid = geteuid (); if (ieuid == 0) return TRUE; iuid = getuid (); iegid = getegid (); igid = getgid (); /* If our effective uid and gid are the same as our real uid and gid, we assume the daemon will have access to the file. */ if (ieuid == iuid && iegid == igid) return TRUE; if (stat ((char *) zfile, &s) != 0) { /* If we get an EACCES error, we can't read the file using our euid. Therefore, the daemon will not have access to the file. */ if (errno == EACCES) ulog (LOG_ERROR, "%s: cannot be read by daemon", zfile); else ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); return FALSE; } /* If our euid is not our uid, but it is the file's uid, see if the owner has read access. Otherwise, if our egid is not our gid, but it is the file's gid, see if the group has read access. Otherwise, see if the world has read access. We know from the above check that at least one of our euid and egid are different, so that is the only one we want to check. This check could fail if the UUCP programs were both setuid and setgid, but why would they be? */ if (ieuid != iuid && ieuid == s.st_uid) fok = (s.st_mode & S_IRUSR) != 0; else if (iegid != igid && iegid == s.st_gid) fok = (s.st_mode & S_IRGRP) != 0; else fok = (s.st_mode & S_IROTH) != 0; if (! fok) { ulog (LOG_ERROR, "%s: cannot be read by daemon", zfile); return FALSE; } return TRUE; } uucp-1.07/unix/addbas.c0000664000076400007640000000161207665321760010471 /* addbas.c If we have a directory, add in a base name. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" /* If we have a directory, add a base name. */ char * zsysdep_add_base (zfile, zname) const char *zfile; const char *zname; { size_t clen; const char *zlook; char *zfree; char *zret; #if DEBUG > 0 if (*zfile != '/') ulog (LOG_FATAL, "zsysdep_add_base: %s: Can't happen", zfile); #endif clen = strlen (zfile); if (zfile[clen - 1] != '/') { if (! fsysdep_directory (zfile)) return zbufcpy (zfile); zfree = NULL; } else { /* Trim out the trailing '/'. */ zfree = zbufcpy (zfile); zfree[clen - 1] = '\0'; zfile = zfree; } zlook = strrchr (zname, '/'); if (zlook != NULL) zname = zlook + 1; zret = zsysdep_in_dir (zfile, zname); ubuffree (zfree); return zret; } uucp-1.07/unix/app3.c0000664000076400007640000000130207665321760010112 /* app3.c Stick two directories and a file name together. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" char * zsappend3 (zdir1, zdir2, zfile) const char *zdir1; const char *zdir2; const char *zfile; { size_t cdir1, cdir2, cfile; char *zret; cdir1 = strlen (zdir1); cdir2 = strlen (zdir2); cfile = strlen (zfile); zret = zbufalc (cdir1 + cdir2 + cfile + 3); if (cdir1 == 1 && *zdir1 == '/') cdir1 = 0; else memcpy (zret, zdir1, cdir1); memcpy (zret + cdir1 + 1, zdir2, cdir2); memcpy (zret + cdir1 + cdir2 + 2, zfile, cfile); zret[cdir1] = '/'; zret[cdir1 + cdir2 + 1] = '/'; zret[cdir1 + cdir2 + cfile + 2] = '\0'; return zret; } uucp-1.07/unix/app4.c0000664000076400007640000000156707665321760010130 /* app4.c Stick three directories and a file name together. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" char * zsappend4 (zdir1, zdir2, zdir3, zfile) const char *zdir1; const char *zdir2; const char *zdir3; const char *zfile; { size_t cdir1, cdir2, cdir3, cfile; char *zret; cdir1 = strlen (zdir1); cdir2 = strlen (zdir2); cdir3 = strlen (zdir3); cfile = strlen (zfile); zret = zbufalc (cdir1 + cdir2 + cdir3 + cfile + 4); if (cdir1 == 1 && *zdir1 == '/') cdir1 = 0; else memcpy (zret, zdir1, cdir1); memcpy (zret + cdir1 + 1, zdir2, cdir2); memcpy (zret + cdir1 + cdir2 + 2, zdir3, cdir3); memcpy (zret + cdir1 + cdir2 + cdir3 + 3, zfile, cfile); zret[cdir1] = '/'; zret[cdir1 + cdir2 + 1] = '/'; zret[cdir1 + cdir2 + cdir3 + 2] = '/'; zret[cdir1 + cdir2 + cdir3 + cfile + 3] = '\0'; return zret; } uucp-1.07/unix/basnam.c0000664000076400007640000000053507665321760010517 /* basnam.c Get the base name of a file. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" /* Get the base name of a file name. */ char * zsysdep_base_name (zfile) const char *zfile; { const char *z; z = strrchr (zfile, '/'); if (z != NULL) return zbufcpy (z + 1); return zbufcpy (zfile); } uucp-1.07/unix/bytfre.c0000664000076400007640000000074507665321760010554 /* bytfre.c Get the number of bytes free on a file system. */ #include "uucp.h" #include "system.h" #include "sysdep.h" #include "fsusg.h" #if HAVE_LIMITS_H #include #else #define LONG_MAX 2147483647 #endif long csysdep_bytes_free (zfile) const char *zfile; { struct fs_usage s; if (get_fs_usage ((char *) zfile, (char *) NULL, &s) < 0) return -1; if (s.fsu_bavail >= LONG_MAX / (long) 512) return LONG_MAX; return s.fsu_bavail * (long) 512; } uucp-1.07/unix/corrup.c0000664000076400007640000000104507665321760010565 /* corrup.c Save a file in the .Corrupt directory. */ #include "uucp.h" #include "sysdep.h" #include "uudefs.h" #include "system.h" char * zsysdep_save_corrupt_file (zfile) const char *zfile; { const char *zslash; char *zto; zslash = strrchr (zfile, '/'); if (zslash == NULL) zslash = zfile; else ++zslash; zto = zsappend3 (zSspooldir, CORRUPTDIR, zslash); if (! fsysdep_move_file (zfile, zto, TRUE, FALSE, FALSE, (const char *) NULL)) { ubuffree (zto); return NULL; } return zto; } uucp-1.07/unix/chmod.c0000664000076400007640000000066007665321760010347 /* chmod.c Change the mode of a file. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include /* Change the mode of a file. */ boolean fsysdep_change_mode (zfile, imode) const char *zfile; unsigned int imode; { if (chmod ((char *) zfile, imode) < 0) { ulog (LOG_ERROR, "chmod (%s): %s", zfile, strerror (errno)); return FALSE; } return TRUE; } uucp-1.07/unix/cohtty.c0000664000076400007640000001622507665321760010573 /* Coherent tty locking support. This file was contributed by Bob Hemedinger of Mark Williams Corporation and lightly edited by Ian Lance Taylor. */ /* The bottom part of this file is lock.c. * This is a hacked lock.c. A full lock.c can be found in the libmisc sources * under /usr/src/misc.tar.Z. * * These are for checking for the existence of locks: * lockexist(resource) * lockttyexist(ttyname) */ #include "uucp.h" #if HAVE_COHERENT_LOCKFILES /* cohtty.c: Given a serial device name, read /etc/ttys and determine if * the device is already enabled. If it is, disable the * device and return a string so that it can be re-enabled * at the completion of the uucico session as part of the * function that resets the serial device before uucico * terminates. * */ #include "uudefs.h" #include "sysdep.h" #include #include /* fscoherent_disable_tty() is a COHERENT specific function. It takes the name * of a serial device and then scans /etc/ttys for a match. If it finds one, * it checks the first field of the entry. If it is a '1', then it will disable * the port and set a flag. The flag will be checked later when uucico wants to * reset the serial device to see if the device needs to be re-enabled. */ /* May 10, 1993: This function will always return true for the following * reasons: * 1) lock files have already been dealt with * 2) if someone else already has the port open, uucico should fail anyways * 3) Coherent's disable command return can return '0' or '1', but will * succeed in any event. * 4) It doesn't matter if there is a ttys entry for the port in question. * /etc/ttys generally only lists devices that MAY be enabled for logins. * If a device will never be used for logins, then there may not be a * ttys entry, in which case, disable won't be called anyways. * ---bob@mwc.com */ boolean fscoherent_disable_tty (zdevice, pzenable) const char *zdevice; char **pzenable; { struct ttyentry{ /* this is an /etc/ttys entry */ char enable_disable[1]; char remote_local[1]; char baud_rate[1]; char tty_device[16]; }; struct ttyentry sought_tty; int x,y,z; /* dummy */ FILE * infp; /* this will point to /etc/ttys */ char disable_command[66]; /* this will be the disable command * passed to the system. */ char enable_device[16]; /* this will hold our device name * to enable. */ *pzenable = NULL; strcpy(enable_device,""); /* initialize our strings */ strcpy(sought_tty.tty_device,""); if( (infp = fopen("/etc/ttys","r")) == NULL){ ulog(LOG_ERROR,"Error: check_disable_tty: failed to open /etc/ttys\n"); return FALSE; } while (NULL !=(fgets(&sought_tty, sizeof (sought_tty), infp ))){ sought_tty.tty_device[strlen(sought_tty.tty_device) -1] = '\0'; strcpy(enable_device,sought_tty.tty_device); /* we must strip away the suffix to the com port name or * we will never find a match. For example, if we are passed * /dev/com4l to call out with and the port is already enabled, * 9/10 the port enabled will be com4r. After we strip away the * suffix of the port found in /etc/ttys, then we can test * if the base port name appears in the device name string * passed to us. */ for(z = strlen(sought_tty.tty_device) ; z > 0 ; z--){ if(isdigit(sought_tty.tty_device[z])){ break; } } y = strlen(sought_tty.tty_device); for(x = z+1 ; x <= y; x++){ sought_tty.tty_device[x] = '\0'; } /* ulog(LOG_NORMAL,"found device {%s}\n",sought_tty.tty_device); */ if(strstr(zdevice, sought_tty.tty_device)){ if(sought_tty.enable_disable[0] == '1'){ ulog(LOG_NORMAL, "coh_tty: Disabling device %s {%s}\n", zdevice, sought_tty.tty_device); sprintf(disable_command, "/etc/disable %s",enable_device); { pid_t ipid; const char *azargs[3]; int aidescs[3]; azargs[0] = "/etc/disable"; azargs[1] = enable_device; azargs[2] = NULL; aidescs[0] = SPAWN_NULL; aidescs[1] = SPAWN_NULL; aidescs[2] = SPAWN_NULL; ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, (const char *) NULL, TRUE, TRUE, (const char *) NULL, (const char *) NULL, (const char *) NULL); if (ipid < 0) x = 1; else x = ixswait ((unsigned long) ipid, (const char *) NULL); } *pzenable = zbufalc (sizeof "/dev/" + strlen (enable_device)); sprintf(*pzenable,"/dev/%s", enable_device); /* ulog(LOG_NORMAL,"Enable string is {%s}",*pzenable); */ return TRUE; }else{ /* device not enabled */ return TRUE; } } } return TRUE; /* no ttys entry found */ } /* The following is COHERENT 4.0 specific. It is used to test for any * existing lockfiles on a port which would have been created by init * when a user logs into a port. */ #define LOCKSIG 9 /* Significant Chars of Lockable Resources. */ #define LOKFLEN 64 /* Max Length of UUCP Lock File Name. */ #define LOCKPRE "LCK.." #define PIDLEN 6 /* Maximum length of string representing a pid. */ #ifndef LOCKDIR #define LOCKDIR SPOOLDIR #endif /* There is a special version of DEVMASK for the PE multiport driver * because of the peculiar way it uses the minor device number. For * all other drivers, the lower 5 bits describe the physical port-- * the upper 3 bits give attributes for the port. */ #define PE_DRIVER 21 /* Major device number for the PE driver. */ #define PE_DEVMASK 0x3f /* PE driver minor device mask. */ #define DEVMASK 0x1f /* Minor device mask. */ /* * Generates a resource name for locking, based on the major number * and the lower 4 bits of the minor number of the tty device. * * Builds the name in buff as two "." separated decimal numbers. * Returns NULL on failure, buff on success. */ static char * gen_res_name(path, buff) char *path; char *buff; { struct stat sbuf; int status; if (0 != (status = stat(path, &sbuf))) { /* Can't stat the file. */ return (NULL); } if (PE_DRIVER == major(sbuf.st_rdev)) { sprintf(buff, "%d.%d", major(sbuf.st_rdev), PE_DEVMASK & minor(sbuf.st_rdev)); } else { sprintf(buff, "%d.%d", major(sbuf.st_rdev), DEVMASK & minor(sbuf.st_rdev)); } return(buff); } /* gen_res_name */ /* * lockexist(resource) char *resource; * * Test for existance of a lock on the given resource. * * Returns: (1) Resource is locked. * (0) Resource is not locked. */ static boolean lockexist(resource) const char *resource; { char lockfn[LOKFLEN]; if ( resource == NULL ) return(0); sprintf(lockfn, "%s/%s%.*s", LOCKDIR, LOCKPRE, LOCKSIG, resource); return (!access(lockfn, AEXISTS)); } /* lockexist() */ /* * lockttyexist(ttyname) char *ttyname; * * Test for existance of a lock on the given tty. * * Returns: (1) Resource is locked. * (0) Resource is not locked. */ boolean lockttyexist(ttyn) const char *ttyn; { char resource[LOKFLEN]; char filename[LOKFLEN]; sprintf(filename, "/dev/%s", ttyn); if (NULL == gen_res_name(filename, resource)){ return(0); /* Non-existent tty can not be locked :-) */ } return(lockexist(resource)); } /* lockttyexist() */ #endif /* HAVE_COHERENT_LOCKFILES */ uucp-1.07/unix/cusub.c0000664000076400007640000006511507665321760010404 /* cusub.c System dependent routines for cu. Copyright (C) 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char cusub_rcsid[] = "$Id: cusub.c,v 1.27 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "uuconf.h" #include "sysdep.h" #include "system.h" #include "cu.h" #include "conn.h" #include "prot.h" #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif /* Get definitions for both O_NONBLOCK and O_NDELAY. */ #ifndef O_NDELAY #ifdef FNDELAY #define O_NDELAY FNDELAY #else /* ! defined (FNDELAY) */ #define O_NDELAY 0 #endif /* ! defined (FNDELAY) */ #endif /* ! defined (O_NDELAY) */ #ifndef O_NONBLOCK #ifdef FNBLOCK #define O_NONBLOCK FNBLOCK #else /* ! defined (FNBLOCK) */ #define O_NONBLOCK 0 #endif /* ! defined (FNBLOCK) */ #endif /* ! defined (O_NONBLOCK) */ #include /* 4.2 systems don't define SIGUSR2. This should work for them. On systems which are missing SIGUSR1, or SIGURG, you must find two signals which you can safely use. */ #ifndef SIGUSR2 #define SIGUSR2 SIGURG #endif /* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */ #ifndef EAGAIN #ifndef EWOULDBLOCK #define EAGAIN (-1) #define EWOULDBLOCK (-1) #else /* defined (EWOULDBLOCK) */ #define EAGAIN EWOULDBLOCK #endif /* defined (EWOULDBLOCK) */ #else /* defined (EAGAIN) */ #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif /* ! defined (EWOULDBLOCK) */ #endif /* defined (EAGAIN) */ #ifndef ENODATA #define ENODATA EAGAIN #endif /* Local variables. */ /* The EOF character, as set by fsysdep_terminal_raw. */ static char bSeof; /* The SUSP character, as set by fsysdep_terminal_raw. */ static char bStstp; /* Local functions. */ static const char *zsport_line P((const struct uuconf_port *qport)); static void uscu_child P((struct sconnection *qconn, int opipe)); static RETSIGTYPE uscu_child_handler P((int isig)); static RETSIGTYPE uscu_alarm P((int isig)); static int cscu_escape P((char *pbcmd, const char *zlocalname)); static RETSIGTYPE uscu_alarm_kill P((int isig)); /* Return the device name for a port, or NULL if none. */ static const char * zsport_line (qport) const struct uuconf_port *qport; { const char *zline; if (qport == NULL) return NULL; switch (qport->uuconf_ttype) { default: case UUCONF_PORTTYPE_STDIN: return NULL; case UUCONF_PORTTYPE_MODEM: zline = qport->uuconf_u.uuconf_smodem.uuconf_zdevice; break; case UUCONF_PORTTYPE_DIRECT: zline = qport->uuconf_u.uuconf_sdirect.uuconf_zdevice; break; case UUCONF_PORTTYPE_TCP: case UUCONF_PORTTYPE_TLI: case UUCONF_PORTTYPE_PIPE: return NULL; } if (zline == NULL) zline = qport->uuconf_zname; return zline; } /* Check whether the user has legitimate access to a port. */ boolean fsysdep_port_access (qport) struct uuconf_port *qport; { const char *zline; char *zfree; boolean fret; zline = zsport_line (qport); if (zline == NULL) return TRUE; zfree = NULL; if (*zline != '/') { zfree = zbufalc (sizeof "/dev/" + strlen (zline)); sprintf (zfree, "/dev/%s", zline); zline = zfree; } fret = access (zline, R_OK | W_OK) == 0; ubuffree (zfree); return fret; } /* Return whether the given port is named by the given line. */ boolean fsysdep_port_is_line (qport, zline) struct uuconf_port *qport; const char *zline; { const char *zpline; char *zfree1, *zfree2; boolean fret; zpline = zsport_line (qport); if (zpline == NULL) return FALSE; if (strcmp (zline, zpline) == 0) return TRUE; zfree1 = NULL; zfree2 = NULL; if (*zline != '/') { zfree1 = zbufalc (sizeof "/dev/" + strlen (zline)); sprintf (zfree1, "/dev/%s", zline); zline = zfree1; } if (*zpline != '/') { zfree2 = zbufalc (sizeof "/dev/" + strlen (zpline)); sprintf (zfree2, "/dev/%s", zpline); zpline = zfree2; } fret = strcmp (zline, zpline) == 0; ubuffree (zfree1); ubuffree (zfree2); return fret; } /* The cu program wants the system dependent layer to handle the details of copying data from the communications port to the terminal. This copying need only be done while executing fsysdep_cu. On Unix, however, we set up a subprocess to do it all the time. This subprocess must be controllable via the fsysdep_cu_copy function. We keep a pipe open to the subprocess. When we want it to stop we send it a signal, and then wait for it to write a byte to us over the pipe. */ /* The subprocess pid. */ static volatile pid_t iSchild; /* The pipe from the subprocess. */ static int oSpipe; /* When we tell the child to stop, it sends this. */ #define CHILD_STOPPED ('S') /* When we tell the child to start, it sends this. */ #define CHILD_STARTED ('G') /* Initialize the subprocess, and have it start copying data. */ boolean fsysdep_cu_init (qconn) struct sconnection *qconn; { int ai[2]; /* Write out anything we may have buffered up during the chat script. We do this before forking the child only to make it easy to move the child into a separate executable. */ while (iPrecend != iPrecstart) { char *z; int c; z = abPrecbuf + iPrecstart; if (iPrecend > iPrecstart) c = iPrecend - iPrecstart; else c = CRECBUFLEN - iPrecstart; iPrecstart = (iPrecstart + c) % CRECBUFLEN; while (c > 0) { int cwrote; cwrote = write (1, z, c); if (cwrote <= 0) { if (cwrote < 0) ulog (LOG_ERROR, "write: %s", strerror (errno)); else ulog (LOG_ERROR, "Line disconnected"); return FALSE; } c -= cwrote; z += cwrote; } } if (pipe (ai) < 0) { ulog (LOG_ERROR, "pipe: %s", strerror (errno)); return FALSE; } iSchild = ixsfork (); if (iSchild < 0) { ulog (LOG_ERROR, "fork: %s", strerror (errno)); return FALSE; } if (iSchild == 0) { (void) close (ai[0]); uscu_child (qconn, ai[1]); /*NOTREACHED*/ } (void) close (ai[1]); oSpipe = ai[0]; return TRUE; } /* Copy all data from the terminal to the communications port. If we see an escape character following a newline character, read the next character and return it. */ boolean fsysdep_cu (qconn, pbcmd, zlocalname) struct sconnection *qconn; char *pbcmd; const char *zlocalname; { boolean fstart; char b; int c; fstart = TRUE; while (TRUE) { if (fsysdep_catch ()) usysdep_start_catch (); else { ulog (LOG_ERROR, (const char *) NULL); return FALSE; } c = read (0, &b, 1); usysdep_end_catch (); if (c <= 0) break; if (fstart && b == *zCuvar_escape && b != '\0') { c = cscu_escape (pbcmd, zlocalname); if (c <= 0) break; if (*pbcmd != b) { write (1, pbcmd, 1); /* For Unix, we let the eof character be the same as '.', and we let the suspend character (if any) be the same as 'z'. */ if (*pbcmd == bSeof) *pbcmd = '.'; if (*pbcmd == bStstp) *pbcmd = 'z'; return TRUE; } } if (! fconn_write (qconn, &b, (size_t) 1)) return FALSE; fstart = strchr (zCuvar_eol, b) != NULL; } if (c < 0) { if (errno != EINTR) ulog (LOG_ERROR, "read: %s", strerror (errno)); else ulog (LOG_ERROR, (const char *) NULL); return FALSE; } /* I'm not sure what's best in this case. */ ulog (LOG_ERROR, "End of file on terminal"); return FALSE; } /* A SIGALRM handler that sets fScu_alarm and optionally longjmps. */ volatile sig_atomic_t fScu_alarm; static RETSIGTYPE uscu_alarm (isig) int isig ATTRIBUTE_UNUSED; { #if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET (void) signal (isig, uscu_alarm); #endif fScu_alarm = TRUE; #if HAVE_RESTARTABLE_SYSCALLS if (fSjmp) longjmp (sSjmp_buf, 1); #endif } /* We've just seen an escape character. We print the host name, optionally after a 1 second delay. We read the next character from the terminal and return it. The 1 second delay on the host name is mostly to be fancy; it lets ~~ look smoother. */ static int cscu_escape (pbcmd, zlocalname) char *pbcmd; const char *zlocalname; { CATCH_PROTECT int c; write (1, zCuvar_escape, 1); fScu_alarm = FALSE; usset_signal (SIGALRM, uscu_alarm, TRUE, (boolean *) NULL); if (fsysdep_catch ()) { usysdep_start_catch (); alarm (1); } c = 0; while (TRUE) { if (fScu_alarm) { char b; fScu_alarm = FALSE; b = '['; write (1, &b, 1); write (1, zlocalname, strlen (zlocalname)); b = ']'; write (1, &b, 1); } if (c <= 0) c = read (0, pbcmd, 1); if (c >= 0 || errno != EINTR) { usysdep_end_catch (); usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); alarm (0); return c; } } } /* A SIGALRM handler which does nothing but send a signal to the child process and schedule another alarm. POSIX.1 permits kill and alarm from a signal handler. The reference to static data may or may not be permissible. */ static volatile sig_atomic_t iSsend_sig; static RETSIGTYPE uscu_alarm_kill (isig) int isig ATTRIBUTE_UNUSED; { #if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET (void) signal (isig, uscu_alarm_kill); #endif (void) kill (iSchild, iSsend_sig); alarm (1); } /* Start or stop copying data from the communications port to the terminal. We send a signal to the child process to tell it what to do. Unfortunately, there are race conditions in the child, so we keep sending it a signal once a second until it responds. We send SIGUSR1 to make it start copying, and SIGUSR2 to make it stop. */ boolean fsysdep_cu_copy (fcopy) boolean fcopy; { int ierr; int c; usset_signal (SIGALRM, uscu_alarm_kill, TRUE, (boolean *) NULL); if (fcopy) iSsend_sig = SIGUSR1; else iSsend_sig = SIGUSR2; uscu_alarm_kill (SIGALRM); alarm (1); while (TRUE) { char b; c = read (oSpipe, &b, 1); #if DEBUG > 1 if (c > 0) DEBUG_MESSAGE1 (DEBUG_INCOMING, "fsysdep_cu_copy: Got '%d'", b); #endif if ((c < 0 && errno != EINTR) || c == 0 || (c > 0 && b == (fcopy ? CHILD_STARTED : CHILD_STOPPED))) break; /* If none of the above conditions were true, then we either got an EINTR error, in which case we probably timed out and the SIGALRM handler resent the signal, or we read the wrong character, in which case we will just read again from the pipe. */ } ierr = errno; usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); alarm (0); if (c > 0) return TRUE; if (c == 0) ulog (LOG_ERROR, "EOF on child pipe"); else ulog (LOG_ERROR, "read: %s", strerror (ierr)); return FALSE; } /* Shut down cu by killing the child process. */ boolean fsysdep_cu_finish () { (void) close (oSpipe); /* We hit the child with SIGTERM, give it two seconds to die, and then send a SIGKILL. */ if (kill (iSchild, SIGTERM) < 0) { /* Don't give an error if the child has already died. */ if (errno != ESRCH) ulog (LOG_ERROR, "kill: %s", strerror (errno)); } usset_signal (SIGALRM, uscu_alarm_kill, TRUE, (boolean *) NULL); iSsend_sig = SIGKILL; alarm (2); (void) ixswait ((unsigned long) iSchild, "child"); usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); alarm (0); return TRUE; } /* Code for the child process. */ /* This signal handler just records the signal. In this case we only care about which signal we received most recently. */ static volatile sig_atomic_t iSchild_sig; static RETSIGTYPE uscu_child_handler (isig) int isig; { #if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET (void) signal (isig, uscu_child_handler); #endif iSchild_sig = isig; #if HAVE_RESTARTABLE_SYSCALLS if (fSjmp) longjmp (sSjmp_buf, 1); #endif /* HAVE_RESTARTABLE_SYSCALLS */ } /* The child process. This copies the port to the terminal, except when it is stopped by a signal. It would be reasonable to write a separate program for this, probably passing it the port on stdin. This would reduce the memory requirements, since we wouldn't need a second process holding all the configuration stuff, and also let it work reasonably on 680x0 versions of MINIX. */ static void uscu_child (qconn, opipe) struct sconnection *qconn; int opipe; { CATCH_PROTECT int oport; CATCH_PROTECT boolean fstopped, fgot; CATCH_PROTECT int cwrite; CATCH_PROTECT char abbuf[1024]; fgot = FALSE; /* It would be nice if we could just use fsysdep_conn_read, but that will log signals that we don't want logged. There should be a generic way to extract the file descriptor from the port. */ if (qconn->qport == NULL) oport = 0; else { switch (qconn->qport->uuconf_ttype) { #if DEBUG > 0 default: ulog (LOG_FATAL, "uscu_child: Can't happen"); oport = -1; break; #endif case UUCONF_PORTTYPE_PIPE: /* A read of 0 on a pipe always means EOF (see below). */ fgot = TRUE; /* Fall through. */ case UUCONF_PORTTYPE_STDIN: oport = ((struct ssysdep_conn *) qconn->psysdep)->ord; break; case UUCONF_PORTTYPE_MODEM: case UUCONF_PORTTYPE_DIRECT: case UUCONF_PORTTYPE_TCP: case UUCONF_PORTTYPE_TLI: oport = ((struct ssysdep_conn *) qconn->psysdep)->o; break; } } /* Force the descriptor into blocking mode. */ (void) fcntl (oport, F_SETFL, fcntl (oport, F_GETFL, 0) &~ (O_NDELAY | O_NONBLOCK)); usset_signal (SIGUSR1, uscu_child_handler, TRUE, (boolean *) NULL); usset_signal (SIGUSR2, uscu_child_handler, TRUE, (boolean *) NULL); usset_signal (SIGINT, SIG_IGN, TRUE, (boolean *) NULL); usset_signal (SIGQUIT, SIG_IGN, TRUE, (boolean *) NULL); usset_signal (SIGPIPE, SIG_DFL, TRUE, (boolean *) NULL); usset_signal (SIGTERM, uscu_child_handler, TRUE, (boolean *) NULL); fstopped = FALSE; iSchild_sig = 0; cwrite = 0; if (fsysdep_catch ()) { usysdep_start_catch (); } while (TRUE) { int isig; int c; /* There is a race condition here between checking the signal and receiving a new and possibly different one. This is solved by having the parent resend the signal until it gets a response. */ isig = iSchild_sig; iSchild_sig = 0; if (isig != 0) { char b; if (isig == SIGTERM) exit (EXIT_SUCCESS); if (isig == SIGUSR1) { fstopped = FALSE; b = CHILD_STARTED; } else { fstopped = TRUE; b = CHILD_STOPPED; cwrite = 0; } c = write (opipe, &b, 1); /* Apparently on some systems we can get EAGAIN here. */ if (c < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)) c = 0; if (c <= 0) { /* Should we give an error message here? */ (void) kill (getppid (), SIGHUP); exit (EXIT_FAILURE); } } if (fstopped) pause (); else if (cwrite > 0) { char *zbuf; zbuf = abbuf; while (cwrite > 0) { c = write (1, zbuf, cwrite); /* Apparently on some systems we can get EAGAIN here. */ if (c < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)) c = 0; if (c < 0 && errno == EINTR) break; if (c <= 0) { /* Should we give an error message here? */ (void) kill (getppid (), SIGHUP); exit (EXIT_FAILURE); } cwrite -= c; zbuf += c; } } else { /* On some systems apparently read will return 0 until something has been written to the port. We therefore accept a 0 return until after we have managed to read something. Setting errno to 0 apparently avoids a problem on Coherent. */ errno = 0; c = read (oport, abbuf, sizeof abbuf); /* Apparently on some systems we can get EAGAIN here. */ if (c < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)) c = 0; if ((c == 0 && fgot) || (c < 0 && errno != EINTR)) { /* This can be a normal way to exit, depending on just how the connection is dropped. */ (void) kill (getppid (), SIGHUP); exit (EXIT_SUCCESS); } if (c > 0) { fgot = TRUE; cwrite = c; } } } } /* Terminal control routines. */ /* Whether file descriptor 0 is attached to a terminal or not. */ static boolean fSterm; /* Whether we are doing local echoing. */ static boolean fSlocalecho; /* The original state of the terminal. */ static sterminal sSterm_orig; /* The new state of the terminal. */ static sterminal sSterm_new; #if ! HAVE_BSD_TTY #ifdef SIGTSTP /* Whether SIGTSTP is being ignored. */ static boolean fStstp_ignored; #endif #endif /* Set the terminal into raw mode. */ boolean fsysdep_terminal_raw (flocalecho) boolean flocalecho; { fSlocalecho = flocalecho; /* This defaults may be overriden below. */ bSeof = '\004'; bStstp = '\032'; if (! fgetterminfo (0, &sSterm_orig)) { fSterm = FALSE; return TRUE; } fSterm = TRUE; sSterm_new = sSterm_orig; #if HAVE_BSD_TTY /* We use CBREAK mode rather than RAW mode, because RAW mode turns off all output processing, which we don't want to do. This means that we have to disable the interrupt characters, which we do by setting them to -1. */ bSeof = sSterm_orig.stchars.t_eofc; sSterm_new.stchars.t_intrc = -1; sSterm_new.stchars.t_quitc = -1; sSterm_new.stchars.t_startc = -1; sSterm_new.stchars.t_stopc = -1; sSterm_new.stchars.t_eofc = -1; sSterm_new.stchars.t_brkc = -1; bStstp = sSterm_orig.sltchars.t_suspc; sSterm_new.sltchars.t_suspc = -1; sSterm_new.sltchars.t_dsuspc = -1; sSterm_new.sltchars.t_rprntc = -1; sSterm_new.sltchars.t_flushc = -1; sSterm_new.sltchars.t_werasc = -1; sSterm_new.sltchars.t_lnextc = -1; if (! flocalecho) { sSterm_new.stty.sg_flags |= (CBREAK | ANYP); sSterm_new.stty.sg_flags &=~ (ECHO | CRMOD | TANDEM); } else { sSterm_new.stty.sg_flags |= (CBREAK | ANYP | ECHO); sSterm_new.stty.sg_flags &=~ (CRMOD | TANDEM); } #endif /* HAVE_BSD_TTY */ #if HAVE_SYSV_TERMIO bSeof = sSterm_new.c_cc[VEOF]; if (! flocalecho) sSterm_new.c_lflag &=~ (ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHONL); else sSterm_new.c_lflag &=~ (ICANON | ISIG); sSterm_new.c_iflag &=~ (INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); sSterm_new.c_oflag &=~ (OPOST); sSterm_new.c_cc[VMIN] = 1; sSterm_new.c_cc[VTIME] = 0; #endif /* HAVE_SYSV_TERMIO */ #if HAVE_POSIX_TERMIOS bSeof = sSterm_new.c_cc[VEOF]; bStstp = sSterm_new.c_cc[VSUSP]; if (! flocalecho) sSterm_new.c_lflag &=~ (ICANON | IEXTEN | ISIG | ECHO | ECHOE | ECHOK | ECHONL); else sSterm_new.c_lflag &=~ (ICANON | IEXTEN | ISIG); sSterm_new.c_iflag &=~ (INLCR | IGNCR | ICRNL | IXON | IXOFF); sSterm_new.c_oflag &=~ (OPOST); sSterm_new.c_cc[VMIN] = 1; sSterm_new.c_cc[VTIME] = 0; #endif /* HAVE_POSIX_TERMIOS */ if (! fsetterminfo (0, &sSterm_new)) { ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno)); return FALSE; } return TRUE; } /* Restore the terminal to its original setting. */ boolean fsysdep_terminal_restore () { if (! fSterm) return TRUE; if (! fsetterminfo (0, &sSterm_orig)) { ulog (LOG_ERROR, "Can't restore terminal: %s", strerror (errno)); return FALSE; } return TRUE; } /* Read a line from the terminal. This will be called after fsysdep_terminal_raw has been called. */ char * zsysdep_terminal_line (zprompt) const char *zprompt; { CATCH_PROTECT size_t cbuf = 0; CATCH_PROTECT char *zbuf = NULL; CATCH_PROTECT size_t cgot = 0; if (zprompt != NULL && *zprompt != '\0') (void) write (1, zprompt, strlen (zprompt)); /* Forgot about any previous SIGINT or SIGQUIT signals we may have received. We don't worry about the race condition here, since we can't get these signals from the terminal at the moment and it's not too likely that somebody else will be sending them to us. */ afSignal[INDEXSIG_SIGINT] = 0; afSignal[INDEXSIG_SIGQUIT] = 0; if (! fsysdep_terminal_restore ()) return NULL; if (fsysdep_catch ()) { usysdep_start_catch (); cbuf = 0; zbuf = NULL; cgot = 0; } while (TRUE) { char b; int c; if (afSignal[INDEXSIG_SIGINT] || afSignal[INDEXSIG_SIGQUIT]) { usysdep_end_catch (); /* Make sure the signal is logged. */ ulog (LOG_ERROR, (const char *) NULL); /* Return an empty string. */ cgot = 0; break; } /* There's a race here between checking the signals and calling read. It just means that the user will have to hit ^C more than once. */ c = read (0, &b, 1); if (c < 0) { if (errno == EINTR) continue; usysdep_end_catch (); ulog (LOG_ERROR, "read: %s", strerror (errno)); (void) fsysdep_terminal_raw (fSlocalecho); return NULL; } if (c == 0) { /* I'm not quite sure what to do here. */ usysdep_end_catch (); ulog (LOG_ERROR, "EOF on terminal"); (void) fsysdep_terminal_raw (fSlocalecho); return NULL; } if (cgot >= cbuf) { char *znew; cbuf += 64; znew = zbufalc (cbuf); if (zbuf != NULL) { memcpy (znew, zbuf, cgot); ubuffree (zbuf); } zbuf = znew; } zbuf[cgot] = b; ++cgot; if (b == '\n') { usysdep_end_catch (); break; } } if (cgot >= cbuf) { char *znew; ++cbuf; znew = zbufalc (cbuf); if (zbuf != NULL) { memcpy (znew, zbuf, cgot); ubuffree (zbuf); } zbuf = znew; } zbuf[cgot] = '\0'; if (! fsysdep_terminal_raw (fSlocalecho)) return NULL; return zbuf; } /* Write a line to the terminal with a trailing newline. */ boolean fsysdep_terminal_puts (zline) const char *zline; { char *zalc, *zprint; size_t clen; if (zline == NULL) { zalc = zbufalc (2); clen = 0; } else { clen = strlen (zline); zalc = zbufalc (clen + 2); memcpy (zalc, zline, clen); } if (fSterm) { zalc[clen] = '\r'; ++clen; } zalc[clen] = '\n'; ++clen; zprint = zalc; while (clen > 0) { int c; c = write (1, zprint, clen); if (c <= 0) { ubuffree (zalc); ulog (LOG_ERROR, "write: %s", strerror (errno)); return FALSE; } clen -= c; zprint += c; } ubuffree (zalc); return TRUE; } /* Allow or disallow signals from the terminal. */ boolean fsysdep_terminal_signals (faccept) boolean faccept; { #if HAVE_BSD_TTY if (faccept) { sSterm_new.stchars.t_intrc = sSterm_orig.stchars.t_intrc; sSterm_new.stchars.t_quitc = sSterm_orig.stchars.t_quitc; } else { sSterm_new.stchars.t_intrc = -1; sSterm_new.stchars.t_quitc = -1; } #else /* ! HAVE_BSD_TTY */ if (faccept) sSterm_new.c_lflag |= ISIG; else sSterm_new.c_lflag &=~ ISIG; #ifdef SIGTSTP /* We only want to get SIGINT and SIGQUIT, not SIGTSTP. This function will be called with faccept TRUE before it is called with faccept FALSE, so fStstp_ignored will be correctly initialized. */ if (faccept) usset_signal (SIGTSTP, SIG_IGN, FALSE, &fStstp_ignored); else if (! fStstp_ignored) usset_signal (SIGTSTP, SIG_DFL, TRUE, (boolean *) NULL); #endif #endif /* ! HAVE_BSD_TTY */ if (! fsetterminfo (0, &sSterm_new)) { ulog (LOG_ERROR, "Can't set terminal: %s", strerror (errno)); return FALSE; } return TRUE; } /* Start up a command, or possibly just a shell. Optionally attach stdin or stdout to the port. We attach directly to the port, rather than copying the data ourselves. */ boolean fsysdep_shell (qconn, zcmd, tcmd) struct sconnection *qconn; const char *zcmd; enum tshell_cmd tcmd; { const char *azargs[4]; int oread, owrite; int aidescs[3]; pid_t ipid; if (tcmd != SHELL_NORMAL) azargs[0] = "/bin/sh"; else { azargs[0] = getenv ("SHELL"); if (azargs[0] == NULL) azargs[0] = "/bin/sh"; } if (zcmd == NULL || *zcmd == '\0') azargs[1] = NULL; else { azargs[1] = "-c"; azargs[2] = zcmd; azargs[3] = NULL; } if (qconn->qport == NULL) { oread = 0; owrite = 1; } else { switch (qconn->qport->uuconf_ttype) { default: oread = owrite = -1; break; case UUCONF_PORTTYPE_STDIN: case UUCONF_PORTTYPE_PIPE: oread = ((struct ssysdep_conn *) qconn->psysdep)->ord; owrite = ((struct ssysdep_conn *) qconn->psysdep)->owr; break; case UUCONF_PORTTYPE_MODEM: case UUCONF_PORTTYPE_DIRECT: case UUCONF_PORTTYPE_TCP: case UUCONF_PORTTYPE_TLI: oread = owrite = ((struct ssysdep_conn *) qconn->psysdep)->o; break; } } aidescs[0] = 0; aidescs[1] = 1; aidescs[2] = 2; if (tcmd == SHELL_STDIN_FROM_PORT || tcmd == SHELL_STDIO_ON_PORT) aidescs[0] = oread; if (tcmd == SHELL_STDOUT_TO_PORT || tcmd == SHELL_STDIO_ON_PORT) aidescs[1] = owrite; ipid = ixsspawn (azargs, aidescs, FALSE, TRUE, (const char *) NULL, FALSE, FALSE, (const char *) NULL, (const char *) NULL, (const char *) NULL); if (ipid < 0) { ulog (LOG_ERROR, "ixsspawn (/bin/sh): %s", strerror (errno)); return FALSE; } return ixswait ((unsigned long) ipid, "shell") == 0; } /* Change directories. */ boolean fsysdep_chdir (zdir) const char *zdir; { if (zdir == NULL || *zdir == '\0') { zdir = getenv ("HOME"); if (zdir == NULL) { ulog (LOG_ERROR, "HOME not defined"); return FALSE; } } if (chdir (zdir) < 0) { ulog (LOG_ERROR, "chdir (%s): %s", zdir, strerror (errno)); return FALSE; } return TRUE; } /* Suspend the current process. */ boolean fsysdep_suspend () { #ifndef SIGTSTP return fsysdep_terminal_puts ("[process suspension not supported]"); #else return kill (getpid (), SIGTSTP) == 0; #endif } uucp-1.07/unix/cwd.c0000664000076400007640000000256707665321760010042 /* cwd.c Routines dealing with the current working directory. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" /* See whether running this file through zsysdep_add_cwd would require knowing the current working directory. This is used to avoid determining the cwd if it will not be needed. */ boolean fsysdep_needs_cwd (zfile) const char *zfile; { return *zfile != '/' && *zfile != '~'; } /* Expand a local file, putting relative pathnames in the current working directory. Note that ~/file is placed in the public directory, rather than in the user's home directory. This is consistent with other UUCP packages. */ char * zsysdep_local_file_cwd (zfile, zpubdir, pfbadname) const char *zfile; const char *zpubdir; boolean *pfbadname; { if (pfbadname != NULL) *pfbadname = FALSE; if (*zfile == '/') return zbufcpy (zfile); else if (*zfile == '~') return zsysdep_local_file (zfile, zpubdir, pfbadname); else return zsysdep_add_cwd (zfile); } /* Add the current working directory to a remote file name. */ char * zsysdep_add_cwd (zfile) const char *zfile; { if (*zfile == '/' || *zfile == '~') return zbufcpy (zfile); if (zScwd == NULL) { ulog (LOG_ERROR, "Can't determine current directory"); return NULL; } return zsysdep_in_dir (zScwd, zfile); } uucp-1.07/unix/detach.c0000664000076400007640000001147607665321760010514 /* detach.c Detach from the controlling terminal. Copyright (C) 1992, 1993, 1995 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "system.h" #include "sysdep.h" #include #if HAVE_SYS_IOCTL_H #include #endif #ifdef TIOCNOTTY #define HAVE_TIOCNOTTY 1 #else #define HAVE_TIOCNOTTY 0 #endif #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #if HAVE_BROKEN_SETSID #undef HAVE_SETSID #define HAVE_SETSID 0 #endif /* Detach from the controlling terminal. This is called by uucico if it is calling out to another system, so that it can receive SIGHUP signals from the port it calls out on. It is also called by uucico just before it starts uuxqt, so that uuxqt is completely independent of the terminal. */ void usysdep_detach () { pid_t igrp; /* Make sure that we can open the log file. We do this now so that, if we can't, a message will be written to stderr. After we leave this routine, stderr will be closed. */ ulog (LOG_NORMAL, (const char *) NULL); /* Make sure we are not a process group leader. */ #if HAVE_BSD_PGRP igrp = getpgrp (0); #else igrp = getpgrp (); #endif if (igrp == getpid ()) { boolean fignored; pid_t ipid; /* Ignore SIGHUP, since our process group leader is about to die. */ usset_signal (SIGHUP, SIG_IGN, FALSE, &fignored); ipid = ixsfork (); if (ipid < 0) ulog (LOG_FATAL, "fork: %s", strerror (errno)); if (ipid != 0) _exit (EXIT_SUCCESS); /* We'll always wind up as a child of process number 1, right? Right? We have to wait for our parent to die before reenabling SIGHUP. */ while (getppid () != 1) sleep (1); ipid = getpid (); ulog_id (ipid); /* Restore SIGHUP catcher if it wasn't being ignored. */ if (! fignored) usset_signal (SIGHUP, ussignal, TRUE, (boolean *) NULL); DEBUG_MESSAGE2 (DEBUG_PORT, "usysdep_detach: Forked; old PID %ld, new pid %ld", (long) igrp, (long) ipid); } #if ! HAVE_SETSID && HAVE_TIOCNOTTY /* Lose the original controlling terminal as well as our process group. */ { int o; o = open ((char *) "/dev/tty", O_RDONLY); if (o >= 0) { (void) ioctl (o, TIOCNOTTY, (char *) NULL); (void) close (o); } } #endif /* ! HAVE_SETSID && HAVE_TIOCNOTTY */ /* Close stdin, stdout and stderr and reopen them on /dev/null, to make sure we have no connection at all to the terminal. */ (void) close (0); (void) close (1); (void) close (2); if (open ((char *) "/dev/null", O_RDONLY) != 0 || open ((char *) "/dev/null", O_WRONLY) != 1 || open ((char *) "/dev/null", O_WRONLY) != 2) ulog (LOG_FATAL, "open (/dev/null): %s", strerror (errno)); #if HAVE_SETSID /* Under POSIX the setsid call creates a new session for which we are the process group leader. It also detaches us from our controlling terminal. */ if (setsid () < 0) ulog (LOG_ERROR, "setsid: %s", strerror (errno)); #else /* ! HAVE_SETSID */ #if ! HAVE_SETPGRP #error Cannot detach from controlling terminal #endif /* If we don't have setsid, we must use setpgrp. On an old System V system setpgrp will make us the leader of a new process group and detach the controlling terminal. On an old BSD system the call setpgrp (0, 0) will set our process group to 0 so that we can acquire a new controlling terminal (TIOCNOTTY may or may not have already done that anyhow). */ #if HAVE_BSD_PGRP if (setpgrp (0, 0) < 0) #else if (setpgrp () < 0) #endif { /* Some systems seem to give EPERM errors inappropriately. */ if (errno != EPERM) ulog (LOG_ERROR, "setpgrp: %s", strerror (errno)); } #endif /* ! HAVE_SETSID */ /* At this point we have completely detached from our controlling terminal. The next terminal device we open will probably become our controlling terminal. */ } uucp-1.07/unix/efopen.c0000664000076400007640000000440007665321761010526 /* efopen.c Open a stdio file with appropriate permissions. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef O_APPEND #ifdef FAPPEND #define O_APPEND FAPPEND #endif #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif FILE * esysdep_fopen (zfile, fpublic, fappend, fmkdirs) const char *zfile; boolean fpublic; boolean fappend; boolean fmkdirs; { int imode; int o; FILE *e; if (fpublic) imode = IPUBLIC_FILE_MODE; else imode = IPRIVATE_FILE_MODE; if (! fappend) o = creat ((char *) zfile, imode); else { #ifdef O_CREAT o = open ((char *) zfile, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, imode); #else o = open ((char *) zfile, O_WRONLY | O_NOCTTY); if (o < 0 && errno == ENOENT) o = creat ((char *) zfile, imode); #endif /* ! defined (O_CREAT) */ } if (o < 0) { if (errno == ENOENT && fmkdirs) { if (! fsysdep_make_dirs (zfile, fpublic)) return NULL; if (! fappend) o = creat ((char *) zfile, imode); else { #ifdef O_CREAT o = open ((char *) zfile, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, imode); #else o = creat ((char *) zfile, imode); #endif } } if (o < 0) { ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno)); return NULL; } } #ifndef O_CREAT #ifdef O_APPEND if (fappend) { if (fcntl (o, F_SETFL, O_APPEND) < 0) { ulog (LOG_ERROR, "fcntl (%s, O_APPEND): %s", zfile, strerror (errno)); (void) close (o); return NULL; } } #endif /* defined (O_APPEND) */ #endif /* ! defined (O_CREAT) */ if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (%s, FD_CLOEXEC): %s", zfile, strerror (errno)); (void) close (o); return NULL; } if (fappend) e = fdopen (o, (char *) "a"); else e = fdopen (o, (char *) "w"); if (e == NULL) { ulog (LOG_ERROR, "fdopen: %s", strerror (errno)); (void) close (o); } return e; } uucp-1.07/unix/epopen.c0000664000076400007640000000422107665321761010541 /* epopen.c A version of popen that goes through ixsspawn. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "sysdep.h" #include /* A version of popen that goes through ixsspawn. This actually takes an array of arguments rather than a string, and takes a boolean read/write value rather than a string. It sets *pipid to the process ID of the child. */ FILE * espopen (pazargs, frd, pipid) const char **pazargs; boolean frd; pid_t *pipid; { int aidescs[3]; pid_t ipid; FILE *eret; if (frd) { aidescs[0] = SPAWN_NULL; aidescs[1] = SPAWN_READ_PIPE; } else { aidescs[0] = SPAWN_WRITE_PIPE; aidescs[1] = SPAWN_NULL; } aidescs[2] = SPAWN_NULL; ipid = ixsspawn (pazargs, aidescs, TRUE, FALSE, (const char *) NULL, FALSE, TRUE, (const char *) NULL, (const char *) NULL, (const char *) NULL); if (ipid < 0) return NULL; if (frd) eret = fdopen (aidescs[1], (char *) "r"); else eret = fdopen (aidescs[0], (char *) "w"); if (eret == NULL) { int ierr; ierr = errno; (void) close (frd ? aidescs[1] : aidescs[0]); (void) kill (ipid, SIGKILL); (void) ixswait ((unsigned long) ipid, (const char *) NULL); errno = ierr; return NULL; } *pipid = ipid; return eret; } uucp-1.07/unix/exists.c0000664000076400007640000000035007665321761010571 /* exists.c Check whether a file exists. */ #include "uucp.h" #include "sysdep.h" #include "system.h" boolean fsysdep_file_exists (zfile) const char *zfile; { struct stat s; return stat ((char *) zfile, &s) == 0; } uucp-1.07/unix/failed.c0000664000076400007640000000065207665321761010503 /* failed.c Save a file in the .Failed directory. */ #include "uucp.h" #include "sysdep.h" #include "uudefs.h" #include "system.h" char * zsysdep_save_failed_file (zfile) const char *zfile; { char *zto; zto = zsappend3 (zSspooldir, FAILEDDIR, zfile); if (! fsysdep_move_file (zfile, zto, TRUE, FALSE, FALSE, (const char *) NULL)) { ubuffree (zto); return NULL; } return zto; } uucp-1.07/unix/filnam.c0000664000076400007640000003412207665321761010524 /* filnam.c Get names to use for UUCP files. Copyright (C) 1991, 1992, 1993, 1995 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "uuconf.h" #include "sysdep.h" #include "system.h" #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif /* We need a definition for SEEK_SET. */ #ifndef SEEK_SET #define SEEK_SET 0 #endif /* We use POSIX style fcntl locks if they are available, unless O_CREAT is not defined. We could use them in the latter case, but the code would have to become more complex to avoid races concerning the use of creat. It is very unlikely that there is any system which does have POSIX style locking but does not have O_CREAT. */ #if ! HAVE_BROKEN_SETLKW #ifdef F_SETLKW #ifdef O_CREAT #define USE_POSIX_LOCKS 1 #endif #endif #endif #ifndef USE_POSIX_LOCKS #define USE_POSIX_LOCKS 0 #endif /* External functions. */ #ifndef lseek extern off_t lseek (); #endif #define ZCHARS \ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* Local functions. */ static boolean fscmd_seq P((const char *zsystem, char *zseq)); static char *zsfile_name P((int btype, const char *zsystem, const char *zlocalname, int bgrade, boolean fxqt, char *ztname, char *zdname, char *zxname)); /* Get a new command sequence number (this is not a sequence number to be used for communicating with another system, but a sequence number to be used when generating the name of a command file). The sequence number is placed into zseq, which should be five characters long. */ static boolean fscmd_seq (zsystem, zseq) const char *zsystem; char *zseq; { int cdelay; char *zfree; const char *zfile; int o; boolean flockfile; int i; boolean fret; cdelay = 5; #if ! USE_POSIX_LOCKS { boolean ferr; /* Lock the sequence file. */ while (! fsdo_lock ("LCK..SEQ", TRUE, &ferr)) { if (ferr || FGOT_SIGNAL ()) return FALSE; sleep (cdelay); if (cdelay < 60) ++cdelay; } } #endif zfree = NULL; #if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 zfile = "SEQF"; #endif #if SPOOLDIR_HDB || SPOOLDIR_SVR4 zfree = zsysdep_in_dir (".Sequence", zsystem); zfile = zfree; #endif #if SPOOLDIR_ULTRIX if (! fsultrix_has_spool (zsystem)) zfile = "sys/DEFAULT/.SEQF"; else { zfree = zsappend3 ("sys", zsystem, ".SEQF"); zfile = zfree; } #endif /* SPOOLDIR_ULTRIX */ #if SPOOLDIR_TAYLOR zfree = zsysdep_in_dir (zsystem, "SEQF"); zfile = zfree; #endif /* SPOOLDIR_TAYLOR */ #ifdef O_CREAT o = open ((char *) zfile, O_RDWR | O_CREAT | O_NOCTTY, IPRIVATE_FILE_MODE); #else o = open ((char *) zfile, O_RDWR | O_NOCTTY); if (o < 0 && errno == ENOENT) { o = creat ((char *) zfile, IPRIVATE_FILE_MODE); if (o >= 0) { (void) close (o); o = open ((char *) zfile, O_RDWR | O_NOCTTY); } } #endif if (o < 0) { if (errno == ENOENT) { if (! fsysdep_make_dirs (zfile, FALSE)) { #if ! USE_POSIX_LOCKS (void) fsdo_unlock ("LCK..SEQ", TRUE); #endif return FALSE; } #ifdef O_CREAT o = open ((char *) zfile, O_RDWR | O_CREAT | O_NOCTTY, IPRIVATE_FILE_MODE); #else o = creat ((char *) zfile, IPRIVATE_FILE_MODE); if (o >= 0) { (void) close (o); o = open ((char *) zfile, O_RDWR | O_NOCTTY); } #endif } if (o < 0) { ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno)); #if ! USE_POSIX_LOCKS (void) fsdo_unlock ("LCK..SEQ", TRUE); #endif return FALSE; } } #if ! USE_POSIX_LOCKS flockfile = TRUE; #else { struct flock slock; flockfile = FALSE; slock.l_type = F_WRLCK; slock.l_whence = SEEK_SET; slock.l_start = 0; slock.l_len = 0; while (fcntl (o, F_SETLKW, &slock) == -1) { boolean fagain; /* Some systems define F_SETLKW, but it does not work. We try to catch those systems at runtime, and revert to using a lock file. */ if (errno == EINVAL) { boolean ferr; /* Lock the sequence file. */ while (! fsdo_lock ("LCK..SEQ", TRUE, &ferr)) { if (ferr || FGOT_SIGNAL ()) { (void) close (o); return FALSE; } sleep (cdelay); if (cdelay < 60) ++cdelay; } flockfile = TRUE; break; } fagain = FALSE; if (errno == ENOMEM) fagain = TRUE; #ifdef ENOLCK if (errno == ENOLCK) fagain = TRUE; #endif #ifdef ENOSPC if (errno == ENOSPC) fagain = TRUE; #endif if (fagain) { sleep (cdelay); if (cdelay < 60) ++cdelay; continue; } ulog (LOG_ERROR, "Locking %s: %s", zfile, strerror (errno)); (void) close (o); return FALSE; } } #endif if (read (o, zseq, CSEQLEN) != CSEQLEN) strcpy (zseq, "0000"); zseq[CSEQLEN] = '\0'; /* We must add one to the sequence number and return the new value. On Ultrix, arbitrary characters are allowed in the sequence number. On other systems, the sequence number apparently must be in hex. */ #if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_HDB || SPOOLDIR_SVR4 i = (int) strtol (zseq, (char **) NULL, 16); ++i; if (i > 0xffff) i = 0; /* The sprintf argument has CSEQLEN built into it. */ sprintf (zseq, "%04x", (unsigned int) i); #endif #if SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR for (i = CSEQLEN - 1; i >= 0; i--) { const char *zdig; zdig = strchr (ZCHARS, zseq[i]); if (zdig == NULL || zdig[0] == '\0' || zdig[1] == '\0') zseq[i] = '0'; else { zseq[i] = zdig[1]; break; } } #endif /* SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR */ fret = TRUE; if (lseek (o, (off_t) 0, SEEK_SET) < 0 || write (o, zseq, CSEQLEN) != CSEQLEN || close (o) < 0) { ulog (LOG_ERROR, "lseek or write or close %s: %s", zfile, strerror (errno)); (void) close (o); fret = FALSE; } if (flockfile) (void) fsdo_unlock ("LCK..SEQ", TRUE); ubuffree (zfree); return fret; } /* Get the name of a command or data file for a remote system. The btype argument should be C for a command file or D for a data file. If the grade of a data file is X, it is assumed that this is going to become an execute file on some other system. The zsystem argument is the system that the file will be transferred to. The ztname argument will be set to a file name that could be passed to zsysdep_spool_file_name. The zdname argument, if not NULL, will be set to a data file name appropriate for the remote system. The zxname argument, if not NULL, will be set to the name of an execute file on the remote system. None of the names will be more than 14 characters long. */ /*ARGSUSED*/ static char * zsfile_name (btype, zsystem, zlocalname, bgrade, fxqt, ztname, zdname, zxname) int btype; const char *zsystem; const char *zlocalname; int bgrade; boolean fxqt; char *ztname; char *zdname; char *zxname; { char abseq[CSEQLEN + 1]; char absimple[11 + CSEQLEN]; char *zname; if (zlocalname == NULL) zlocalname = zSlocalname; while (TRUE) { if (! fscmd_seq (zsystem, abseq)) return NULL; if (btype == 'C') { #if ! SPOOLDIR_TAYLOR sprintf (absimple, "C.%.7s%c%s", zsystem, bgrade, abseq); #else sprintf (absimple, "C.%c%s", bgrade, abseq); #endif } else if (btype == 'D') { /* This name doesn't really matter that much; it's just the name we use on the local system. The name we use on the remote system, which we return in zdname, should contain our system name so that remote UUCP's running SPOOLDIR_V2 and the like can distinguish while files come from which systems. */ #if SPOOLDIR_SVR4 sprintf (absimple, "D.%.7s%c%s", zsystem, bgrade, abseq); #else /* ! SPOOLDIR_SVR4 */ #if ! SPOOLDIR_TAYLOR sprintf (absimple, "D.%.7s%c%s", zlocalname, bgrade, abseq); #else /* SPOOLDIR_TAYLOR */ if (fxqt) sprintf (absimple, "D.X%s", abseq); else sprintf (absimple, "D.%s", abseq); #endif /* SPOOLDIR_TAYLOR */ #endif /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ } #if DEBUG > 0 else ulog (LOG_FATAL, "zsfile_name: Can't happen"); #endif zname = zsfind_file (absimple, zsystem, bgrade); if (zname == NULL) return NULL; if (! fsysdep_file_exists (zname)) break; ubuffree (zname); } if (ztname != NULL) strcpy (ztname, absimple); if (zdname != NULL) sprintf (zdname, "D.%.7s%c%s", zlocalname, bgrade, abseq); if (zxname != NULL) sprintf (zxname, "X.%.7s%c%s", zlocalname, bgrade, abseq); return zname; } /* Return a name to use for a data file to be copied to another system. The name returned will be for a real file. The zlocalname argument is the local name as seen by the remote system, the bgrade argument is the file grade, and the fxqt argument is TRUE if this file will become an execution file. The ztname argument, if not NULL, will be set to a name that could be passed to zsysdep_spool_file_name to get back the return value of this function. The zdname argument, if not NULL, will be set to a name that the file could be given on another system. The zxname argument, if not NULL, will be set to a name for an execute file on another system. */ char * zsysdep_data_file_name (qsys, zlocalname, bgrade, fxqt, ztname, zdname, zxname) const struct uuconf_system *qsys; const char *zlocalname; int bgrade; boolean fxqt; char *ztname; char *zdname; char *zxname; { return zsfile_name ('D', qsys->uuconf_zname, zlocalname, bgrade, fxqt, ztname, zdname, zxname); } #if SPOOLDIR_TAYLOR /* Write out a number in base 62 into a given number of characters, right justified with zero fill. This is used by zscmd_file if SPOOLDIR_TAYLOR. */ static void usput62 P((long i, char *, int c)); static void usput62 (i, z, c) long i; char *z; int c; { for (--c; c >= 0; --c) { int d; d = i % 62; i /= 62; if (d < 26) z[c] = 'A' + d; else if (d < 52) z[c] = 'a' + d - 26; else z[c] = '0' + d - 52; } } #endif /* SPOOLDIR_TAYLOR */ /* Get a command file name. */ char * zscmd_file (qsys, bgrade) const struct uuconf_system *qsys; int bgrade; { #if ! SPOOLDIR_TAYLOR return zsfile_name ('C', qsys->uuconf_zname, (const char *) NULL, bgrade, FALSE, (char *) NULL, (char *) NULL, (char *) NULL); #else char *zname; long isecs, imicros; pid_t ipid; /* This file name is never seen by the remote system, so we don't actually need to get a sequence number for it. We just need to get a file name which is unique for this system. We don't try this optimization for other spool directory formats, mainly due to compatibility concerns. It would be possible for HDB and SVR4 spool directory formats. We get a unique name by combining the process ID and the current time. The file name must start with C.g, where g is the grade. Note that although it is likely that this name will be unique, it is not guaranteed, so the caller must be careful. */ isecs = ixsysdep_time (&imicros); ipid = getpid (); /* We are going to represent the file name as a series of numbers in base 62 (using the alphanumeric characters). The maximum file name length is 14 characters, so we may use 11. We use 3 for the seconds within the day, 3 for the microseconds, and 5 for the process ID. */ /* Cut the seconds down to a number within a day (maximum value 86399 < 62 ** 3 == 238328). */ isecs %= (long) 24 * (long) 60 * (long) 60; /* Divide the microseconds (max 999999) by 5 to make sure they are less than 62 ** 3. */ imicros %= 1000000; imicros /= 5; while (TRUE) { char ab[15]; ab[0] = 'C'; ab[1] = '.'; ab[2] = bgrade; usput62 (isecs, ab + 3, 3); usput62 (imicros, ab + 6, 3); usput62 ((long) ipid, ab + 9, 5); ab[14] = '\0'; zname = zsfind_file (ab, qsys->uuconf_zname, bgrade); if (zname == NULL) return NULL; if (! fsysdep_file_exists (zname)) break; ubuffree (zname); /* We hit a duplicate. Move backward in time until we find an available name. Note that there is still a theoretical race condition, since 5 base 62 digits might not be enough for the process ID, and some other process might be running these checks at the same time as we are. The caller must deal with this. */ if (imicros == 0) { imicros = (long) 62 * (long) 62 * (long) 62; if (isecs == 0) isecs = (long) 62 * (long) 62 * (long) 62; --isecs; } --imicros; } return zname; #endif } /* Return a name for an execute file to be created locally. This is used by uux to execute a command locally with remote files. */ char * zsysdep_xqt_file_name () { char abseq[CSEQLEN + 1]; char absx[11 + CSEQLEN]; char *zname; while (TRUE) { if (! fscmd_seq (zSlocalname, abseq)) return NULL; sprintf (absx, "X.%.7sX%s", zSlocalname, abseq); zname = zsfind_file (absx, zSlocalname, -1); if (zname == NULL) return NULL; if (! fsysdep_file_exists (zname)) break; ubuffree (zname); } return zname; } uucp-1.07/unix/fsusg.c0000664000076400007640000001743707665321761010417 /* fsusage.c -- return space usage of mounted filesystems Copyright (C) 1991, 1992 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. This file was modified slightly by Ian Lance Taylor, December 1992, and again July 1995, for use with Taylor UUCP. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "fsusg.h" int statfs (); #if HAVE_SYS_PARAM_H #include #endif #if HAVE_SYS_MOUNT_H #include #endif #if HAVE_SYS_VFS_H #include #endif #if HAVE_SYS_FILSYS_H #include /* SVR2. */ #endif #if HAVE_FCNTL_H #include #endif #if HAVE_SYS_STATFS_H #include #endif #if HAVE_SYS_DUSTAT_H /* AIX PS/2. */ #include #endif #if HAVE_SYS_STATVFS_H /* SVR4. */ #include int statvfs (); #endif #if HAVE_USTAT_H /* SVR2 and others. */ #include #endif #if STAT_DISK_SPACE /* QNX. */ #include #include #endif #define STAT_NONE 0 #if ! STAT_STATFS3_OSF1 #if ! STAT_STATFS2_FS_DATA #if ! STAT_STATFS2_BSIZE #if ! STAT_STATFS2_FSIZE #if ! STAT_STATFS4 #if ! STAT_STATVFS #if ! STAT_DISK_SPACE #if ! STAT_USTAT #undef STAT_NONE #define STAT_NONE 1 #endif #endif #endif #endif #endif #endif #endif #endif #if ! STAT_NONE static long adjust_blocks P((long blocks, int fromsize, int tosize)); /* Return the number of TOSIZE-byte blocks used by BLOCKS FROMSIZE-byte blocks, rounding away from zero. TOSIZE must be positive. Return -1 if FROMSIZE is not positive. */ static long adjust_blocks (blocks, fromsize, tosize) long blocks; int fromsize, tosize; { if (tosize <= 0) abort (); if (fromsize <= 0) return -1; if (fromsize == tosize) /* E.g., from 512 to 512. */ return blocks; else if (fromsize > tosize) /* E.g., from 2048 to 512. */ return blocks * (fromsize / tosize); else /* E.g., from 256 to 512. */ return (blocks + (blocks < 0 ? -1 : 1)) / (tosize / fromsize); } #endif /* Fill in the fields of FSP with information about space usage for the filesystem on which PATH resides. DISK is the device on which PATH is mounted, for space-getting methods that need to know it. Return 0 if successful, -1 if not. */ int get_fs_usage (path, disk, fsp) char *path, *disk ATTRIBUTE_UNUSED; struct fs_usage *fsp; { #if STAT_NONE return -1; #endif #if STAT_STATFS3_OSF1 struct statfs fsd; if (statfs (path, &fsd, sizeof (struct statfs)) != 0) return -1; #define CONVERT_BLOCKS(b) adjust_blocks ((b), fsd.f_fsize, 512) #endif /* STAT_STATFS3_OSF1 */ #if STAT_STATFS2_FS_DATA /* Ultrix. */ struct fs_data fsd; if (statfs (path, &fsd) != 1) return -1; #define CONVERT_BLOCKS(b) adjust_blocks ((long) (b), 1024, 512) fsp->fsu_blocks = CONVERT_BLOCKS (fsd.fd_req.btot); fsp->fsu_bfree = CONVERT_BLOCKS (fsd.fd_req.bfree); fsp->fsu_bavail = CONVERT_BLOCKS (fsd.fd_req.bfreen); fsp->fsu_files = fsd.fd_req.gtot; fsp->fsu_ffree = fsd.fd_req.gfree; #endif #if STAT_STATFS2_BSIZE /* 4.3BSD, SunOS 4, HP-UX, AIX. */ struct statfs fsd; if (statfs (path, &fsd) < 0) return -1; #define CONVERT_BLOCKS(b) adjust_blocks ((b), fsd.f_bsize, 512) #endif #if STAT_STATFS2_FSIZE /* 4.4BSD. */ struct statfs fsd; if (statfs (path, &fsd) < 0) return -1; #define CONVERT_BLOCKS(b) adjust_blocks ((b), fsd.f_fsize, 512) #endif #if STAT_STATFS4 /* SVR3, Dynix, Irix. */ struct statfs fsd; if (statfs (path, &fsd, sizeof fsd, 0) < 0) return -1; /* Empirically, the block counts on most SVR3 and SVR3-derived systems seem to always be in terms of 512-byte blocks, no matter what value f_bsize has. */ # if _AIX # define CONVERT_BLOCKS(b) adjust_blocks ((b), fsd.f_bsize, 512) # else # define CONVERT_BLOCKS(b) (b) # ifndef _SEQUENT_ /* _SEQUENT_ is DYNIX/ptx. */ # ifndef DOLPHIN /* DOLPHIN 3.8.alfa/7.18 has f_bavail */ # define f_bavail f_bfree # endif # endif # endif #endif #if STAT_STATVFS /* SVR4. */ struct statvfs fsd; if (statvfs (path, &fsd) < 0) return -1; /* f_frsize isn't guaranteed to be supported. */ #define CONVERT_BLOCKS(b) \ adjust_blocks ((b), fsd.f_frsize ? fsd.f_frsize : fsd.f_bsize, 512) #endif #if STAT_DISK_SPACE /* QNX. */ int o; int iret; long cfree_blocks, ctotal_blocks; char *zpath; char *zslash; zpath = zbufcpy (path); while ((o = open (zpath, O_RDONLY, 0)) == -1 && errno == ENOENT) { /* The named file doesn't exist, so we can't open it. Try the directory containing it. */ if ((strcmp ("/", zpath) == 0) || (strcmp (zpath, ".") == 0) || (strcmp (zpath, "") == 0) /* QNX peculiarity: "//2" means root on node 2 */ || ((strncmp (zpath, "//", 2) == 0) && (strchr (zpath + 2, '/') == NULL))) { /* We can't shorten this! */ break; } /* Shorten the pathname by one component and try again. */ zslash = strrchr (zpath, '/'); if (zslash == NULL) { /* Try the current directory. We can open directories. */ zpath[0] = '.'; zpath[1] = '\0'; } else if (zslash == zpath) { /* Try the root directory. */ zpath[0] = '/'; zpath[1] = '\0'; } else { /* Chop off last path component. */ zslash[0] = '\0'; } } if (o == -1) { ulog (LOG_ERROR, "get_fs_usage: open (%s) failed: %s", zpath, strerror (errno)); ubuffree (zpath); return -1; } ubuffree (zpath); iret = disk_space (o, &cfree_blocks, &ctotal_blocks); (void) close (o); if (iret == -1) { ulog (LOG_ERROR, "get_fs_usage: disk_space failed: %s", strerror (errno)); return -1; } fsp->fsu_blocks = ctotal_blocks; fsp->fsu_bfree = cfree_blocks; fsp->fsu_bavail = cfree_blocks; /* QNX has no limit on the number of inodes. Most inodes are stored directly in the directory entry. */ fsp->fsu_files = -1; fsp->fsu_ffree = -1; #endif /* STAT_DISK_SPACE */ #if STAT_USTAT struct stat sstat; struct ustat s; if (stat (path, &sstat) < 0 || ustat (sstat.st_dev, &s) < 0) return -1; fsp->fsu_blocks = -1; fsp->fsu_bfree = s.f_tfree; fsp->fsu_bavail = s.f_tfree; fsp->fsu_files = -1; fsp->fsu_ffree = -1; #endif #if ! STAT_STATFS2_FS_DATA /* ! Ultrix */ #if ! STAT_DISK_SPACE #if ! STAT_USTAT #if ! STAT_NONE fsp->fsu_blocks = CONVERT_BLOCKS (fsd.f_blocks); fsp->fsu_bfree = CONVERT_BLOCKS (fsd.f_bfree); fsp->fsu_bavail = CONVERT_BLOCKS (fsd.f_bavail); fsp->fsu_files = fsd.f_files; fsp->fsu_ffree = fsd.f_ffree; #endif #endif #endif #endif return 0; } #ifdef _AIX #ifdef _I386 /* AIX PS/2 does not supply statfs. */ int statfs (path, fsb) char *path; struct statfs *fsb; { struct stat stats; struct dustat fsd; if (stat (path, &stats)) return -1; if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd))) return -1; fsb->f_type = 0; fsb->f_bsize = fsd.du_bsize; fsb->f_blocks = fsd.du_fsize - fsd.du_isize; fsb->f_bfree = fsd.du_tfree; fsb->f_bavail = fsd.du_tfree; fsb->f_files = (fsd.du_isize - 2) * fsd.du_inopb; fsb->f_ffree = fsd.du_tinode; fsb->f_fsid.val[0] = fsd.du_site; fsb->f_fsid.val[1] = fsd.du_pckno; return 0; } #endif /* _I386 */ #endif /* _AIX */ uucp-1.07/unix/indir.c0000664000076400007640000000646107665321761010370 /* indir.c See if a file is in a directory. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include /* See whether a file is in a directory, and optionally check access. */ boolean fsysdep_in_directory (zfile, zdir, fcheck, freadable, zuser) const char *zfile; const char *zdir; boolean fcheck; boolean freadable; const char *zuser; { size_t c; char *zcopy, *zslash; struct stat s; if (*zfile != '/') return FALSE; c = strlen (zdir); if (c > 0 && zdir[c - 1] == '/') c--; if (strncmp (zfile, zdir, c) != 0 || (zfile[c] != '/' && zfile[c] != '\0')) return FALSE; if (strstr (zfile + c, "/../") != NULL) return FALSE; /* If we're not checking access, get out now. */ if (! fcheck) return TRUE; zcopy = zbufcpy (zfile); /* Start checking directories after zdir. Otherwise, we would require that all directories down to /usr/spool/uucppublic be publically searchable; they probably are but it should not be a requirement. */ zslash = zcopy + c; do { char b; struct stat shold; b = *zslash; *zslash = '\0'; shold = s; if (stat (zcopy, &s) != 0) { if (errno != ENOENT) { ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno)); ubuffree (zcopy); return FALSE; } /* If this is the top directory, any problems will be caught later when we try to open it. */ if (zslash == zcopy + c) { ubuffree (zcopy); return TRUE; } /* Go back and check the last directory for read or write access. */ s = shold; break; } /* If this is not a directory, get out of the loop. */ if (! S_ISDIR (s.st_mode)) break; /* Make sure the directory is searchable. */ if (! fsuser_access (&s, X_OK, zuser)) { ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); ubuffree (zcopy); return FALSE; } /* If we've reached the end of the string, get out. */ if (b == '\0') break; *zslash = b; } while ((zslash = strchr (zslash + 1, '/')) != NULL); /* At this point s holds a stat on the last component of the path. We must check it for readability or writeability. */ if (! fsuser_access (&s, freadable ? R_OK : W_OK, zuser)) { ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); ubuffree (zcopy); return FALSE; } ubuffree (zcopy); return TRUE; } uucp-1.07/unix/init.c0000664000076400007640000002306007665321761010220 /* init.c Initialize the system dependent routines. Copyright (C) 1991, 1992, 1993, 1994, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "sysdep.h" #include #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #if ! HAVE_GETHOSTNAME && HAVE_UNAME #include #endif /* Use getcwd in preference to getwd; if we have neither, we will be using a getcwd replacement. */ #if HAVE_GETCWD #undef HAVE_GETWD #define HAVE_GETWD 0 #else /* ! HAVE_GETCWD */ #if ! HAVE_GETWD #undef HAVE_GETCWD #define HAVE_GETCWD 1 #endif /* ! HAVE_GETWD */ #endif /* ! HAVE_GETCWD */ #if HAVE_GETWD /* Get a value for MAXPATHLEN. */ #if HAVE_SYS_PARAMS_H #include #endif #if HAVE_LIMITS_H #include #endif #ifndef MAXPATHLEN #ifdef PATH_MAX #define MAXPATHLEN PATH_MAX #else /* ! defined (PATH_MAX) */ #define MAXPATHLEN 1024 #endif /* ! defined (PATH_MAX) */ #endif /* ! defined (MAXPATHLEN) */ #endif /* HAVE_GETWD */ /* External functions. */ #ifndef getlogin extern char *getlogin (); #endif #if GETPWNAM_DECLARATION_OK #ifndef getpwnam extern struct passwd *getpwnam (); #endif #endif #if GETPWUID_DECLARATION_OK #ifndef getpwuid extern struct passwd *getpwuid (); #endif #endif #if HAVE_GETCWD #ifndef getcwd extern char *getcwd (); #endif #endif #if HAVE_GETWD #ifndef getwd extern char *getwd (); #endif #endif #if HAVE_SYSCONF #ifndef sysconf extern long sysconf (); #endif #endif /* Initialize the system dependent routines. We will probably be running suid to uucp, so we make sure that nothing is obviously wrong. We save the login name since we will be losing the real uid. */ static char *zSlogin; /* The UUCP spool directory. */ const char *zSspooldir; /* The UUCP lock directory. */ const char *zSlockdir; /* The local UUCP name. */ const char *zSlocalname; /* We save the current directory since we will do a chdir to the spool directory. */ char *zScwd; /* The maximum length of a system name is controlled by the type of spool directory we use. */ #if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX size_t cSysdep_max_name_len = 7; #endif #if SPOOLDIR_HDB || SPOOLDIR_SVR4 size_t cSysdep_max_name_len = 14; #endif #if SPOOLDIR_TAYLOR #if HAVE_LONG_FILE_NAMES size_t cSysdep_max_name_len = 255; #else /* ! HAVE_LONG_FILE_NAMES */ size_t cSysdep_max_name_len = 14; #endif /* ! HAVE_LONG_FILE_NAMES */ #endif /* SPOOLDIR_TAYLOR */ /* Initialize the system dependent routines. */ void usysdep_initialize (puuconf,iflags) pointer puuconf; int iflags; { int iuuconf; char *z; struct passwd *q; ulog_id (getpid ()); if ((iflags & INIT_NOCLOSE) == 0) { int cdescs; int o; /* Close everything but stdin, stdout and stderr. */ #if HAVE_GETDTABLESIZE cdescs = getdtablesize (); #else #if HAVE_SYSCONF cdescs = sysconf (_SC_OPEN_MAX); #else #ifdef OPEN_MAX cdescs = OPEN_MAX; #else #ifdef NOFILE cdescs = NOFILE; #else cdescs = 20; #endif /* ! defined (NOFILE) */ #endif /* ! defined (OPEN_MAX) */ #endif /* ! HAVE_SYSCONF */ #endif /* ! HAVE_GETDTABLESIZE */ for (o = 3; o < cdescs; o++) (void) close (o); } /* Make sure stdin, stdout and stderr are open. */ if (fcntl (0, F_GETFD, 0) < 0 && open ((char *) "/dev/null", O_RDONLY, 0) != 0) exit (EXIT_FAILURE); if (fcntl (1, F_GETFD, 0) < 0 && open ((char *) "/dev/null", O_WRONLY, 0) != 1) exit (EXIT_FAILURE); if (fcntl (2, F_GETFD, 0) < 0 && open ((char *) "/dev/null", O_WRONLY, 0) != 2) exit (EXIT_FAILURE); iuuconf = uuconf_spooldir (puuconf, &zSspooldir); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); iuuconf = uuconf_lockdir (puuconf, &zSlockdir); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); iuuconf = uuconf_localname (puuconf, &zSlocalname); if (iuuconf == UUCONF_NOT_FOUND) { #if HAVE_GETHOSTNAME char ab[256]; if (gethostname (ab, sizeof ab - 1) < 0) ulog (LOG_FATAL, "gethostname: %s", strerror (errno)); ab[sizeof ab - 1] = '\0'; ab[strcspn (ab, ".")] = '\0'; zSlocalname = zbufcpy (ab); #else /* ! HAVE_GETHOSTNAME */ #if HAVE_UNAME struct utsname s; if (uname (&s) < 0) ulog (LOG_FATAL, "uname: %s", strerror (errno)); zSlocalname = zbufcpy (s.nodename); #else /* ! HAVE_UNAME */ ulog (LOG_FATAL, "Don't know how to get local node name"); #endif /* ! HAVE_UNAME */ #endif /* ! HAVE_GETHOSTNAME */ } else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); /* We always set our file modes to exactly what we want. */ umask (0); /* Get the login name, making sure that it matches the uid. Many systems truncate the getlogin return value to 8 characters, but keep the full name in the password file, so we prefer the name in the password file. */ z = getenv ("LOGNAME"); if (z == NULL) z = getenv ("USER"); if (z == NULL) z = getlogin (); if (z == NULL) q = NULL; else { q = getpwnam (z); if (q != NULL) z = q->pw_name; } if (q == NULL || q->pw_uid != getuid ()) { q = getpwuid (getuid ()); if (q == NULL) z = NULL; else z = q->pw_name; } if (z != NULL) zSlogin = zbufcpy (z); /* On some old systems, an suid program run by root is started with an euid of 0. If this happens, we look up the uid we should have and set ourselves to it manually. This means that on such a system root will not be able to uucp or uux files that are not readable by uucp. */ if ((iflags & INIT_SUID) != 0 && geteuid () == 0) { q = getpwnam (OWNER); if (q != NULL) setuid (q->pw_uid); } if ((iflags & INIT_GETCWD) != 0) { const char *zenv; struct stat senv, sdot; /* Get the current working directory. We have to get it now, since we're about to do a chdir. We use PWD if it's defined and if it really names the working directory, since if it's not the same as whatever getcwd returns it's probably more appropriate. */ zenv = getenv ("PWD"); if (zenv != NULL && stat ((char *) zenv, &senv) == 0 && stat ((char *) ".", &sdot) == 0 && senv.st_ino == sdot.st_ino && senv.st_dev == sdot.st_dev) zScwd = zbufcpy (zenv); else { #if HAVE_GETCWD { size_t c; c = 128; while (TRUE) { zScwd = (char *) xmalloc (c); if (getcwd (zScwd, c) != NULL) break; xfree ((pointer) zScwd); zScwd = NULL; if (errno != ERANGE) break; c <<= 1; } } #endif /* HAVE_GETCWD */ #if HAVE_GETWD zScwd = (char *) xmalloc (MAXPATHLEN); if (getwd (zScwd) == NULL) { xfree ((pointer) zScwd); zScwd = NULL; } #endif /* HAVE_GETWD */ if (zScwd != NULL) zScwd = (char *) xrealloc ((pointer) zScwd, strlen (zScwd) + 1); } } if ((iflags & INIT_NOCHDIR) == 0) { /* Connect to the spool directory, and create it if it doesn't exist. */ if (chdir (zSspooldir) < 0) { if (errno == ENOENT && mkdir ((char *) zSspooldir, IDIRECTORY_MODE) < 0) ulog (LOG_FATAL, "mkdir (%s): %s", zSspooldir, strerror (errno)); if (chdir (zSspooldir) < 0) ulog (LOG_FATAL, "chdir (%s): %s", zSspooldir, strerror (errno)); } } } /* Exit the program. */ void usysdep_exit (fsuccess) boolean fsuccess; { exit (fsuccess ? EXIT_SUCCESS : EXIT_FAILURE); } /* This is called when a non-standard configuration file is used, to make sure the program doesn't hand out privileged file access. This means that to test non-standard configuration files, you should be logged in as uucp. This is called before usysdep_initialize. It ensures that someone can't simply use an alternate configuration file to steal UUCP transfers from other systems. This will still permit people to set up their own configuration file and pretend to be whatever system they choose. The only real security is to use a high level of protection on the modem ports. */ /*ARGSUSED*/ boolean fsysdep_other_config (z) const char *z ATTRIBUTE_UNUSED; { (void) setuid (getuid ()); (void) setgid (getgid ()); return TRUE; } /* Get the node name to use if it was not specified in the configuration file. */ const char * zsysdep_localname () { return zSlocalname; } /* Get the login name. We actually get the login name in usysdep_initialize, because after that we may switch away from the real uid. */ const char * zsysdep_login_name () { if (zSlogin == NULL) ulog (LOG_FATAL, "Can't get login name"); return zSlogin; } uucp-1.07/unix/isdir.c0000664000076400007640000000042607665321761010370 /* isdir.c See whether a file exists and is a directory. */ #include "uucp.h" #include "system.h" #include "sysdep.h" boolean fsysdep_directory (z) const char *z; { struct stat s; if (stat ((char *) z, &s) < 0) return FALSE; return S_ISDIR (s.st_mode); } uucp-1.07/unix/isfork.c0000664000076400007640000000047507665321761010557 /* isfork.c Retry fork several times before giving up. */ #include "uucp.h" #include "sysdep.h" #include pid_t ixsfork () { int i; pid_t iret; for (i = 0; i < 10; i++) { iret = fork (); if (iret >= 0 || errno != EAGAIN) return iret; sleep (5); } return iret; } uucp-1.07/unix/iswait.c0000664000076400007640000001116007665321761010553 /* iswait.c Wait for a process to finish. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include #if HAVE_SYS_WAIT_H #include #endif /* We use a typedef wait_status for wait (waitpid, wait4) to put results into. We define the POSIX examination functions we need if they are not already defined (if they aren't defined, I assume that we have a standard wait status). */ #if HAVE_UNION_WAIT typedef union wait wait_status; #ifndef WIFEXITED #define WIFEXITED(u) ((u).w_termsig == 0) #endif #ifndef WEXITSTATUS #define WEXITSTATUS(u) ((u).w_retcode) #endif #ifndef WTERMSIG #define WTERMSIG(u) ((u).w_termsig) #endif #else /* ! HAVE_UNION_WAIT */ typedef int wait_status; #ifndef WIFEXITED #define WIFEXITED(i) (((i) & 0xff) == 0) #endif #ifndef WEXITSTATUS #define WEXITSTATUS(i) (((i) >> 8) & 0xff) #endif #ifndef WTERMSIG #define WTERMSIG(i) ((i) & 0x7f) #endif #endif /* ! HAVE_UNION_WAIT */ /* Wait for a particular process to finish. The ipid argument should be pid_t, but then we couldn't have a prototype. If the zreport argument is not NULL, then a wait error will be logged, and if the exit status is non-zero it will be logged with zreport as the header of the log message. If the zreport argument is NULL, no errors will be logged. This function returns the exit status if the process exited normally, or -1 on error or if the process was killed by a signal (I don't just always return the exit status because then the calling code would have to prepared to handle union wait status vs. int status, and none of the callers care which signal killed the program anyhow). This functions keeps waiting until the process finished, even if it is interrupted by a signal. I think this is right for all uses. The controversial one would be when called from uuxqt to wait for a requested process. Hitting uuxqt with SIGKILL will approximate the actions taken if we return from here with an error anyhow. If we do get a signal, we call ulog with a NULL argument to get it in the log file at about the right time. */ int ixswait (ipid, zreport) unsigned long ipid; const char *zreport; { wait_status istat; #if HAVE_WAITPID while (waitpid ((pid_t) ipid, (pointer) &istat, 0) < 0) { if (errno != EINTR) { if (zreport != NULL) ulog (LOG_ERROR, "waitpid: %s", strerror (errno)); return -1; } ulog (LOG_ERROR, (const char *) NULL); } #else /* ! HAVE_WAITPID */ #if HAVE_WAIT4 while (wait4 ((pid_t) ipid, (pointer) &istat, 0, (struct rusage *) NULL) < 0) { if (errno != EINTR) { if (zreport != NULL) ulog (LOG_ERROR, "wait4: %s", strerror (errno)); return -1; } ulog (LOG_ERROR, (const char *) NULL); } #else /* ! HAVE_WAIT4 */ pid_t igot; /* We could theoretically get the wrong child here if we're in some kind of weird pipeline, so we don't give any error messages for it. */ while ((igot = wait ((pointer) &istat)) != (pid_t) ipid) { if (igot < 0) { if (errno != EINTR) { if (zreport != NULL) ulog (LOG_ERROR, "wait: %s", strerror (errno)); return -1; } ulog (LOG_ERROR, (const char *) NULL); } } #endif /* ! HAVE_WAIT4 */ #endif /* ! HAVE_WAITPID */ DEBUG_MESSAGE2 (DEBUG_EXECUTE, "%s %d", WIFEXITED (istat) ? "Exit status" : "Signal", WIFEXITED (istat) ? WEXITSTATUS (istat) : WTERMSIG (istat)); if (WIFEXITED (istat) && WEXITSTATUS (istat) == 0) return 0; if (zreport != NULL) { if (! WIFEXITED (istat)) ulog (LOG_ERROR, "%s: Got signal %d", zreport, WTERMSIG (istat)); else ulog (LOG_ERROR, "%s: Exit status %d", zreport, WEXITSTATUS (istat)); } if (WIFEXITED (istat)) return WEXITSTATUS (istat); else return -1; } uucp-1.07/unix/jobid.c0000664000076400007640000000754007665321761010351 /* jobid.c Convert file names to jobids and vice versa. Copyright (C) 1991, 1992, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uuconf.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" /* Translate a file name and an associated system into a job id. These job ids are used by uustat. */ char * zsfile_to_jobid (qsys, zfile, bgrade) const struct uuconf_system *qsys; const char *zfile; int bgrade ATTRIBUTE_UNUSED; { size_t clen; char *zret; clen = strlen (qsys->uuconf_zname); #if ! SPOOLDIR_TAYLOR /* We use the system name attached to the grade and sequence number. This won't work correctly if the file name was actually created by some other version of uucp that uses a different length for the sequence number. Too bad. */ zret = zbufalc (clen + CSEQLEN + 2); memcpy (zret, qsys->uuconf_zname, clen); zret[clen] = bgrade; memcpy (zret + clen + 1, zfile + strlen (zfile) - CSEQLEN, CSEQLEN + 1); #else /* We use the system name followed by a dot, the grade, and the sequence number. In this case, the sequence number is a long string. */ { size_t cseqlen; /* zfile is SYS/C./C.gseq. */ zfile = strrchr (zfile, '/'); #if DEBUG > 0 if (zfile == NULL || zfile[1] != 'C' || zfile[2] != '.' || zfile[3] == '\0') ulog (LOG_FATAL, "zsfile_to_jobid: Can't happen"); #endif /* Make zfile point at .gseq. */ zfile += 2; cseqlen = strlen (zfile); zret = zbufalc (clen + cseqlen + 1); memcpy (zret, qsys->uuconf_zname, clen); memcpy (zret + clen, zfile, cseqlen + 1); } #endif return zret; } /* Turn a job id back into a file name. */ char * zsjobid_to_file (zid, pzsystem, pbgrade) const char *zid; char **pzsystem; char *pbgrade; { #if ! SPOOLDIR_TAYLOR size_t clen; const char *zend; char *zsys; char abname[CSEQLEN + 11]; char *zret; clen = strlen (zid); if (clen <= CSEQLEN) { ulog (LOG_ERROR, "%s: Bad job id", zid); return NULL; } zend = zid + clen - CSEQLEN - 1; zsys = zbufalc (clen - CSEQLEN); memcpy (zsys, zid, clen - CSEQLEN - 1); zsys[clen - CSEQLEN - 1] = '\0'; /* This must correspond to zsfile_name. */ sprintf (abname, "C.%.7s%s", zsys, zend); zret = zsfind_file (abname, zsys, *zend); if (zret != NULL && pzsystem != NULL) *pzsystem = zsys; else ubuffree (zsys); if (pbgrade != NULL) *pbgrade = *zend; return zret; #else /* SPOOLDIR_TAYLOR */ char *zdot; size_t csyslen; char *zsys; char ab[15]; char *zret; zdot = strrchr (zid, '.'); if (zdot == NULL) { ulog (LOG_ERROR, "%s: Bad job id", zid); return NULL; } csyslen = zdot - zid; zsys = zbufalc (csyslen + 1); memcpy (zsys, zid, csyslen); zsys[csyslen] = '\0'; ab[0] = 'C'; strcpy (ab + 1, zdot); zret = zsfind_file (ab, zsys, zdot[1]); if (zret != NULL && pzsystem != NULL) *pzsystem = zsys; else ubuffree (zsys); if (pbgrade != NULL) *pbgrade = zdot[1]; return zret; #endif /* SPOOLDIR_TAYLOR */ } uucp-1.07/unix/lcksys.c0000664000076400007640000000211107665321761010557 /* lcksys.c Lock and unlock a remote system. */ #include "uucp.h" #include "uudefs.h" #include "uuconf.h" #include "sysdep.h" #include "system.h" /* Do the actual lock or unlock. */ static boolean fslock_sys P((boolean, const char *)); static boolean fslock_sys (flock, zname) boolean flock; const char *zname; { size_t clen; char *z; boolean fret; clen = strlen (zname); #if ! HAVE_LONG_FILE_NAMES if (clen > 8) clen = 8; #endif z = zbufalc (sizeof "LCK.." + clen); memcpy (z, "LCK..", sizeof "LCK.." - 1); memcpy (z + sizeof "LCK.." - 1, zname, clen); z[sizeof "LCK.." - 1 + clen] = '\0'; if (flock) fret = fsdo_lock (z, FALSE, (boolean *) NULL); else fret = fsdo_unlock (z, FALSE); ubuffree (z); return fret; } /* Lock a remote system. */ boolean fsysdep_lock_system (qsys) const struct uuconf_system *qsys; { return fslock_sys (TRUE, qsys->uuconf_zname); } /* Unlock a remote system. */ boolean fsysdep_unlock_system (qsys) const struct uuconf_system *qsys; { return fslock_sys (FALSE, qsys->uuconf_zname); } uucp-1.07/unix/link.c0000664000076400007640000000121607665321761010211 /* link.c Link two files. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include boolean fsysdep_link (zfrom, zto, pfworked) const char *zfrom; const char *zto; boolean *pfworked; { *pfworked = FALSE; if (link (zfrom, zto) == 0) { *pfworked = TRUE; return TRUE; } if (errno == ENOENT) { if (! fsysdep_make_dirs (zto, TRUE)) return FALSE; if (link (zfrom, zto) == 0) { *pfworked = TRUE; return TRUE; } } if (errno == EXDEV) return TRUE; ulog (LOG_ERROR, "link (%s, %s): %s", zfrom, zto, strerror (errno)); return FALSE; } uucp-1.07/unix/locfil.c0000664000076400007640000000442507665321761010531 /* locfil.c Expand a file name on the local system. Copyright (C) 1991, 1992, 1993 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include #if GETPWNAM_DECLARATION_OK #ifndef getpwnam extern struct passwd *getpwnam (); #endif #endif /* Turn a file name into an absolute path, by doing tilde expansion and moving any other type of file into the public directory. */ char * zsysdep_local_file (zfile, zpubdir, pfbadname) const char *zfile; const char *zpubdir; boolean *pfbadname; { const char *zdir; if (pfbadname != NULL) *pfbadname = FALSE; if (*zfile == '/') return zbufcpy (zfile); if (*zfile != '~') zdir = zpubdir; else { if (zfile[1] == '\0') return zbufcpy (zpubdir); if (zfile[1] == '/') { zdir = zpubdir; zfile += 2; } else { size_t cuserlen; char *zcopy; struct passwd *q; ++zfile; cuserlen = strcspn ((char *) zfile, "/"); zcopy = zbufalc (cuserlen + 1); memcpy (zcopy, zfile, cuserlen); zcopy[cuserlen] = '\0'; q = getpwnam (zcopy); if (q == NULL) { ulog (LOG_ERROR, "User %s not found", zcopy); ubuffree (zcopy); if (pfbadname != NULL) *pfbadname = TRUE; return NULL; } ubuffree (zcopy); if (zfile[cuserlen] == '\0') return zbufcpy (q->pw_dir); zdir = q->pw_dir; zfile += cuserlen + 1; } } return zsysdep_in_dir (zdir, zfile); } uucp-1.07/unix/lock.c0000664000076400007640000004217107665321761010211 /* lock.c Lock and unlock a file name. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char lock_rcsid[] = "$Id: lock.c,v 1.23 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "sysdep.h" #include "system.h" #include #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #if TM_IN_SYS_TIME #include #else #include #endif #if HAVE_QNX_LOCKFILES #include #include #include #include #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef localtime extern struct tm *localtime (); #endif #if HAVE_QNX_LOCKFILES static boolean fsqnx_stale P((unsigned long ipid, unsigned long inme, unsigned long inid, boolean *pferr)); #endif /* Lock something. If the fspooldir argument is TRUE, the argument is a file name relative to the spool directory; otherwise the argument is a simple file name which should be created in the system lock directory (under HDB this is /etc/locks). */ boolean fsdo_lock (zlock, fspooldir, pferr) const char *zlock; boolean fspooldir; boolean *pferr; { char *zfree; const char *zpath, *zslash; size_t cslash; pid_t ime; char *ztempfile; char abtempfile[sizeof "TMP12345678901234567890"]; int o; #if HAVE_QNX_LOCKFILES nid_t inme; char ab[23]; char *zend; #else #if HAVE_V2_LOCKFILES int i; #else char ab[12]; #endif #endif int cwrote; const char *zerr; boolean fret; if (pferr != NULL) *pferr = TRUE; if (fspooldir) { zfree = NULL; zpath = zlock; } else { zfree = zsysdep_in_dir (zSlockdir, zlock); zpath = zfree; } ime = getpid (); #if HAVE_QNX_LOCKFILES inme = getnid (); #endif /* We do the actual lock by creating a file and then linking it to the final file name we want. This avoids race conditions due to one process checking the file before we have finished writing it, and also works even if we are somehow running as root. First, create the file in the right directory (we must create the file in the same directory since otherwise we might attempt a cross-device link). */ zslash = strrchr (zpath, '/'); if (zslash == NULL) cslash = 0; else cslash = zslash - zpath + 1; #if HAVE_QNX_LOCKFILES sprintf (abtempfile, "TMP%010lx%010lx", (unsigned long) ime, (unsigned long) inme); #else sprintf (abtempfile, "TMP%010lx", (unsigned long) ime); #endif ztempfile = zbufalc (cslash + sizeof abtempfile); memcpy (ztempfile, zpath, cslash); memcpy (ztempfile + cslash, abtempfile, sizeof abtempfile); o = creat (ztempfile, IPUBLIC_FILE_MODE); if (o < 0) { if (errno == ENOENT) { if (! fsysdep_make_dirs (ztempfile, FALSE)) { ubuffree (zfree); ubuffree (ztempfile); return FALSE; } o = creat (ztempfile, IPUBLIC_FILE_MODE); } if (o < 0) { ulog (LOG_ERROR, "creat (%s): %s", ztempfile, strerror (errno)); ubuffree (zfree); ubuffree (ztempfile); return FALSE; } } #if HAVE_QNX_LOCKFILES sprintf (ab, "%10ld %10ld\n", (long) ime, (long) inme); cwrote = write (o, ab, strlen (ab)); #else #if HAVE_V2_LOCKFILES i = (int) ime; cwrote = write (o, &i, sizeof i); #else sprintf (ab, "%10ld\n", (long) ime); cwrote = write (o, ab, strlen (ab)); #endif #endif zerr = NULL; if (cwrote < 0) zerr = "write"; if (close (o) < 0) zerr = "close"; if (zerr != NULL) { ulog (LOG_ERROR, "%s (%s): %s", zerr, ztempfile, strerror (errno)); (void) remove (ztempfile); ubuffree (zfree); ubuffree (ztempfile); return FALSE; } /* Now try to link the file we just created to the lock file that we want. If it fails, try reading the existing file to make sure the process that created it still exists. We do this in a loop to make it easy to retry if the old locking process no longer exists. */ fret = TRUE; if (pferr != NULL) *pferr = FALSE; o = -1; zerr = NULL; while (link (ztempfile, zpath) != 0) { int cgot; pid_t ipid; boolean freadonly; struct stat st; char abtime[sizeof "1991-12-31 12:00:00"]; #if HAVE_QNX_LOCKFILES nid_t inid; #endif fret = FALSE; if (errno != EEXIST) { ulog (LOG_ERROR, "link (%s, %s): %s", ztempfile, zpath, strerror (errno)); if (pferr != NULL) *pferr = TRUE; break; } freadonly = FALSE; o = open ((char *) zpath, O_RDWR | O_NOCTTY, 0); if (o < 0) { if (errno == EACCES) { freadonly = TRUE; o = open ((char *) zpath, O_RDONLY, 0); } if (o < 0) { if (errno == ENOENT) { /* The file was presumably removed between the link and the open. Try the link again. */ fret = TRUE; continue; } zerr = "open"; break; } } /* The race starts here. See below for a discussion. */ #if HAVE_V2_LOCKFILES cgot = read (o, &i, sizeof i); #else cgot = read (o, ab, sizeof ab - 1); #endif if (cgot < 0) { zerr = "read"; break; } #if DEBUG > 0 #if HAVE_V2_LOCKFILES { char ab[10]; if (read (o, ab, sizeof ab) > 4 && isdigit (BUCHAR (ab[0]))) ulog (LOG_ERROR, "Lock file %s may be HDB format; check LOCKFILES in policy.h", zpath); } #else if (cgot == 4) ulog (LOG_ERROR, "Lock file %s may be V2 format; check LOCKFILES in policy.h", zpath); #endif #endif /* DEBUG > 0 */ #if HAVE_QNX_LOCKFILES ab[cgot] = '\0'; ipid = (pid_t) strtol (ab, &zend, 10); inid = (nid_t) strtol (zend, (char **) NULL, 10); #else #if HAVE_V2_LOCKFILES ipid = (pid_t) i; #else ab[cgot] = '\0'; ipid = (pid_t) strtol (ab, (char **) NULL, 10); #endif #endif /* On NFS, the link might have actually succeeded even though we got a failure return. This can happen if the original acknowledgement was lost or delayed and the operation was retried. In this case the pid will be our own. This introduces a rather improbable race condition: if a stale lock was left with our process ID in it, and another process just did the kill, below, but has not yet changed the lock file to hold its own process ID, we could start up and make it all the way to here and think we have the lock. I'm not going to worry about this possibility. */ if (ipid == ime) { #if HAVE_QNX_LOCKFILES if (inid == inme) #endif { fret = TRUE; break; } } /* If the lock file is empty (cgot == 0), we assume that it is stale. This can happen if the system crashed after the lock file was created but before the process ID was written out. */ if (cgot > 0) { #if HAVE_QNX_LOCKFILES if (! fsqnx_stale ((unsigned long) ipid, (unsigned long) inme, (unsigned long) inid, pferr)) break; #else /* If the process still exists, we will get EPERM rather than ESRCH. We then return FALSE to indicate that we cannot make the lock. */ if (kill (ipid, 0) == 0 || errno == EPERM) break; #endif } if (fstat (o, &st) < 0) strcpy (abtime, "unknown"); else { time_t itm; struct tm *q; itm = (time_t) st.st_mtime; q = localtime (&itm); sprintf (abtime, "%04d-%02d-%02d %02d:%02d:%02d", q->tm_year + 1900, q->tm_mon + 1, q->tm_mday, q->tm_hour, q->tm_min, q->tm_sec); } #if HAVE_QNX_LOCKFILES ulog (LOG_ERROR, "Stale lock %s held by process %ld on node %ld created %s", zpath, (long) ipid, (long) inid, abtime); #else ulog (LOG_ERROR, "Stale lock %s held by process %ld created %s", zpath, (long) ipid, abtime); #endif /* This is a stale lock, created by a process that no longer exists. Now we could remove the file (and, if the file mode disallows writing, that's what we have to do), but we try to avoid doing so since it causes a race condition. If we remove the file, and are interrupted any time after we do the read until we do the remove, another process could get in, open the file, find that it was a stale lock, remove the file and create a new one. When we regained control we would remove the file the other process just created. These files are being generated partially for the benefit of cu, and it would be nice to avoid the race however cu avoids it, so that the programs remain compatible. Unfortunately, nobody seems to know how cu avoids the race, or even if it tries to avoid it at all. There are a few ways to avoid the race. We could use kernel locking primitives, but they may not be available. We could link to a special file name, but if that file were left lying around then no stale lock could ever be broken (Henry Spencer would think this was a good thing). Instead I've implemented the following procedure: seek to the start of the file, write our pid into it, sleep for five seconds, and then make sure our pid is still there. Anybody who checks the file while we're asleep will find our pid there and fail the lock. The only race will come from another process which has done the read by the time we do our write. That process will then have five seconds to do its own write. When we wake up, we'll notice that our pid is no longer in the file, and retry the lock from the beginning. This relies on the atomicity of write(2). If it possible for the writes of two processes to be interleaved, the two processes could livelock. POSIX unfortunately leaves this case explicitly undefined; however, given that the write is of less than a disk block, it's difficult to imagine an interleave occurring. Note that this is still a race. If it takes the second process more than five seconds to do the kill, the lseek, and the write, both processes will think they have the lock. Perhaps the length of time to sleep should be configurable. Even better, perhaps I should add a configuration option to use a permanent lock file, which eliminates any race and forces the installer to be aware of the existence of the permanent lock file. We stat the file after the sleep, to make sure some other program hasn't deleted it for us. */ if (freadonly) { (void) close (o); o = -1; if (remove (zpath) != 0) { zerr = "remove"; break; } fret = TRUE; continue; } if (lseek (o, (off_t) 0, SEEK_SET) != 0) { zerr = "lseek"; break; } #if HAVE_QNX_LOCKFILES sprintf (ab, "%10ld %10ld\n", (long) ime, (long) inme); cwrote = write (o, ab, strlen (ab)); #else #if HAVE_V2_LOCKFILES i = (int) ime; cwrote = write (o, &i, sizeof i); #else sprintf (ab, "%10ld\n", (long) ime); cwrote = write (o, ab, strlen (ab)); #endif #endif if (cwrote < 0) { zerr = "write"; break; } (void) sleep (5); if (lseek (o, (off_t) 0, SEEK_SET) != 0) { zerr = "lseek"; break; } #if HAVE_V2_LOCKFILES cgot = read (o, &i, sizeof i); #else cgot = read (o, ab, sizeof ab - 1); #endif if (cgot < 0) { zerr = "read"; break; } #if HAVE_QNX_LOCKFILES ab[cgot] = '\0'; ipid = (pid_t) strtol (ab, &zend, 10); inid = (nid_t) strtol (zend, (char **) NULL, 10); #else #if HAVE_V2_LOCKFILES ipid = (pid_t) i; #else ab[cgot] = '\0'; ipid = (pid_t) strtol (ab, (char **) NULL, 10); #endif #endif if (ipid == ime) { #if HAVE_QNX_LOCKFILES if (inid == inme) #endif { struct stat sfile, sdescriptor; /* It looks like we have the lock. Do the final stat check. */ if (stat ((char *) zpath, &sfile) < 0) { if (errno != ENOENT) { zerr = "stat"; break; } /* Loop around and try again. */ } else { if (fstat (o, &sdescriptor) < 0) { zerr = "fstat"; break; } if (sfile.st_ino == sdescriptor.st_ino && sfile.st_dev == sdescriptor.st_dev) { /* Close the file before assuming we've succeeded to pick up any trailing errors. */ if (close (o) < 0) { zerr = "close"; break; } o = -1; /* We have the lock. */ fret = TRUE; break; } } } } /* Loop around and try the lock again. We keep doing this until the lock file holds a pid that exists. */ (void) close (o); o = -1; fret = TRUE; } if (zerr != NULL) { ulog (LOG_ERROR, "%s (%s): %s", zerr, zpath, strerror (errno)); if (pferr != NULL) *pferr = TRUE; } if (o >= 0) (void) close (o); ubuffree (zfree); /* It would be nice if we could leave the temporary file around for future calls, but considering that we create lock files in various different directories it's probably more trouble than it's worth. */ if (remove (ztempfile) != 0) ulog (LOG_ERROR, "remove (%s): %s", ztempfile, strerror (errno)); ubuffree (ztempfile); return fret; } /* Unlock something. The fspooldir argument is as in fsdo_lock. */ boolean fsdo_unlock (zlock, fspooldir) const char *zlock; boolean fspooldir; { char *zfree; const char *zpath; if (fspooldir) { zfree = NULL; zpath = zlock; } else { zfree = zsysdep_in_dir (zSlockdir, zlock); zpath = zfree; } if (remove (zpath) == 0 || errno == ENOENT) { ubuffree (zfree); return TRUE; } else { ulog (LOG_ERROR, "remove (%s): %s", zpath, strerror (errno)); ubuffree (zfree); return FALSE; } } #if HAVE_QNX_LOCKFILES /* Return TRUE if the lock is stale. */ static boolean fsqnx_stale (ipid, inme, inid, pferr) unsigned long ipid; unsigned long inme; unsigned long inid; boolean *pferr; { /* A virtual process ID. This virtual process ID, which will exist on the local node, will represent the process ID of the process manager process (Proc) on the remote node. */ pid_t ivid; /* The return value of the qnx_psinfo function. This is either a process ID which might or might not be the same as the process being looked for, or -1 to indicate no process found. */ pid_t ifound_pid; /* This holds the actual result of qnx_psinfo. We will ignore almost all the fields since we're just checking for existence. */ struct _psinfo spsdata; /* Establish connection with a remote process manager if necessary. */ if (inid != inme) { ivid = qnx_vc_attach (inid /* remote node ID */, PROC_PID /* pid of process manager */, 1000 /* initial buffer size */, 0 /* flags */); if (ivid < 0) { ulog (LOG_ERROR, "qnx_vc_attach (%lu, PROC_PID): %s", inid, strerror (errno)); if (pferr != NULL) *pferr = TRUE; return FALSE; } } else { /* Use the local pid of the local process manager. */ ivid = PROC_PID; } /* Request the process information. */ ifound_pid = qnx_psinfo (ivid /* process manager handling request */, ipid /* get info on this process */, &spsdata /* put info in this struct */, 0 /* unused */, (struct _seginfo *) NULL /* unused */); /* Deallocate the virtual connection before continuing. */ { int isaved_errno = errno; if (qnx_vc_detach (ivid) < 0) ulog (LOG_ERROR, "qnx_vd_detach (%ld): %s", (long) ivid, strerror (errno)); errno = isaved_errno; } /* If the returned pid matches then the process still holds the lock. */ if ((ifound_pid == ipid) && (spsdata.pid == ipid)) return FALSE; /* If the returned pid is positive and doesn't match, then the process doesn't exist and the lock is stale. Continue. */ /* If the returned pid is negative (-1) and errno is EINVAL (or ESRCH in older versions of QNX), then the process doesn't exist and the lock is stale. Continue. */ /* Check for impossible errors. */ if ((ifound_pid < 0) && (errno != ESRCH) && (errno != EINVAL)) { ulog (LOG_ERROR, "qnx_psinfo (%ld, %ld): %s", (long) ivid, (long) ipid, strerror (errno)); /* Since we don't know what the hell this means, and we don't want our system to freeze, we treat this case as a stale lock. Continue on. */ } return TRUE; } #endif /* HAVE_QNX_LOCKFILES */ uucp-1.07/unix/loctim.c0000664000076400007640000000060207665321761010541 /* loctim.c Turn a time epoch into a struct tm. This is trivial on Unix. */ #include "uucp.h" #if TM_IN_SYS_TIME #include #else #include #endif #include "system.h" #ifndef localtime extern struct tm *localtime (); #endif void usysdep_localtime (itime, q) long itime; struct tm *q; { time_t i; i = (time_t) itime; *q = *localtime (&i); } uucp-1.07/unix/mail.c0000664000076400007640000000544507665321761010206 /* mail.c Send mail to a user. Copyright (C) 1992, 1993 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include #if HAVE_TIME_H #include #endif #ifndef ctime extern char *ctime (); #endif /* Mail a message to a user. */ boolean fsysdep_mail (zto, zsubject, cstrs, paz) const char *zto; const char *zsubject; int cstrs; const char **paz; { char **pazargs; char *zcopy, *ztok; size_t cargs, iarg; FILE *e; pid_t ipid; time_t itime; int i; /* Parse MAIL_PROGRAM into an array of arguments. */ zcopy = zbufcpy (MAIL_PROGRAM); cargs = 0; for (ztok = strtok (zcopy, " \t"); ztok != NULL; ztok = strtok ((char *) NULL, " \t")) ++cargs; pazargs = (char **) xmalloc ((cargs + 4) * sizeof (char *)); memcpy (zcopy, MAIL_PROGRAM, sizeof MAIL_PROGRAM); for (ztok = strtok (zcopy, " \t"), iarg = 0; ztok != NULL; ztok = strtok ((char *) NULL, " \t"), ++iarg) pazargs[iarg] = ztok; #if ! MAIL_PROGRAM_SUBJECT_BODY pazargs[iarg++] = (char *) "-s"; pazargs[iarg++] = (char *) zsubject; #endif #if ! MAIL_PROGRAM_TO_BODY pazargs[iarg++] = (char *) zto; #endif pazargs[iarg] = NULL; e = espopen ((const char **) pazargs, FALSE, &ipid); ubuffree (zcopy); xfree ((pointer) pazargs); if (e == NULL) { ulog (LOG_ERROR, "espopen (%s): %s", MAIL_PROGRAM, strerror (errno)); return FALSE; } #if MAIL_PROGRAM_TO_BODY fprintf (e, "To: %s\n", zto); #endif #if MAIL_PROGRAM_SUBJECT_BODY fprintf (e, "Subject: %s\n", zsubject); #endif #if MAIL_PROGRAM_TO_BODY || MAIL_PROGRAM_SUBJECT_BODY fprintf (e, "\n"); #endif (void) time (&itime); /* Remember that ctime includes a \n, so this skips a line. */ fprintf (e, "Message from UUCP on %s %s\n", zSlocalname, ctime (&itime)); for (i = 0; i < cstrs; i++) fputs (paz[i], e); (void) fclose (e); return ixswait ((unsigned long) ipid, MAIL_PROGRAM) == 0; } uucp-1.07/unix/mkdirs.c0000664000076400007640000000210507665321761010543 /* mkdirs.c Create any directories needed for a file name. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include boolean fsysdep_make_dirs (zfile, fpublic) const char *zfile; boolean fpublic; { char *zcopy, *z; int imode; zcopy = zbufcpy (zfile); if (fpublic) imode = IPUBLIC_DIRECTORY_MODE; else imode = IDIRECTORY_MODE; for (z = zcopy; *z != '\0'; z++) { if (*z == '/' && z != zcopy) { /* Some versions of uuto will send a double slash. Some systems will fail to create a directory ending in a slash. */ if (z[-1] == '/') continue; *z = '\0'; if (mkdir (zcopy, imode) != 0) { int ierr; ierr = errno; if (ierr != EEXIST && ierr != EISDIR #ifdef EROFS && ierr != EROFS #endif && (ierr != EACCES || ! fsysdep_directory (zcopy))) { ulog (LOG_ERROR, "mkdir (%s): %s", zcopy, strerror (ierr)); ubuffree (zcopy); return FALSE; } } *z = '/'; } } ubuffree (zcopy); return TRUE; } uucp-1.07/unix/mode.c0000664000076400007640000000110007665321761010170 /* mode.c Get the Unix file mode of a file. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include unsigned int ixsysdep_file_mode (zfile) const char *zfile; { struct stat s; if (stat ((char *) zfile, &s) != 0) { ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); return 0; } #if S_IRWXU != 0700 #error Files modes need to be translated #endif /* We can't return 0, since that indicates an error. */ if ((s.st_mode & 0777) == 0) return 0400; return s.st_mode & 0777; } uucp-1.07/unix/move.c0000664000076400007640000001023707665321761010225 /* move.c Move a file. Copyright (C) 1991, 1992, 1993 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif /* Move (rename) a file from one name to another. This routine will optionally create necessary directories, and fpublic indicates whether the new directories should be publically accessible or not. If fcheck is true, it will try to determine whether the named user has write access to the new file. */ boolean fsysdep_move_file (zorig, zto, fmkdirs, fpublic, fcheck, zuser) const char *zorig; const char *zto; boolean fmkdirs; boolean fpublic; boolean fcheck; const char *zuser; { struct stat s; int o; DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, "fsysdep_move_file: Moving %s to %s", zorig, zto); /* Optionally make sure that zuser has write access on the directory. */ if (fcheck) { char *zcopy; char *zslash; zcopy = zbufcpy (zto); zslash = strrchr (zcopy, '/'); if (zslash == zcopy) zslash[1] = '\0'; else *zslash = '\0'; if (stat (zcopy, &s) != 0) { ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno)); ubuffree (zcopy); return FALSE; } if (! fsuser_access (&s, W_OK, zuser)) { ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); ubuffree (zcopy); return FALSE; } ubuffree (zcopy); /* A malicious user now has a few milliseconds to change a symbolic link to a directory uucp has write permission on but the user does not (the obvious choice being /usr/lib/uucp). The only certain method I can come up with to close this race is to fork an suid process which takes on the users identity and does the actual copy. This is sufficiently high overhead that I'm not going to do it. */ } /* We try to use rename to move the file. */ if (rename (zorig, zto) == 0) return TRUE; if (fmkdirs && errno == ENOENT) { if (! fsysdep_make_dirs (zto, fpublic)) return FALSE; if (rename (zorig, zto) == 0) return TRUE; } #if HAVE_RENAME /* On some systems the system call rename seems to fail for arbitrary reasons. To get around this, we always try to copy the file by hand if the rename failed. */ errno = EXDEV; #endif /* If we can't link across devices, we must copy the file by hand. */ if (errno != EXDEV) { ulog (LOG_ERROR, "rename (%s, %s): %s", zorig, zto, strerror (errno)); return FALSE; } /* Copy the file. */ if (stat ((char *) zorig, &s) < 0) { ulog (LOG_ERROR, "stat (%s): %s", zorig, strerror (errno)); return FALSE; } /* Make sure the file gets the right mode by creating it before we call fcopy_file. */ (void) remove (zto); o = creat ((char *) zto, s.st_mode); if (o < 0) { if (fmkdirs && errno == ENOENT) { if (! fsysdep_make_dirs (zto, fpublic)) return FALSE; o = creat ((char *) zto, s.st_mode); } if (o < 0) { ulog (LOG_ERROR, "creat (%s): %s", zto, strerror (errno)); return FALSE; } } (void) close (o); if (! fcopy_file (zorig, zto, fpublic, fmkdirs, FALSE)) return FALSE; if (remove (zorig) != 0) ulog (LOG_ERROR, "remove (%s): %s", zorig, strerror (errno)); return TRUE; } uucp-1.07/unix/opensr.c0000664000076400007640000001405107665321761010563 /* opensr.c Open files for sending and receiving. Copyright (C) 1991, 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "sysdep.h" #include #if HAVE_TIME_H #include #endif #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif #ifndef time extern time_t time (); #endif /* Open a file to send to another system, and return the mode and the size. */ openfile_t esysdep_open_send (qsys, zfile, fcheck, zuser) const struct uuconf_system *qsys ATTRIBUTE_UNUSED; const char *zfile; boolean fcheck; const char *zuser; { struct stat s; openfile_t e; int o; if (fsysdep_directory (zfile)) { ulog (LOG_ERROR, "%s: is a directory", zfile); return EFILECLOSED; } #if USE_STDIO e = fopen (zfile, BINREAD); if (e == NULL) { ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno)); return NULL; } o = fileno (e); #else e = open ((char *) zfile, O_RDONLY | O_NOCTTY, 0); if (e == -1) { ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno)); return -1; } o = e; #endif if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); (void) ffileclose (e); return EFILECLOSED; } if (fstat (o, &s) == -1) { ulog (LOG_ERROR, "fstat: %s", strerror (errno)); s.st_mode = 0666; } /* We have to recheck the file permission, although we probably checked it already, because otherwise there would be a window in which somebody could change the contents of a symbolic link to point to some file which was only readable by uucp. */ if (fcheck) { if (! fsuser_access (&s, R_OK, zuser)) { ulog (LOG_ERROR, "%s: %s", zfile, strerror (EACCES)); (void) ffileclose (e); return EFILECLOSED; } } return e; } /* Get a temporary file name to receive into. We use the ztemp argument to pick the file name, so that we restart the file if the transmission is aborted. */ char * zsysdep_receive_temp (qsys, zto, ztemp, frestart) const struct uuconf_system *qsys; const char *zto ATTRIBUTE_UNUSED; const char *ztemp; boolean frestart; { if (frestart && ztemp != NULL && *ztemp == 'D' && strcmp (ztemp, "D.0") != 0) return zsappend3 (".Temp", qsys->uuconf_zname, ztemp); else return zstemp_file (qsys); } /* The number of seconds in one week. We must cast to long for this to be calculated correctly on a machine with 16 bit ints. */ #define SECS_PER_WEEK ((long) 7 * (long) 24 * (long) 60 * (long) 60) /* Open a temporary file to receive into. This should, perhaps, check that we have write permission on the receiving directory, but it doesn't. */ openfile_t esysdep_open_receive (qsys, zto, ztemp, zreceive, pcrestart) const struct uuconf_system *qsys ATTRIBUTE_UNUSED; const char *zto ATTRIBUTE_UNUSED; const char *ztemp; const char *zreceive; long *pcrestart; { int o; openfile_t e; /* If we used the ztemp argument in zsysdep_receive_temp, above, then we will have a name consistent across conversations. In that case, we may have already received some portion of this file. */ o = -1; if (pcrestart != NULL) *pcrestart = -1; if (pcrestart != NULL && ztemp != NULL && *ztemp == 'D' && strcmp (ztemp, "D.0") != 0) { o = open ((char *) zreceive, O_WRONLY); if (o >= 0) { struct stat s; /* For safety, we insist on the file being less than 1 week old. This can still catch people, unfortunately. I don't know of any good solution to the problem of old files hanging around. If anybody has a file they want restarted, and they know about this issue, they can touch it to bring it up to date. */ if (fstat (o, &s) < 0 || s.st_mtime + SECS_PER_WEEK < time ((time_t *) NULL)) { (void) close (o); o = -1; } else { DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, "esysdep_open_receive: Reusing %s", zreceive); *pcrestart = (long) s.st_size; } } } if (o < 0) o = creat ((char *) zreceive, IPRIVATE_FILE_MODE); if (o < 0) { if (errno == ENOENT) { if (! fsysdep_make_dirs (zreceive, FALSE)) return EFILECLOSED; o = creat ((char *) zreceive, IPRIVATE_FILE_MODE); } if (o < 0) { ulog (LOG_ERROR, "creat (%s): %s", zreceive, strerror (errno)); return EFILECLOSED; } } if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); (void) close (o); (void) remove (zreceive); return EFILECLOSED; } #if USE_STDIO e = fdopen (o, (char *) BINWRITE); if (e == NULL) { ulog (LOG_ERROR, "fdopen (%s): %s", zreceive, strerror (errno)); (void) close (o); (void) remove (zreceive); return EFILECLOSED; } #else e = o; #endif return e; } uucp-1.07/unix/pause.c0000664000076400007640000000413007665321761010367 /* pause.c Pause for half a second. */ #include "uucp.h" #include "sysdep.h" #include "system.h" /* Pick a timing routine to use. I somewhat arbitrarily picked usleep above napms above poll above select above nap. The nap function is last because on different systems the argument has different meanings. */ #if HAVE_USLEEP || HAVE_NAPMS || HAVE_POLL || HAVE_SELECT #undef HAVE_NAP #define HAVE_NAP 0 #endif #if HAVE_USLEEP || HAVE_NAPMS || HAVE_POLL #undef HAVE_SELECT #define HAVE_SELECT 0 #endif #if HAVE_USLEEP || HAVE_NAPMS #undef HAVE_POLL #define HAVE_POLL 0 #endif #if HAVE_USLEEP #undef HAVE_NAPMS #define HAVE_NAPMS 0 #endif #if HAVE_SELECT #if HAVE_SYS_TIME_H #include #endif #if HAVE_SYS_SELECT_H #include #endif #endif #if HAVE_POLL #if HAVE_STROPTS_H #include #endif #if HAVE_POLL_H #include #endif #if ! HAVE_STROPTS_H && ! HAVE_POLL_H /* We need a definition for struct pollfd, although it doesn't matter what it contains. */ struct pollfd { int idummy; }; #endif /* ! HAVE_STROPTS_H && ! HAVE_POLL_H */ #endif /* HAVE_POLL */ #if HAVE_TIME_H #if ! HAVE_SYS_TIME_H || ! HAVE_SELECT || TIME_WITH_SYS_TIME #include #endif #endif void usysdep_pause () { #if HAVE_NAPMS napms (500); #endif /* HAVE_NAPMS */ #if HAVE_NAP #if HAVE_HUNDREDTHS_NAP nap (50L); #else nap (500L); #endif /* ! HAVE_HUNDREDTHS_NAP */ #endif /* HAVE_NAP */ #if HAVE_USLEEP usleep (500 * (long) 1000); #endif /* HAVE_USLEEP */ #if HAVE_POLL struct pollfd sdummy; /* We need to pass an unused pollfd structure because poll checks the address before checking the number of elements. */ memset (&sdummy, 0, sizeof sdummy); poll (&sdummy, 0, 500); #endif /* HAVE_POLL */ #if HAVE_SELECT struct timeval s; s.tv_sec = 0; s.tv_usec = 500 * (long) 1000; select (0, (pointer) NULL, (pointer) NULL, (pointer) NULL, &s); #endif /* HAVE_SELECT */ #if ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP #if ! HAVE_SELECT && ! HAVE_POLL sleep (1); #endif /* ! HAVE_SELECT && ! HAVE_POLL */ #endif /* ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP */ } uucp-1.07/unix/picksb.c0000664000076400007640000001215607665321761010534 /* picksb.c System dependent routines for uupick. Copyright (C) 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char picksb_rcsid[] = "$Id: picksb.c,v 1.13 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "system.h" #include "sysdep.h" #include #include #if HAVE_OPENDIR #if HAVE_DIRENT_H #include #else /* ! HAVE_DIRENT_H */ #include #define dirent direct #endif /* ! HAVE_DIRENT_H */ #endif /* HAVE_OPENDIR */ #if GETPWUID_DECLARATION_OK #ifndef getpwuid extern struct passwd *getpwuid (); #endif #endif /* Local variables. */ /* Directory of ~/receive/USER. */ static DIR *qStopdir; /* Name of ~/receive/USER. */ static char *zStopdir; /* Directory of ~/receive/USER/SYSTEM. */ static DIR *qSsysdir; /* Name of system. */ static char *zSsysdir; /* Prepare to get a list of all the file to uupick for this user. */ /*ARGSUSED*/ boolean fsysdep_uupick_init (zsystem, zpubdir) const char *zsystem ATTRIBUTE_UNUSED; const char *zpubdir; { const char *zuser; zuser = zsysdep_login_name (); zStopdir = (char *) xmalloc (strlen (zpubdir) + sizeof "/receive/" + strlen (zuser)); sprintf (zStopdir, "%s/receive/%s", zpubdir, zuser); qStopdir = opendir (zStopdir); if (qStopdir == NULL && errno != ENOENT) { ulog (LOG_ERROR, "opendir (%s): %s", zStopdir, strerror (errno)); return FALSE; } qSsysdir = NULL; return TRUE; } /* Return the next file from the uupick directories. */ /*ARGSUSED*/ char * zsysdep_uupick (zsysarg, zpubdir, pzfrom, pzfull) const char *zsysarg; const char *zpubdir ATTRIBUTE_UNUSED; char **pzfrom; char **pzfull; { struct dirent *qentry; while (TRUE) { while (qSsysdir == NULL) { const char *zsystem; char *zdir; if (qStopdir == NULL) return NULL; if (zsysarg != NULL) { closedir (qStopdir); qStopdir = NULL; zsystem = zsysarg; } else { do { qentry = readdir (qStopdir); if (qentry == NULL) { closedir (qStopdir); qStopdir = NULL; return NULL; } } while (strcmp (qentry->d_name, ".") == 0 || strcmp (qentry->d_name, "..") == 0); zsystem = qentry->d_name; } zdir = zbufalc (strlen (zStopdir) + strlen (zsystem) + sizeof "/"); sprintf (zdir, "%s/%s", zStopdir, zsystem); qSsysdir = opendir (zdir); if (qSsysdir == NULL) { if (errno != ENOENT && errno != ENOTDIR) ulog (LOG_ERROR, "opendir (%s): %s", zdir, strerror (errno)); } else { ubuffree (zSsysdir); zSsysdir = zbufcpy (zsystem); } ubuffree (zdir); } qentry = readdir (qSsysdir); if (qentry == NULL) { closedir (qSsysdir); qSsysdir = NULL; continue; } if (strcmp (qentry->d_name, ".") == 0 || strcmp (qentry->d_name, "..") == 0) continue; *pzfrom = zbufcpy (zSsysdir); *pzfull = zsappend3 (zStopdir, zSsysdir, qentry->d_name); return zbufcpy (qentry->d_name); } } /*ARGSUSED*/ boolean fsysdep_uupick_free (zsystem, zpubdir) const char *zsystem ATTRIBUTE_UNUSED; const char *zpubdir ATTRIBUTE_UNUSED; { xfree ((pointer) zStopdir); if (qStopdir != NULL) { closedir (qStopdir); qStopdir = NULL; } ubuffree (zSsysdir); zSsysdir = NULL; if (qSsysdir != NULL) { closedir (qSsysdir); qSsysdir = NULL; } return TRUE; } /* Expand a local file name for uupick. */ char * zsysdep_uupick_local_file (zfile, pfbadname) const char *zfile; boolean *pfbadname; { struct passwd *q; if (pfbadname != NULL) *pfbadname = FALSE; /* If this does not start with a simple ~, pass it to zsysdep_local_file_cwd; as it happens, zsysdep_local_file_cwd only uses the zpubdir argument if the file starts with a simple ~, so it doesn't really matter what we pass for zpubdir. */ if (zfile[0] != '~' || (zfile[1] != '/' && zfile[1] != '\0')) return zsysdep_local_file_cwd (zfile, (const char *) NULL, pfbadname); q = getpwuid (getuid ()); if (q == NULL) { ulog (LOG_ERROR, "Can't get home directory"); return NULL; } if (zfile[1] == '\0') return zbufcpy (q->pw_dir); return zsysdep_in_dir (q->pw_dir, zfile + 2); } uucp-1.07/unix/pipe.c0000664000076400007640000001634207665321761010217 /* pipe.c The pipe port communication routines for Unix. Contributed by Marc Boucher . Copyright (C) 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char pipe_rcsid[] = "$Id: pipe.c,v 1.10 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "conn.h" #include "sysdep.h" #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif /* Local functions. */ static void uspipe_free P((struct sconnection *qconn)); static boolean fspipe_open P((struct sconnection *qconn, long ibaud, boolean fwait, boolean fuser)); static boolean fspipe_close P((struct sconnection *qconn, pointer puuconf, struct uuconf_dialer *qdialer, boolean fsuccess)); static boolean fspipe_dial P((struct sconnection *qconn, pointer puuconf, const struct uuconf_system *qsys, const char *zphone, struct uuconf_dialer *qdialer, enum tdialerfound *ptdialer)); /* The command table for standard input ports. */ static const struct sconncmds spipecmds = { uspipe_free, NULL, /* pflock */ NULL, /* pfunlock */ fspipe_open, fspipe_close, fspipe_dial, fsdouble_read, fsdouble_write, fsysdep_conn_io, NULL, /* pfbreak */ NULL, /* pfset */ NULL, /* pfcarrier */ fsdouble_chat, NULL /* pibaud */ }; /* Initialize a pipe connection. */ boolean fsysdep_pipe_init (qconn) struct sconnection *qconn; { struct ssysdep_conn *q; q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn)); q->o = -1; q->ord = -1; q->owr = -1; q->zdevice = NULL; q->iflags = -1; q->iwr_flags = -1; q->fterminal = FALSE; q->ftli = FALSE; q->ibaud = 0; q->ipid = -1; qconn->psysdep = (pointer) q; qconn->qcmds = &spipecmds; return TRUE; } static void uspipe_free (qconn) struct sconnection *qconn; { xfree (qconn->psysdep); } /* Open a pipe port. */ /*ARGSUSED*/ static boolean fspipe_open (qconn, ibaud, fwait, fuser) struct sconnection *qconn ATTRIBUTE_UNUSED; long ibaud ATTRIBUTE_UNUSED; boolean fwait; boolean fuser ATTRIBUTE_UNUSED; { /* We don't do incoming waits on pipes. */ if (fwait) return FALSE; return TRUE; } /* Close a pipe port. */ /*ARGSUSED*/ static boolean fspipe_close (qconn, puuconf, qdialer, fsuccess) struct sconnection *qconn; pointer puuconf ATTRIBUTE_UNUSED; struct uuconf_dialer *qdialer ATTRIBUTE_UNUSED; boolean fsuccess ATTRIBUTE_UNUSED; { struct ssysdep_conn *qsysdep; boolean fret; qsysdep = (struct ssysdep_conn *) qconn->psysdep; fret = TRUE; /* Close our sides of the pipe. */ if (qsysdep->ord >= 0 && close (qsysdep->ord) < 0) { ulog (LOG_ERROR, "fspipe_close: close read fd: %s", strerror (errno)); fret = FALSE; } if (qsysdep->owr != qsysdep->ord && qsysdep->owr >= 0 && close (qsysdep->owr) < 0) { ulog (LOG_ERROR, "fspipe_close: close write fd: %s", strerror (errno)); fret = FALSE; } qsysdep->ord = -1; qsysdep->owr = -1; /* Kill dangling child process. */ if (qsysdep->ipid >= 0) { if (kill (qsysdep->ipid, SIGHUP) == 0) usysdep_sleep (2); #ifdef SIGPIPE if (kill (qsysdep->ipid, SIGPIPE) == 0) usysdep_sleep (2); #endif if (kill (qsysdep->ipid, SIGKILL) < 0 && errno == EPERM) { ulog (LOG_ERROR, "fspipe_close: Cannot kill child pid %lu: %s", (unsigned long) qsysdep->ipid, strerror (errno)); fret = FALSE; } else (void) ixswait ((unsigned long) qsysdep->ipid, (const char *) NULL); } qsysdep->ipid = -1; return fret; } /* Dial out on a pipe port, so to speak: launch connection program under us. The code alternates q->o between q->ord and q->owr as appropriate. It is always q->ord before any call to fsblock. */ /*ARGSUSED*/ static boolean fspipe_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialer) struct sconnection *qconn; pointer puuconf; const struct uuconf_system *qsys ATTRIBUTE_UNUSED; const char *zphone ATTRIBUTE_UNUSED; struct uuconf_dialer *qdialer; enum tdialerfound *ptdialer; { struct ssysdep_conn *q; int aidescs[3]; const char **pzprog; q = (struct ssysdep_conn *) qconn->psysdep; *ptdialer = DIALERFOUND_FALSE; pzprog = (const char **) qconn->qport->uuconf_u.uuconf_spipe.uuconf_pzcmd; if (pzprog == NULL) { ulog (LOG_ERROR, "No command for pipe connection"); return FALSE; } aidescs[0] = SPAWN_WRITE_PIPE; aidescs[1] = SPAWN_READ_PIPE; aidescs[2] = SPAWN_NULL; /* Pass fkeepuid, fkeepenv and fshell as TRUE. This puts the responsibility of security on the connection program. */ q->ipid = ixsspawn (pzprog, aidescs, TRUE, TRUE, (const char *) NULL, FALSE, TRUE, (const char *) NULL, (const char *) NULL, (const char *) NULL); if (q->ipid < 0) { ulog (LOG_ERROR, "ixsspawn (%s): %s", pzprog[0], strerror (errno)); return FALSE; } q->owr = aidescs[0]; q->ord = aidescs[1]; q->o = q->ord; q->iflags = fcntl (q->ord, F_GETFL, 0); q->iwr_flags = fcntl (q->owr, F_GETFL, 0); if (q->iflags < 0 || q->iwr_flags < 0) { ulog (LOG_ERROR, "fspipe_dial: fcntl: %s", strerror (errno)); (void) fspipe_close (qconn, puuconf, qdialer, FALSE); return FALSE; } return TRUE; } #if 0 /* Marc Boucher's contributed code used an alarm to avoid waiting too long when closing the pipe. However, I believe that it is not possible for the kernel to sleep when closing a pipe; it is only possible when closing a device. Therefore, I have removed the code, but am preserving it in case I am wrong. To reenable it, the two calls to close in fspipe_close should be changed to call fspipe_alarmclose. */ static RETSIGTYPE usalarm (isig) int isig; { #if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET (void) signal (isig, usalarm); #endif #if HAVE_RESTARTABLE_SYSCALLS longjmp (sSjmp_buf, 1); #endif } static int fspipe_alarmclose (fd) int fd; { int iret = -1; int ierrno = 0; if (fsysdep_catch ()) { usysdep_start_catch (); usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); (void) alarm (30); iret = close (fd); ierrno = errno; } usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); (void) alarm (0); usysdep_end_catch (); errno = ierrno; return iret; } #endif /* 0 */ uucp-1.07/unix/portnm.c0000664000076400007640000000167507665321761010604 /* portnm.c Get the port name of stdin. */ #include "uucp.h" #include "sysdep.h" #include "system.h" #if HAVE_TCP #if HAVE_SYS_TYPES_TCP_H #include #endif #include #endif #ifndef ttyname extern char *ttyname (); #endif /* Get the port name of standard input. I assume that Unix systems generally support ttyname. If they don't, this function can just return NULL. It uses getsockname to see whether standard input is a TCP connection. */ const char * zsysdep_port_name (ftcp_port) boolean *ftcp_port; { const char *z; *ftcp_port = FALSE; #if HAVE_TCP { size_t clen; struct sockaddr s; clen = sizeof (struct sockaddr); if (getsockname (0, &s, &clen) == 0) *ftcp_port = TRUE; } #endif /* HAVE_TCP */ z = ttyname (0); if (z == NULL) return NULL; if (strncmp (z, "/dev/", sizeof "/dev/" - 1) == 0) return z + sizeof "/dev/" - 1; else return z; } uucp-1.07/unix/priv.c0000664000076400007640000000120007665321761010225 /* priv.c See if a user is privileged. */ #include "uucp.h" #include "sysdep.h" #include "system.h" /* See whether the user is privileged (for example, only privileged users are permitted to kill arbitrary jobs with uustat). This is true only for root and uucp. We check for uucp by seeing if the real user ID and the effective user ID are the same; this works because we should be suid to uucp, so our effective user ID will always be uucp while our real user ID will be whoever ran the program. */ boolean fsysdep_privileged () { uid_t iuid; iuid = getuid (); return iuid == 0 || iuid == geteuid (); } uucp-1.07/unix/proctm.c0000664000076400007640000001124407665321761010562 /* proctm.c Get the time spent in the process. Copyright (C) 1992, 1993 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "sysdep.h" #include "system.h" #if HAVE_SYS_PARAM_H #include #endif #if HAVE_LIMITS_H #include #endif /* Prefer gettimeofday to ftime to times. */ #if HAVE_GETTIMEOFDAY || HAVE_FTIME #undef HAVE_TIMES #define HAVE_TIMES 0 #endif #if HAVE_GETTIMEOFDAY #undef HAVE_FTIME #define HAVE_FTIME 0 #endif #if HAVE_TIME_H && (TIME_WITH_SYS_TIME || ! HAVE_GETTIMEOFDAY) #include #endif #if HAVE_GETTIMEOFDAY #include #endif #if HAVE_FTIME #include #endif #if HAVE_TIMES #if HAVE_SYS_TIMES_H #include #endif #if TIMES_DECLARATION_OK /* We use a macro to protect this because times really returns clock_t and on some systems, such as Ultrix 4.0, clock_t is int. We don't leave it out entirely because on some systems, such as System III, the declaration is necessary for correct compilation. */ #ifndef times extern long times (); #endif #endif /* TIMES_DECLARATION_OK */ #ifdef _SC_CLK_TCK #define HAVE_SC_CLK_TCK 1 #else #define HAVE_SC_CLK_TCK 0 #endif /* TIMES_TICK may have been set in policy.h, or we may be able to get it using sysconf. If neither is the case, try to find a useful definition from the system header files. */ #if TIMES_TICK == 0 && (! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK) #ifdef CLK_TCK #undef TIMES_TICK #define TIMES_TICK CLK_TCK #else /* ! defined (CLK_TCK) */ #ifdef HZ #undef TIMES_TICK #define TIMES_TICK HZ #endif /* defined (HZ) */ #endif /* ! defined (CLK_TCK) */ #endif /* TIMES_TICK == 0 && (! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK) */ #endif /* HAVE_TIMES */ #ifndef time extern time_t time (); #endif #if HAVE_SYSCONF #ifndef sysconf extern long sysconf (); #endif #endif /* Get the time in seconds and microseconds; this need only work within the process when called from the system independent code. It is also called by ixsysdep_time. */ long ixsysdep_process_time (pimicros) long *pimicros; { #if HAVE_GETTIMEOFDAY struct timeval stime; struct timezone stz; (void) gettimeofday (&stime, &stz); if (pimicros != NULL) *pimicros = (long) stime.tv_usec; return (long) stime.tv_sec; #endif /* HAVE_GETTIMEOFDAY */ #if HAVE_FTIME static boolean fbad; if (! fbad) { struct timeb stime; static struct timeb slast; (void) ftime (&stime); /* On some systems, such as SCO 3.2.2, ftime can go backwards in time. If we detect this, we switch to using time. */ if (slast.time != 0 && (stime.time < slast.time || (stime.time == slast.time && stime.millitm < slast.millitm))) fbad = TRUE; else { slast = stime; if (pimicros != NULL) *pimicros = (long) stime.millitm * (long) 1000; return (long) stime.time; } } if (pimicros != NULL) *pimicros = 0; return (long) time ((time_t *) NULL); #endif /* HAVE_FTIME */ #if HAVE_TIMES struct tms s; long i; static int itick; if (itick == 0) { #if TIMES_TICK == 0 #if HAVE_SYSCONF && HAVE_SC_CLK_TCK itick = (int) sysconf (_SC_CLK_TCK); #else /* ! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK */ const char *z; z = getenv ("HZ"); if (z != NULL) itick = (int) strtol (z, (char **) NULL, 10); /* If we really couldn't get anything, just use 60. */ if (itick == 0) itick = 60; #endif /* ! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK */ #else /* TIMES_TICK != 0 */ itick = TIMES_TICK; #endif /* TIMES_TICK == 0 */ } i = (long) times (&s); if (pimicros != NULL) *pimicros = (i % (long) itick) * ((long) 1000000 / (long) itick); return i / (long) itick; #endif /* HAVE_TIMES */ #if ! HAVE_GETTIMEOFDAY && ! HAVE_FTIME && ! HAVE_TIMES if (pimicros != NULL) *pimicros = 0; return (long) time ((time_t *) NULL); #endif /* ! HAVE_GETTIMEOFDAY && ! HAVE_FTIME && ! HAVE_TIMES */ } uucp-1.07/unix/recep.c0000664000076400007640000001273207665321761010357 /* recep.c See whether a file has already been received. Copyright (C) 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "uuconf.h" #include "sysdep.h" #include "system.h" #include #if HAVE_TIME_H #include #endif #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif static char *zsreceived_name P((const struct uuconf_system *qsys, const char *ztemp)); /* These routines are used to see whether we have already received a file in a previous UUCP connection. It is possible for the acknowledgement of a received file to be lost. The sending system will then not know that the file was correctly received, and will send it again. This can be a problem particularly with protocols which support channels, since they may send several small files in a single window, all of which may be received correctly although the sending system never sees the acknowledgement. If these files involve an execution, the execution will happen twice, which will be bad. We use a simple system. For each file we want to remember, we create an empty file names .Received/SYS/TEMP, where SYS is the name of the system and TEMP is the name of the temporary file used by the sender. If no temporary file is used by the sender, we don't remember that we received the file. This is not perfect, but execution files will always have a temporary file, so the most important case is handled. Also, any file received from Taylor UUCP 1.04 or greater will always have a temporary file. */ /* Return the name we are going use for the marker, or NULL if we have no name. */ static char * zsreceived_name (qsys, ztemp) const struct uuconf_system *qsys; const char *ztemp; { if (ztemp != NULL && *ztemp == 'D' && strcmp (ztemp, "D.0") != 0) return zsappend3 (".Received", qsys->uuconf_zname, ztemp); else return NULL; } /* Remember that we have already received a file. */ /*ARGSUSED*/ boolean fsysdep_remember_reception (qsys, zto, ztemp) const struct uuconf_system *qsys; const char *zto ATTRIBUTE_UNUSED; const char *ztemp; { char *zfile; int o; zfile = zsreceived_name (qsys, ztemp); if (zfile == NULL) return TRUE; o = creat (zfile, IPUBLIC_FILE_MODE); if (o < 0) { if (errno == ENOENT) { if (fsysdep_make_dirs (zfile, FALSE)) { ubuffree (zfile); return FALSE; } o = creat (zfile, IPUBLIC_FILE_MODE); } if (o < 0) { ulog (LOG_ERROR, "creat (%s): %s", zfile, strerror (errno)); ubuffree (zfile); return FALSE; } } ubuffree (zfile); /* We don't have to actually put anything in the file; we just use the name. This is more convenient than keeping a file with a list of names. */ if (close (o) < 0) { ulog (LOG_ERROR, "fsysdep_remember_reception: close: %s", strerror (errno)); return FALSE; } return TRUE; } /* The number of seconds in one week. We must cast to long for this to be calculated correctly on a machine with 16 bit ints. */ #define SECS_PER_WEEK ((long) 7 * (long) 24 * (long) 60 * (long) 60) /* See if we have already received a file. Note that don't delete the marker file here, because we need to know that the sending system has received our denial first. This function returns TRUE if the file has already been received, FALSE if it has not. */ /*ARGSUSED*/ boolean fsysdep_already_received (qsys, zto, ztemp) const struct uuconf_system *qsys; const char *zto ATTRIBUTE_UNUSED; const char *ztemp; { char *zfile; struct stat s; boolean fret; zfile = zsreceived_name (qsys, ztemp); if (zfile == NULL) return FALSE; if (stat (zfile, &s) < 0) { if (errno != ENOENT) ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); ubuffree (zfile); return FALSE; } /* Ignore the file (return FALSE) if it is over one week old. */ fret = s.st_mtime + SECS_PER_WEEK >= time ((time_t *) NULL); if (fret) DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, "fsysdep_already_received: Found %s", zfile); ubuffree (zfile); return fret; } /* Forget that we have received a file. */ /*ARGSUSED*/ boolean fsysdep_forget_reception (qsys, zto, ztemp) const struct uuconf_system *qsys; const char *zto ATTRIBUTE_UNUSED; const char *ztemp; { char *zfile; zfile = zsreceived_name (qsys, ztemp); if (zfile == NULL) return TRUE; if (remove (zfile) < 0 && errno != ENOENT) { ulog (LOG_ERROR, "remove (%s): %s", zfile, strerror (errno)); ubuffree (zfile); return FALSE; } return TRUE; } uucp-1.07/unix/run.c0000664000076400007640000000563607665321761010072 /* run.c Run a program. Copyright (C) 1992, 1993, 1994 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include /* Start up a new program. */ boolean fsysdep_run (ffork, zprogram, zarg1, zarg2) boolean ffork; const char *zprogram; const char *zarg1; const char *zarg2; { char *zlib; const char *azargs[4]; int aidescs[3]; pid_t ipid; /* If we are supposed to fork, fork and then spawn so that we don't have to worry about zombie processes. */ if (ffork) { ipid = ixsfork (); if (ipid < 0) { ulog (LOG_ERROR, "fork: %s", strerror (errno)); return FALSE; } if (ipid != 0) { /* This is the parent. Wait for the child we just forked to exit (below) and return. */ (void) ixswait ((unsigned long) ipid, (const char *) NULL); /* Force the log files to be reopened in case the child just output any error messages and stdio doesn't handle appending correctly. */ ulog_close (); return TRUE; } /* This is the child. Detach from the terminal to avoid any unexpected SIGHUP signals. At this point we are definitely not a process group leader, so usysdep_detach will not fork again. */ usysdep_detach (); /* Now spawn the program and then exit. */ } zlib = zbufalc (sizeof SBINDIR + sizeof "/" + strlen (zprogram)); sprintf (zlib, "%s/%s", SBINDIR, zprogram); azargs[0] = zlib; azargs[1] = zarg1; azargs[2] = zarg2; azargs[3] = NULL; aidescs[0] = SPAWN_NULL; aidescs[1] = SPAWN_NULL; aidescs[2] = SPAWN_NULL; /* We pass fsetuid and fshell as TRUE, which permits uucico and uuxqt to be replaced by (non-setuid) shell scripts. */ ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, (const char *) NULL, FALSE, TRUE, (const char *) NULL, (const char *) NULL, (const char *) NULL); ubuffree (zlib); if (ipid < 0) { ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); if (ffork) _exit (EXIT_FAILURE); return FALSE; } if (ffork) _exit (EXIT_SUCCESS); return TRUE; } uucp-1.07/unix/seq.c0000664000076400007640000000616107665321761010050 /* seq.c Get and increment the conversation sequence number for a system. Copyright (C) 1991, 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "uuconf.h" #include "sysdep.h" #include "system.h" #include /* Get the current conversation sequence number for a remote system, and increment it for next time. The conversation sequence number is kept in a file named for the system in the directory .Sequence in the spool directory. This is not compatible with other versions of UUCP, but it makes more sense to me. The sequence file is only used if specified in the information for that system. */ long ixsysdep_get_sequence (qsys) const struct uuconf_system *qsys; { FILE *e; char *zname; struct stat s; long iseq; /* This will only be called when the system is locked anyhow, so there is no need to use a separate lock for the conversation sequence file. */ #if SPOOLDIR_HDB || SPOOLDIR_SVR4 zname = zsysdep_in_dir (".SQFILE", qsys->uuconf_zname); #else zname = zsysdep_in_dir (".Sequence", qsys->uuconf_zname); #endif iseq = 0; if (stat (zname, &s) == 0) { boolean fok; char *zline; size_t cline; /* The file should only be readable and writable by uucp. */ if ((s.st_mode & (S_IRWXG | S_IRWXO)) != 0) { ulog (LOG_ERROR, "Bad file protection for conversation sequence file"); ubuffree (zname); return -1; } e = fopen (zname, "r+"); if (e == NULL) { ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno)); ubuffree (zname); return -1; } ubuffree (zname); fok = TRUE; zline = NULL; cline = 0; if (getline (&zline, &cline, e) <= 0) fok = FALSE; else { char *zend; iseq = strtol (zline, &zend, 10); if (zend == zline) fok = FALSE; } xfree ((pointer) zline); if (! fok) { ulog (LOG_ERROR, "Bad format for conversation sequence file"); (void) fclose (e); return -1; } rewind (e); } else { e = esysdep_fopen (zname, FALSE, FALSE, TRUE); ubuffree (zname); if (e == NULL) return -1; } ++iseq; fprintf (e, "%ld", iseq); if (fclose (e) != 0) { ulog (LOG_ERROR, "fclose: %s", strerror (errno)); return -1; } return iseq; } uucp-1.07/unix/serial.c0000664000076400007640000025500207665321761010537 /* serial.c The serial port communication routines for Unix. Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char serial_rcsid[] = "$Id: serial.c,v 1.78 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "conn.h" #include "sysdep.h" #include #include #if HAVE_SYS_PARAM_H #include #endif #if HAVE_LIMITS_H #include #endif #if HAVE_TLI #if HAVE_TIUSER_H #include #else /* ! HAVE_TIUSER_H */ #if HAVE_XTI_H #include #endif /* HAVE_XTI_H */ #endif /* ! HAVE_TIUSER_H */ #endif /* HAVE_TLI */ #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif #if HAVE_SYS_IOCTL_H || HAVE_TXADDCD #include #endif #if HAVE_SELECT #if HAVE_SYS_TIME_H #include #endif #if HAVE_SYS_SELECT_H #include #endif #endif #if HAVE_TIME_H #if ! HAVE_SYS_TIME_H || ! HAVE_SELECT || TIME_WITH_SYS_TIME #include #endif #endif #if HAVE_STRIP_BUG && HAVE_BSD_TTY #include #endif #if HAVE_SVR4_LOCKFILES /* Get the right definitions for major and minor. */ #if MAJOR_IN_MKDEV #include #endif /* MAJOR_IN_MKDEV */ #if MAJOR_IN_SYSMACROS #include #endif /* MAJOR_IN_SYSMACROS */ #if ! MAJOR_IN_MKDEV && ! MAJOR_IN_SYSMACROS #ifndef major #define major(i) (((i) >> 8) & 0xff) #endif #ifndef minor #define minor(i) ((i) & 0xff) #endif #endif /* ! MAJOR_IN_MKDEV && ! MAJOR_IN_SYSMACROS */ #endif /* HAVE_SVR4_LOCKFILES */ #if HAVE_DEV_INFO #include #endif #if HAVE_SYS_TERMIOX_H #include #endif /* Get definitions for both O_NONBLOCK and O_NDELAY. */ #ifndef O_NDELAY #ifdef FNDELAY #define O_NDELAY FNDELAY #else /* ! defined (FNDELAY) */ #define O_NDELAY 0 #endif /* ! defined (FNDELAY) */ #endif /* ! defined (O_NDELAY) */ #ifndef O_NONBLOCK #ifdef FNBLOCK #define O_NONBLOCK FNBLOCK #else /* ! defined (FNBLOCK) */ #define O_NONBLOCK 0 #endif /* ! defined (FNBLOCK) */ #endif /* ! defined (O_NONBLOCK) */ #if O_NDELAY == 0 && O_NONBLOCK == 0 #error No way to do nonblocking I/O #endif /* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */ #ifndef EAGAIN #ifndef EWOULDBLOCK #define EAGAIN (-1) #define EWOULDBLOCK (-1) #else /* defined (EWOULDBLOCK) */ #define EAGAIN EWOULDBLOCK #endif /* defined (EWOULDBLOCK) */ #else /* defined (EAGAIN) */ #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif /* ! defined (EWOULDBLOCK) */ #endif /* defined (EAGAIN) */ #ifndef ENODATA #define ENODATA EAGAIN #endif /* Make sure we have a definition for MAX_INPUT. */ #ifndef MAX_INPUT #define MAX_INPUT (256) #endif /* If we have the TIOCSINUSE ioctl call, we use it to lock a terminal. Otherwise, if we have the TIOCEXCL ioctl call, we have to open the terminal before we know that it is unlocked. */ #ifdef TIOCSINUSE #define HAVE_TIOCSINUSE 1 #else #ifdef TIOCEXCL #define HAVE_TIOCEXCL 1 #endif #endif #if HAVE_TLI extern int t_errno; extern char *t_errlist[]; extern int t_nerr; #endif /* Determine bits to clear for the various terminal control fields for HAVE_SYSV_TERMIO and HAVE_POSIX_TERMIOS. */ /* These fields are defined on some systems, and I am told that it does not hurt to clear them, and it sometimes helps. */ #ifndef IMAXBEL #define IMAXBEL 0 #endif #ifndef PENDIN #define PENDIN 0 #endif #if HAVE_SYSV_TERMIO #define ICLEAR_IFLAG (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK \ | ISTRIP | INLCR | IGNCR | ICRNL | IUCLC \ | IXON | IXANY | IXOFF | IMAXBEL) #define ICLEAR_OFLAG (OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET \ | OFILL | OFDEL | NLDLY | CRDLY | TABDLY | BSDLY \ | VTDLY | FFDLY) #define ICLEAR_CFLAG (CBAUD | CSIZE | PARENB | PARODD) #define ISET_CFLAG (CS8 | CREAD | HUPCL) #define ICLEAR_LFLAG (ISIG | ICANON | XCASE | ECHO | ECHOE | ECHOK \ | ECHONL | NOFLSH | PENDIN) #endif #if HAVE_POSIX_TERMIOS #define ICLEAR_IFLAG (BRKINT | ICRNL | IGNBRK | IGNCR | IGNPAR \ | INLCR | INPCK | ISTRIP | IXOFF | IXON \ | PARMRK | IMAXBEL) #define ICLEAR_OFLAG (OPOST) #define ICLEAR_CFLAG (CSIZE | PARENB | PARODD) #define ISET_CFLAG (CS8 | CREAD | HUPCL) #define ICLEAR_LFLAG (ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN \ | ISIG | NOFLSH | TOSTOP | PENDIN) #endif enum tclocal_setting { SET_CLOCAL, CLEAR_CLOCAL, IGNORE_CLOCAL }; /* Local functions. */ static RETSIGTYPE usalarm P((int isig)); static boolean fsserial_init P((struct sconnection *qconn, const struct sconncmds *qcmds, const char *zdevice)); static void usserial_free P((struct sconnection *qconn)); static boolean fsserial_lockfile P((boolean flok, const struct sconnection *)); static boolean fsserial_lock P((struct sconnection *qconn, boolean fin, boolean fuser)); static boolean fsserial_unlock P((struct sconnection *qconn)); static boolean fsserial_open P((struct sconnection *qconn, long ibaud, boolean fwait, boolean fuser, enum tclocal_setting tlocal)); static boolean fsstdin_open P((struct sconnection *qconn, long ibaud, boolean fwait, boolean fuser)); static boolean fsmodem_open P((struct sconnection *qconn, long ibaud, boolean fwait, boolean fuser)); static boolean fsdirect_open P((struct sconnection *qconn, long ibaud, boolean fwait, boolean fuser)); static boolean fsblock P((struct ssysdep_conn *q, boolean fblock)); static boolean fsserial_close P((struct ssysdep_conn *q)); static boolean fsstdin_close P((struct sconnection *qconn, pointer puuconf, struct uuconf_dialer *qdialer, boolean fsuccess)); static boolean fsmodem_close P((struct sconnection *qconn, pointer puuconf, struct uuconf_dialer *qdialer, boolean fsuccess)); static boolean fsdirect_close P((struct sconnection *qconn, pointer puuconf, struct uuconf_dialer *qdialer, boolean fsuccess)); static boolean fsserial_break P((struct sconnection *qconn)); static boolean fsstdin_break P((struct sconnection *qconn)); static boolean fsserial_set P((struct sconnection *qconn, enum tparitysetting tparity, enum tstripsetting tstrip, enum txonxoffsetting txonxoff)); static boolean fsstdin_set P((struct sconnection *qconn, enum tparitysetting tparity, enum tstripsetting tstrip, enum txonxoffsetting txonxoff)); static boolean fsmodem_carrier P((struct sconnection *qconn, boolean fcarrier)); static boolean fsserial_hardflow P((struct sconnection *qconn, boolean fhardflow)); static boolean fsrun_chat P((int oread, int owrite, char **pzprog)); static long isserial_baud P((struct sconnection *qconn)); /* The command table for standard input ports. */ static const struct sconncmds sstdincmds = { usserial_free, NULL, /* pflock */ NULL, /* pfunlock */ fsstdin_open, fsstdin_close, NULL, /* pfdial */ fsdouble_read, fsdouble_write, fsysdep_conn_io, fsstdin_break, fsstdin_set, NULL, /* pfcarrier */ fsdouble_chat, isserial_baud }; /* The command table for modem ports. */ static const struct sconncmds smodemcmds = { usserial_free, fsserial_lock, fsserial_unlock, fsmodem_open, fsmodem_close, fmodem_dial, fsysdep_conn_read, fsysdep_conn_write, fsysdep_conn_io, fsserial_break, fsserial_set, fsmodem_carrier, fsysdep_conn_chat, isserial_baud }; /* The command table for direct ports. */ static const struct sconncmds sdirectcmds = { usserial_free, fsserial_lock, fsserial_unlock, fsdirect_open, fsdirect_close, NULL, /* pfdial */ fsysdep_conn_read, fsysdep_conn_write, fsysdep_conn_io, fsserial_break, fsserial_set, NULL, /* pfcarrier */ fsysdep_conn_chat, isserial_baud }; /* If the system will let us set both O_NDELAY and O_NONBLOCK, we do so. This is because some ancient drivers on some systems appear to look for one but not the other. Some other systems will give an EINVAL error if we attempt to set both, so we use a static global to hold the value we want to set. If we get EINVAL, we change the global and try again (if some system gives an error other than EINVAL, the code will have to be modified). */ static int iSunblock = O_NDELAY | O_NONBLOCK; /* This code handles SIGALRM. See the discussion above fsysdep_conn_read. Normally we ignore SIGALRM, but the handler will temporarily be set to this function, which should set fSalarm and then either longjmp or schedule another SIGALRM. fSalarm is never referred to outside of this file, but we don't make it static to try to fool compilers which don't understand volatile. */ volatile sig_atomic_t fSalarm; static RETSIGTYPE usalarm (isig) int isig ATTRIBUTE_UNUSED; { #if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET (void) signal (isig, usalarm); #endif fSalarm = TRUE; #if HAVE_RESTARTABLE_SYSCALLS longjmp (sSjmp_buf, 1); #else alarm (1); #endif } /* We need a simple routine to block SIGINT, SIGQUIT, SIGTERM and SIGPIPE and another to restore the original state. When these functions are called (in fsysdep_modem_close) SIGHUP is being ignored. The routines are isblocksigs, which returns a value of type HELD_SIG_MASK and usunblocksigs which takes a single argument of type HELD_SIG_MASK. */ #if HAVE_SIGPROCMASK /* Use the POSIX sigprocmask call. */ #define HELD_SIG_MASK sigset_t static sigset_t isblocksigs P((void)); static sigset_t isblocksigs () { sigset_t sblock, sold; /* These expressions need an extra set of parentheses to avoid a bug in SCO 3.2.2. */ (void) (sigemptyset (&sblock)); (void) (sigaddset (&sblock, SIGINT)); (void) (sigaddset (&sblock, SIGQUIT)); (void) (sigaddset (&sblock, SIGTERM)); (void) (sigaddset (&sblock, SIGPIPE)); (void) sigprocmask (SIG_BLOCK, &sblock, &sold); return sold; } #define usunblocksigs(s) \ ((void) sigprocmask (SIG_SETMASK, &(s), (sigset_t *) NULL)) #else /* ! HAVE_SIGPROCMASK */ #if HAVE_SIGBLOCK /* Use the BSD sigblock and sigsetmask calls. */ #define HELD_SIG_MASK int #ifndef sigmask #define sigmask(i) (1 << ((i) - 1)) #endif #define isblocksigs() \ sigblock (sigmask (SIGINT) | sigmask (SIGQUIT) \ | sigmask (SIGTERM) | sigmask (SIGPIPE)) #define usunblocksigs(i) ((void) sigsetmask (i)) #else /* ! HAVE_SIGBLOCK */ #if HAVE_SIGHOLD /* Use the SVR3 sighold and sigrelse calls. */ #define HELD_SIG_MASK int static int isblocksigs P((void)); static int isblocksigs () { sighold (SIGINT); sighold (SIGQUIT); sighold (SIGTERM); sighold (SIGPIPE); return 0; } static void usunblocksigs P((int)); /*ARGSUSED*/ static void usunblocksigs (i) int i; { sigrelse (SIGINT); sigrelse (SIGQUIT); sigrelse (SIGTERM); sigrelse (SIGPIPE); } #else /* ! HAVE_SIGHOLD */ /* We have no way to block signals. This system will suffer from a race condition in fsysdep_modem_close. */ #define HELD_SIG_MASK int #define isblocksigs() 0 #define usunblocksigs(i) #endif /* ! HAVE_SIGHOLD */ #endif /* ! HAVE_SIGBLOCK */ #endif /* ! HAVE_SIGPROCMASK */ /* Initialize a connection for use on a serial port. */ static boolean fsserial_init (qconn, qcmds, zdevice) struct sconnection *qconn; const struct sconncmds *qcmds; const char *zdevice; { struct ssysdep_conn *q; q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn)); if (zdevice == NULL && qconn->qport != NULL && qconn->qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN) zdevice = qconn->qport->uuconf_zname; if (zdevice == NULL) q->zdevice = NULL; else if (*zdevice == '/') q->zdevice = zbufcpy (zdevice); else { size_t clen; clen = strlen (zdevice); q->zdevice = zbufalc (sizeof "/dev/" + clen); memcpy (q->zdevice, "/dev/", sizeof "/dev/" - 1); memcpy (q->zdevice + sizeof "/dev/" - 1, zdevice, clen); q->zdevice[sizeof "/dev/" + clen - 1] = '\0'; } q->o = -1; q->ord = -1; q->owr = -1; q->ftli = FALSE; qconn->psysdep = (pointer) q; qconn->qcmds = qcmds; return TRUE; } /* Initialize a connection for use on standard input. */ boolean fsysdep_stdin_init (qconn) struct sconnection *qconn; { /* chmod /dev/tty to prevent other users from writing messages to it. This is essentially `mesg n'. */ (void) chmod ("/dev/tty", S_IRUSR | S_IWUSR); return fsserial_init (qconn, &sstdincmds, (const char *) NULL); } /* Initialize a connection for use on a modem port. */ boolean fsysdep_modem_init (qconn) struct sconnection *qconn; { return fsserial_init (qconn, &smodemcmds, qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdevice); } /* Initialize a connection for use on a direct port. */ boolean fsysdep_direct_init (qconn) struct sconnection *qconn; { return fsserial_init (qconn, &sdirectcmds, qconn->qport->uuconf_u.uuconf_sdirect.uuconf_zdevice); } /* Free up a serial port. */ static void usserial_free (qconn) struct sconnection *qconn; { struct ssysdep_conn *qsysdep; qsysdep = (struct ssysdep_conn *) qconn->psysdep; ubuffree (qsysdep->zdevice); xfree ((pointer) qsysdep); qconn->psysdep = NULL; } #if HAVE_SEQUENT_LOCKFILES #define LCK_TEMPLATE "LCK..tty" #else #define LCK_TEMPLATE "LCK.." #endif /* This routine is used for both locking and unlocking. It is the only routine which knows how to translate a device name into the name of a lock file. If it can't figure out a name, it does nothing and returns TRUE. */ static boolean fsserial_lockfile (flok, qconn) boolean flok; const struct sconnection *qconn; { struct ssysdep_conn *qsysdep; const char *z; char *zalc; boolean fret; qsysdep = (struct ssysdep_conn *) qconn->psysdep; if (qconn->qport == NULL) z = NULL; else z = qconn->qport->uuconf_zlockname; zalc = NULL; if (z == NULL) { #if HAVE_QNX_LOCKFILES { nid_t idevice_nid; char abdevice_nid[13]; /* length of long, a period, and a NUL */ size_t cdevice_nid; const char *zbase; size_t clen; /* If the node ID is explicitly specified as part of the pathname to the device, use that. Otherwise, presume the device is local to the current node. */ if (qsysdep->zdevice[0] == '/' && qsysdep->zdevice[1] == '/') idevice_nid = (nid_t) strtol (qsysdep->zdevice + 2, (char **) NULL, 10); else idevice_nid = getnid (); sprintf (abdevice_nid, "%ld.", (long) idevice_nid); cdevice_nid = strlen (abdevice_nid); zbase = strrchr (qsysdep->zdevice, '/') + 1; clen = strlen (zbase); zalc = zbufalc (sizeof LCK_TEMPLATE + cdevice_nid + clen); memcpy (zalc, LCK_TEMPLATE, sizeof LCK_TEMPLATE - 1); memcpy (zalc + sizeof LCK_TEMPLATE - 1, abdevice_nid, cdevice_nid); memcpy (zalc + sizeof LCK_TEMPLATE - 1 + cdevice_nid, zbase, clen + 1); z = zalc; } #else /* ! HAVE_QNX_LOCKFILES */ #if ! HAVE_SVR4_LOCKFILES { const char *zbase; size_t clen; zbase = strrchr (qsysdep->zdevice, '/') + 1; clen = strlen (zbase); zalc = zbufalc (sizeof LCK_TEMPLATE + clen); memcpy (zalc, LCK_TEMPLATE, sizeof LCK_TEMPLATE - 1); memcpy (zalc + sizeof LCK_TEMPLATE - 1, zbase, clen + 1); #if HAVE_SCO_LOCKFILES { char *zl; zl = zalc + sizeof LCK_TEMPLATE + clen - 2; if (isupper (*zl)) *zl = tolower (*zl); } #endif z = zalc; } #else /* HAVE_SVR4_LOCKFILES */ { struct stat s; if (stat (qsysdep->zdevice, &s) != 0) { ulog (LOG_ERROR, "stat (%s): %s", qsysdep->zdevice, strerror (errno)); return FALSE; } zalc = zbufalc (sizeof "LK.1234567890.1234567890.1234567890"); sprintf (zalc, "LK.%03d.%03d.%03d", major (s.st_dev), major (s.st_rdev), minor (s.st_rdev)); z = zalc; } #endif /* HAVE_SVR4_LOCKFILES */ #endif /* ! HAVE_QNX_LOCKFILES */ } if (flok) fret = fsdo_lock (z, FALSE, (boolean *) NULL); else fret = fsdo_unlock (z, FALSE); #if HAVE_COHERENT_LOCKFILES if (fret) { if (flok) { if (lockttyexist (z + sizeof LCK_TEMPLATE - 1)) { ulog (LOG_NORMAL, "%s: port already locked", z + sizeof LCK_TEMPLATE - 1); fret = FALSE; } else fret = fscoherent_disable_tty (z + sizeof LCK_TEMPLATE - 1, &qsysdep->zenable); } else { fret = TRUE; if (qsysdep->zenable != NULL) { const char *azargs[3]; int aidescs[3]; pid_t ipid; azargs[0] = "/etc/enable"; azargs[1] = qsysdep->zenable; azargs[2] = NULL; aidescs[0] = SPAWN_NULL; aidescs[1] = SPAWN_NULL; aidescs[2] = SPAWN_NULL; ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, (const char *) NULL, TRUE, TRUE, (const char *) NULL, (const char *) NULL, (const char *) NULL); if (ipid < 0) { ulog (LOG_ERROR, "ixsspawn (/etc/enable %s): %s", qsysdep->zenable, strerror (errno)); fret = FALSE; } else { if (ixswait ((unsigned long) ipid, (const char *) NULL) == 0) fret = TRUE; else fret = FALSE; } ubuffree (qsysdep->zenable); qsysdep->zenable = NULL; } } } #endif /* HAVE_COHERENT_LOCKFILES */ ubuffree (zalc); return fret; } /* If we can mark a modem line in use, then when we lock a port we must open it and mark it in use. We can't wait until the actual open because we can't fail out if it is locked then. */ static boolean fsserial_lock (qconn, fin, fuser) struct sconnection *qconn; boolean fin; boolean fuser; { if (! fsserial_lockfile (TRUE, qconn)) return FALSE; #if HAVE_TIOCSINUSE || HAVE_TIOCEXCL || HAVE_DEV_INFO /* Open the line and try to mark it in use. */ { struct ssysdep_conn *qsysdep; int iflag; uid_t ieuid; gid_t iegid; qsysdep = (struct ssysdep_conn *) qconn->psysdep; if (fin) iflag = 0; else iflag = iSunblock; if (fuser) { if (! fsuser_perms (&ieuid, &iegid)) { (void) fsserial_lockfile (FALSE, qconn); return FALSE; } } qsysdep->o = open (qsysdep->zdevice, O_RDWR | iflag); if (qsysdep->o < 0) { #if O_NONBLOCK != 0 if (! fin && iSunblock != O_NONBLOCK && errno == EINVAL) { iSunblock = O_NONBLOCK; qsysdep->o = open (qsysdep->zdevice, O_RDWR | O_NONBLOCK); } #endif if (qsysdep->o < 0) { int ierr; ierr = errno; if (fuser) (void) fsuucp_perms ((long) ieuid, (long) iegid); if (ierr != EBUSY) ulog (LOG_ERROR, "open (%s): %s", qsysdep->zdevice, strerror (ierr)); (void) fsserial_lockfile (FALSE, qconn); return FALSE; } } if (fuser) { if (! fsuucp_perms ((long) ieuid, (long) iegid)) { (void) close (qsysdep->o); qsysdep->o = -1; (void) fsserial_lockfile (FALSE, qconn); return FALSE; } } #if HAVE_TIOCSINUSE /* If we can't mark it in use, return FALSE to indicate that the lock failed. */ if (ioctl (qsysdep->o, TIOCSINUSE, 0) < 0) { if (errno != EALREADY) ulog (LOG_ERROR, "ioctl (TIOCSINUSE): %s", strerror (errno)); #ifdef TIOCNOTTY (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL); #endif (void) close (qsysdep->o); qsysdep->o = -1; (void) fsserial_lockfile (FALSE, qconn); return FALSE; } #endif #if HAVE_DEV_INFO /* QNX programs "lock" a serial port by simply opening it and checking if some other program also has the port open. If the count of openers is greater than one, the program presumes the port is "locked" and backs off. This isn't really "locking" of course, but it pretty much seems to work. This can result in dropping incoming connections if an outgoing connection is started at exactly the same time. It would probably be better to stop using the lock files at all for this case, but that would involve more complex changes to the code, and I'm afraid I would break something. -- Joe Wells */ { struct _dev_info_entry sdevinfo; if (dev_info (qsysdep->o, &sdevinfo) == -1) { ulog (LOG_ERROR, "dev_info: %s", strerror (errno)); sdevinfo.open_count = 2; /* force presumption of "locked" */ } if (sdevinfo.open_count != 1) { #ifdef TIOCNOTTY (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL); #endif /* TIOCNOTTY */ (void) close (qsysdep->o); qsysdep->o = -1; (void) fsserial_lockfile (FALSE, qconn); return FALSE; } } #endif /* HAVE_DEV_INFO */ if (fcntl (qsysdep->o, F_SETFD, fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); #ifdef TIOCNOTTY (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL); #endif (void) close (qsysdep->o); qsysdep->o = -1; (void) fsserial_lockfile (FALSE, qconn); return FALSE; } } #endif /* HAVE_TIOCSINUSE || HAVE_TIOCEXCL */ return TRUE; } /* Unlock a modem or direct port. */ static boolean fsserial_unlock (qconn) struct sconnection *qconn; { boolean fret; struct ssysdep_conn *qsysdep; fret = TRUE; /* The file may have been opened by fsserial_lock, so close it here if necessary. */ qsysdep = (struct ssysdep_conn *) qconn->psysdep; if (qsysdep->o >= 0) { #ifdef TIOCNOTTY (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL); #endif if (close (qsysdep->o) < 0) { ulog (LOG_ERROR, "close: %s", strerror (errno)); fret = FALSE; } qsysdep->o = -1; } if (! fsserial_lockfile (FALSE, qconn)) fret = FALSE; return fret; } /* A table to map baud rates into index numbers. */ #if HAVE_POSIX_TERMIOS typedef speed_t baud_code; #else typedef int baud_code; #endif static struct sbaud_table { baud_code icode; long ibaud; } asSbaud_table[] = { { B50, 50 }, { B75, 75 }, { B110, 110 }, { B134, 134 }, { B150, 150 }, { B200, 200 }, { B300, 300 }, { B600, 600 }, { B1200, 1200 }, { B1800, 1800 }, { B2400, 2400 }, { B4800, 4800 }, { B9600, 9600 }, #ifdef B19200 { B19200, 19200 }, #else /* ! defined (B19200) */ #ifdef EXTA { EXTA, 19200 }, #endif /* EXTA */ #endif /* ! defined (B19200) */ #ifdef B38400 { B38400, 38400 }, #else /* ! defined (B38400) */ #ifdef EXTB { EXTB, 38400 }, #endif /* EXTB */ #endif /* ! defined (B38400) */ #ifdef B57600 { B57600, 57600 }, #endif #ifdef B76800 { B76800, 76800 }, #endif #ifdef B115200 { B115200, 115200 }, #endif #ifdef B230400 { B230400, 230400 }, #else #ifdef _B230400 { _B230400, 230400 }, #endif /* _B230400 */ #endif /* ! defined (B230400) */ #ifdef B460800 { B460800, 460800 }, #else #ifdef _B460800 { _B460800, 460800 }, #endif /* _B460800 */ #endif /* ! defined (B460800) */ #ifdef B500000 { B500000, 500000 }, #endif #ifdef B576000 { B576000, 576000 }, #endif #ifdef B921600 { B921600, 921600 }, #endif #ifdef B1000000 { B1000000, 1000000 }, #endif #ifdef B1152000 { B1152000, 1152000 }, #endif #ifdef B1500000 { B1500000, 1500000 }, #endif #ifdef B2000000 { B2000000, 2000000 }, #endif #ifdef B2500000 { B2500000, 2500000 }, #endif #ifdef B3000000 { B3000000, 3000000 }, #endif #ifdef B3500000 { B3500000, 3500000 }, #endif #ifdef B4000000 { B4000000, 4000000 }, #endif { B0, 0 } }; #define CBAUD_TABLE (sizeof asSbaud_table / sizeof asSbaud_table[0]) #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS /* Hold the MIN value for the terminal to avoid setting it unnecessarily. */ static int cSmin; #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ /* Open a serial line. This sets the terminal settings. We begin in seven bit mode and let the protocol change if necessary. If fwait is FALSE we open the terminal in non-blocking mode. If fuser is true we open the port using the user's permissions. The tlocal parameter indicates whether to set, clear, or ignore CLOCAL. */ static boolean fsserial_open (qconn, ibaud, fwait, fuser, tlocal) struct sconnection *qconn; long ibaud; boolean fwait; boolean fuser; enum tclocal_setting tlocal; { struct ssysdep_conn *q; baud_code ib; q = (struct ssysdep_conn *) qconn->psysdep; if (q->zdevice != NULL) { #if LOG_DEVICE_PREFIX ulog_device (q->zdevice); #else const char *z; if (strncmp (q->zdevice, "/dev/", sizeof "/dev/" - 1) == 0) z = q->zdevice + sizeof "/dev/" - 1; else z = q->zdevice; ulog_device (z); #endif } else { const char *zport; boolean fdummy; #if DEBUG > 0 if (qconn->qport != NULL && qconn->qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN) ulog (LOG_FATAL, "fsserial_open: Can't happen"); #endif zport = zsysdep_port_name (&fdummy); if (zport != NULL) ulog_device (zport); } ib = B0; if (ibaud != 0) { size_t i; for (i = 0; i < CBAUD_TABLE; i++) if (asSbaud_table[i].ibaud == ibaud) break; if (i >= CBAUD_TABLE) { ulog (LOG_ERROR, "Unsupported baud rate %ld", ibaud); return FALSE; } ib = asSbaud_table[i].icode; } /* The port may have already been opened by the locking routine. */ if (q->o < 0) { int iflag; uid_t ieuid; gid_t iegid; if (fwait) iflag = 0; else iflag = iSunblock; if (fuser) { if (! fsuser_perms (&ieuid, &iegid)) return FALSE; } q->o = open (q->zdevice, O_RDWR | iflag); if (q->o < 0) { #if O_NONBLOCK != 0 if (! fwait && iSunblock != O_NONBLOCK && errno == EINVAL) { iSunblock = O_NONBLOCK; q->o = open (q->zdevice, O_RDWR | O_NONBLOCK); } #endif if (q->o < 0) { int ierr; ierr = errno; if (fuser) (void) fsuucp_perms ((long) ieuid, (long) iegid); ulog (LOG_ERROR, "open (%s): %s", q->zdevice, strerror (ierr)); return FALSE; } } if (fuser) { if (! fsuucp_perms ((long) ieuid, (long) iegid)) return FALSE; } if (fcntl (q->o, F_SETFD, fcntl (q->o, F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); return FALSE; } } /* Get the port flags, and make sure the ports are blocking. */ q->iflags = fcntl (q->o, F_GETFL, 0); if (q->iflags < 0) { ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); return FALSE; } q->iwr_flags = -1; if (! fsblock (q, TRUE)) return FALSE; if (! fgetterminfo (q->o, &q->sorig)) { q->fterminal = FALSE; return TRUE; } q->fterminal = TRUE; q->snew = q->sorig; #if HAVE_BSD_TTY q->snew.stty.sg_flags = RAW | ANYP; if (ibaud == 0) ib = q->snew.stty.sg_ospeed; else { q->snew.stty.sg_ispeed = ib; q->snew.stty.sg_ospeed = ib; } /* We don't want to receive any interrupt characters. */ q->snew.stchars.t_intrc = -1; q->snew.stchars.t_quitc = -1; q->snew.stchars.t_eofc = -1; q->snew.stchars.t_brkc = -1; q->snew.sltchars.t_suspc = -1; q->snew.sltchars.t_rprntc = -1; q->snew.sltchars.t_dsuspc = -1; q->snew.sltchars.t_flushc = -1; q->snew.sltchars.t_werasc = -1; q->snew.sltchars.t_lnextc = -1; #ifdef NTTYDISC /* We want to use the ``new'' terminal driver so that we can use the local mode bits to control XON/XOFF. */ { int iparam; if (ioctl (q->o, TIOCGETD, &iparam) >= 0 && iparam != NTTYDISC) { iparam = NTTYDISC; (void) ioctl (q->o, TIOCSETD, &iparam); } } #endif #ifdef TIOCHPCL /* When the file is closed, hang up the line. This is a safety measure in case the program crashes. */ (void) ioctl (q->o, TIOCHPCL, 0); #endif #ifdef TIOCFLUSH { int iparam; /* Flush pending input. */ #ifdef FREAD iparam = FREAD; #else iparam = 0; #endif (void) ioctl (q->o, TIOCFLUSH, &iparam); } #endif /* TIOCFLUSH */ #endif /* HAVE_BSD_TTY */ #if HAVE_SYSV_TERMIO if (ibaud == 0) ib = q->snew.c_cflag & CBAUD; q->snew.c_iflag &=~ ICLEAR_IFLAG; q->snew.c_oflag &=~ ICLEAR_OFLAG; q->snew.c_cflag &=~ ICLEAR_CFLAG; q->snew.c_cflag |= ib | ISET_CFLAG; q->snew.c_lflag &=~ ICLEAR_LFLAG; cSmin = 1; q->snew.c_cc[VMIN] = cSmin; q->snew.c_cc[VTIME] = 1; #ifdef TCFLSH /* Flush pending input. */ (void) ioctl (q->o, TCFLSH, 0); #endif #endif /* HAVE_SYSV_TERMIO */ #if HAVE_POSIX_TERMIOS if (ibaud == 0) ib = cfgetospeed (&q->snew); q->snew.c_iflag &=~ ICLEAR_IFLAG; q->snew.c_oflag &=~ ICLEAR_OFLAG; q->snew.c_cflag &=~ ICLEAR_CFLAG; q->snew.c_cflag |= ISET_CFLAG; q->snew.c_lflag &=~ ICLEAR_LFLAG; cSmin = 1; q->snew.c_cc[VMIN] = cSmin; q->snew.c_cc[VTIME] = 1; (void) cfsetospeed (&q->snew, ib); (void) cfsetispeed (&q->snew, ib); /* Flush pending input. */ (void) tcflush (q->o, TCIFLUSH); #endif /* HAVE_POSIX_TERMIOS */ #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS switch (tlocal) { case SET_CLOCAL: q->snew.c_cflag |= CLOCAL; break; case CLEAR_CLOCAL: q->snew.c_cflag &=~ CLOCAL; break; case IGNORE_CLOCAL: break; } #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ if (! fsetterminfo (q->o, &q->snew)) { ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno)); return FALSE; } #ifdef TIOCSCTTY /* On BSD 4.4, make it our controlling terminal. */ (void) ioctl (q->o, TIOCSCTTY, 0); #endif if (ibaud != 0) q->ibaud = ibaud; else { size_t i; q->ibaud = (long) 1200; for (i = 0; i < CBAUD_TABLE; i++) { if (asSbaud_table[i].icode == ib && asSbaud_table[i].ibaud != 0) { q->ibaud = asSbaud_table[i].ibaud; break; } } DEBUG_MESSAGE1 (DEBUG_PORT, "fsserial_open: Baud rate is %ld", q->ibaud); } return TRUE; } /* Open a standard input port. The code alternates q->o between q->ord and q->owr as appropriate. It is always q->ord before any call to fsblock. */ static boolean fsstdin_open (qconn, ibaud, fwait, fuser) struct sconnection *qconn; long ibaud; boolean fwait; boolean fuser; { struct ssysdep_conn *q; q = (struct ssysdep_conn *) qconn->psysdep; q->ord = 0; q->owr = 1; q->o = q->ord; if (! fsserial_open (qconn, ibaud, fwait, fuser, IGNORE_CLOCAL)) return FALSE; q->iwr_flags = fcntl (q->owr, F_GETFL, 0); if (q->iwr_flags < 0) { ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); return FALSE; } return TRUE; } /* Open a modem port. */ static boolean fsmodem_open (qconn, ibaud, fwait, fuser) struct sconnection *qconn; long ibaud; boolean fwait; boolean fuser; { struct uuconf_modem_port *qm; qm = &qconn->qport->uuconf_u.uuconf_smodem; if (ibaud == (long) 0) ibaud = qm->uuconf_ibaud; if (! fsserial_open (qconn, ibaud, fwait, fuser, fwait ? CLEAR_CLOCAL : SET_CLOCAL)) return FALSE; /* If we are waiting for carrier, then turn on hardware flow control. We don't turn on hardware flow control when dialing out, because some modems don't assert the necessary signals until they see carrier. Instead, we turn on hardware flow control in fsmodem_carrier. */ if (fwait && ! fsserial_hardflow (qconn, qm->uuconf_fhardflow)) return FALSE; return TRUE; } /* Open a direct port. */ static boolean fsdirect_open (qconn, ibaud, fwait, fuser) struct sconnection *qconn; long ibaud; boolean fwait; boolean fuser; { struct uuconf_direct_port *qd; qd = &qconn->qport->uuconf_u.uuconf_sdirect; if (ibaud == (long) 0) ibaud = qd->uuconf_ibaud; if (! fsserial_open (qconn, ibaud, fwait, fuser, qd->uuconf_fcarrier ? CLEAR_CLOCAL : SET_CLOCAL)) return FALSE; /* Always turn on hardware flow control for a direct port when it is opened. There is no other sensible time to turn it on. */ return fsserial_hardflow (qconn, qd->uuconf_fhardflow); } /* Change the blocking status of the port. We keep track of the current blocking status to avoid calling fcntl unnecessarily; fcntl turns out to be surprisingly expensive, at least on Ultrix. */ static boolean fsblock (qs, fblock) struct ssysdep_conn *qs; boolean fblock; { int iwant; int isys; if (fblock) iwant = qs->iflags &~ (O_NDELAY | O_NONBLOCK); else iwant = qs->iflags | iSunblock; if (iwant == qs->iflags) return TRUE; isys = fcntl (qs->o, F_SETFL, iwant); if (isys < 0) { #if O_NONBLOCK != 0 if (! fblock && iSunblock != O_NONBLOCK && errno == EINVAL) { iSunblock = O_NONBLOCK; iwant = qs->iflags | O_NONBLOCK; isys = fcntl (qs->o, F_SETFL, iwant); } #endif if (isys < 0) { ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); return FALSE; } } qs->iflags = iwant; if (qs->iwr_flags >= 0 && qs->ord != qs->owr) { if (fblock) iwant = qs->iwr_flags &~ (O_NDELAY | O_NONBLOCK); else iwant = qs->iwr_flags | iSunblock; isys = fcntl (qs->owr, F_SETFL, iwant); if (isys < 0) { #if O_NONBLOCK != 0 if (! fblock && iSunblock != O_NONBLOCK && errno == EINVAL) { iSunblock = O_NONBLOCK; iwant = qs->iwr_flags | O_NONBLOCK; isys = fcntl (qs->owr, F_SETFL, iwant); } #endif if (isys < 0) { ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); return FALSE; } } qs->iwr_flags = iwant; } return TRUE; } /* Close a serial port. */ static boolean fsserial_close (q) struct ssysdep_conn *q; { if (q->o >= 0) { /* Use a 30 second timeout to avoid hanging while draining output. */ if (q->fterminal) { fSalarm = FALSE; if (fsysdep_catch ()) { usysdep_start_catch (); usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); (void) alarm (30); (void) fsetterminfodrain (q->o, &q->sorig); } usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); (void) alarm (0); usysdep_end_catch (); /* If we timed out, use the non draining call. Hopefully this can't hang. */ if (fSalarm) (void) fsetterminfo (q->o, &q->sorig); } #ifdef TIOCNOTTY /* We don't want this as our controlling terminal any more, so get rid of it. This is necessary because we don't want to open /dev/tty, since that can confuse the serial port locking on some computers. */ (void) ioctl (q->o, TIOCNOTTY, (char *) NULL); #endif (void) close (q->o); q->o = -1; /* Sleep to give the terminal a chance to settle, in case we are about to call out again. */ sleep (2); } return TRUE; } /* Close a stdin port. */ /*ARGSUSED*/ static boolean fsstdin_close (qconn, puuconf, qdialer, fsuccess) struct sconnection *qconn; pointer puuconf ATTRIBUTE_UNUSED; struct uuconf_dialer *qdialer ATTRIBUTE_UNUSED; boolean fsuccess ATTRIBUTE_UNUSED; { struct ssysdep_conn *qsysdep; qsysdep = (struct ssysdep_conn *) qconn->psysdep; (void) close (qsysdep->owr); (void) close (2); qsysdep->o = qsysdep->ord; return fsserial_close (qsysdep); } /* Close a modem port. */ static boolean fsmodem_close (qconn, puuconf, qdialer, fsuccess) struct sconnection *qconn; pointer puuconf; struct uuconf_dialer *qdialer; boolean fsuccess; { struct ssysdep_conn *qsysdep; boolean fret; struct uuconf_dialer sdialer; const struct uuconf_chat *qchat; qsysdep = (struct ssysdep_conn *) qconn->psysdep; fret = TRUE; /* Figure out the dialer so that we can run the complete or abort chat scripts. */ if (qdialer == NULL) { if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL) { const char *zdialer; int iuuconf; zdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer[0]; iuuconf = uuconf_dialer_info (puuconf, zdialer, &sdialer); if (iuuconf == UUCONF_SUCCESS) qdialer = &sdialer; else { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); fret = FALSE; } } else qdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_qdialer; } /* Get the complete or abort chat script to use. */ qchat = NULL; if (qdialer != NULL) { if (fsuccess) qchat = &qdialer->uuconf_scomplete; else qchat = &qdialer->uuconf_sabort; } if (qchat != NULL && (qchat->uuconf_pzprogram != NULL || qchat->uuconf_pzchat != NULL)) { boolean fsighup_ignored; HELD_SIG_MASK smask; int i; sig_atomic_t afhold[INDEXSIG_COUNT]; /* We're no longer interested in carrier. */ (void) fsmodem_carrier (qconn, FALSE); /* The port I/O routines check whether any signal has been received, and abort if one has. While we are closing down the modem, we don't care if we received a signal in the past, but we do care if we receive a new signal (otherwise it would be difficult to kill a uucico which was closing down a modem). We never care if we get SIGHUP at this point. So we turn off SIGHUP, remember what signals we've already seen, and clear our notion of what signals we've seen. We have to block the signals while we remember and clear the array, since we might otherwise miss a signal which occurred between the copy and the clear (old systems can't block signals; they will just have to suffer the race). */ usset_signal (SIGHUP, SIG_IGN, FALSE, &fsighup_ignored); smask = isblocksigs (); for (i = 0; i < INDEXSIG_COUNT; i++) { afhold[i] = afSignal[i]; afSignal[i] = FALSE; } usunblocksigs (smask); if (! fchat (qconn, puuconf, qchat, (const struct uuconf_system *) NULL, (const struct uuconf_dialer *) NULL, (const char *) NULL, FALSE, qconn->qport->uuconf_zname, qsysdep->ibaud)) fret = FALSE; /* Restore the old signal array and the SIGHUP handler. It is not necessary to block signals here, since all we are doing is exactly what the signal handler itself would do if the signal occurred. */ for (i = 0; i < INDEXSIG_COUNT; i++) if (afhold[i]) afSignal[i] = TRUE; if (! fsighup_ignored) usset_signal (SIGHUP, ussignal, TRUE, (boolean *) NULL); } if (qdialer != NULL && qdialer == &sdialer) (void) uuconf_dialer_free (puuconf, &sdialer); #if ! HAVE_RESET_BUG /* Reset the terminal to make sure we drop DTR. It should be dropped when we close the descriptor, but that doesn't seem to happen on some systems. Use a 30 second timeout to avoid hanging while draining output. */ if (qsysdep->fterminal) { #if HAVE_BSD_TTY qsysdep->snew.stty.sg_ispeed = B0; qsysdep->snew.stty.sg_ospeed = B0; #endif #if HAVE_SYSV_TERMIO qsysdep->snew.c_cflag = (qsysdep->snew.c_cflag &~ CBAUD) | B0; #endif #if HAVE_POSIX_TERMIOS (void) cfsetospeed (&qsysdep->snew, B0); #endif fSalarm = FALSE; if (fsysdep_catch ()) { usysdep_start_catch (); usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); (void) alarm (30); (void) fsetterminfodrain (qsysdep->o, &qsysdep->snew); } usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); (void) alarm (0); usysdep_end_catch (); /* Let the port settle. */ sleep (2); } #endif /* ! HAVE_RESET_BUG */ if (! fsserial_close (qsysdep)) fret = FALSE; return fret; } /* Close a direct port. */ /*ARGSUSED*/ static boolean fsdirect_close (qconn, puuconf, qdialer, fsuccess) struct sconnection *qconn; pointer puuconf ATTRIBUTE_UNUSED; struct uuconf_dialer *qdialer ATTRIBUTE_UNUSED; boolean fsuccess ATTRIBUTE_UNUSED; { return fsserial_close ((struct ssysdep_conn *) qconn->psysdep); } /* Begin dialing out on a modem port. This opens the dialer device if there is one. */ boolean fsysdep_modem_begin_dial (qconn, qdial) struct sconnection *qconn; struct uuconf_dialer *qdial; { struct ssysdep_conn *qsysdep; const char *z; qsysdep = (struct ssysdep_conn *) qconn->psysdep; #ifdef TIOCMODEM /* If we can tell the modem to obey modem control, do so. */ { int iperm; iperm = 0; (void) ioctl (qsysdep->o, TIOCMODEM, &iperm); } #endif /* TIOCMODEM */ /* If we supposed to toggle DTR, do so. */ if (qdial->uuconf_fdtr_toggle) { #ifdef TIOCCDTR (void) ioctl (qsysdep->o, TIOCCDTR, 0); sleep (2); (void) ioctl (qsysdep->o, TIOCSDTR, 0); #else /* ! defined (TIOCCDTR) */ if (qsysdep->fterminal) { sterminal sbaud; sbaud = qsysdep->snew; #if HAVE_BSD_TTY sbaud.stty.sg_ispeed = B0; sbaud.stty.sg_ospeed = B0; #endif #if HAVE_SYSV_TERMIO sbaud.c_cflag = (sbaud.c_cflag &~ CBAUD) | B0; #endif #if HAVE_POSIX_TERMIOS (void) cfsetospeed (&sbaud, B0); #endif (void) fsetterminfodrain (qsysdep->o, &sbaud); sleep (2); (void) fsetterminfo (qsysdep->o, &qsysdep->snew); } #endif /* ! defined (TIOCCDTR) */ if (qdial->uuconf_fdtr_toggle_wait) sleep (2); } if (! fsmodem_carrier (qconn, FALSE)) return FALSE; /* Open the dial device if there is one. */ z = qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdial_device; if (z != NULL) { char *zfree; int o; qsysdep->ohold = qsysdep->o; zfree = NULL; if (*z != '/') { zfree = zbufalc (sizeof "/dev/" + strlen (z)); sprintf (zfree, "/dev/%s", z); z = zfree; } o = open ((char *) z, O_RDWR | O_NOCTTY); if (o < 0) { ulog (LOG_ERROR, "open (%s): %s", z, strerror (errno)); ubuffree (zfree); return FALSE; } ubuffree (zfree); if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); (void) close (o); return FALSE; } qsysdep->o = o; } return TRUE; } /* Tell the port to require or not require carrier. On BSD this uses TIOCCAR and TIOCNCAR, which I assume are generally supported (it can also use the LNOMDM bit supported by IS68K Unix). On System V it resets or sets CLOCAL. We only require carrier if the port supports it. This will only be called with fcarrier TRUE if the dialer supports carrier. */ static boolean fsmodem_carrier (qconn, fcarrier) struct sconnection *qconn; boolean fcarrier; { register struct ssysdep_conn *q; struct uuconf_modem_port *qm; q = (struct ssysdep_conn *) qconn->psysdep; if (! q->fterminal) return TRUE; qm = &qconn->qport->uuconf_u.uuconf_smodem; if (fcarrier) { if (qm->uuconf_fcarrier) { #ifdef TIOCCAR /* Tell the modem to pay attention to carrier. */ if (ioctl (q->o, TIOCCAR, 0) < 0) { ulog (LOG_ERROR, "ioctl (TIOCCAR): %s", strerror (errno)); return FALSE; } #endif /* TIOCCAR */ #if HAVE_BSD_TTY #ifdef LNOMDM /* IS68K Unix uses a local LNOMDM bit. */ { int iparam; iparam = LNOMDM; if (ioctl (q->o, TIOCLBIC, &iparam) < 0) { ulog (LOG_ERROR, "ioctl (TIOCLBIC, LNOMDM): %s", strerror (errno)); return FALSE; } } #endif /* LNOMDM */ #endif /* HAVE_BSD_TTY */ #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS /* Put the modem into nonlocal mode. */ q->snew.c_cflag &=~ CLOCAL; if (! fsetterminfo (q->o, &q->snew)) { ulog (LOG_ERROR, "Can't clear CLOCAL: %s", strerror (errno)); return FALSE; } #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ } /* Turn on hardware flow control after turning on carrier. We don't do it until now because some modems don't assert the right signals until they see carrier. */ if (! fsserial_hardflow (qconn, qm->uuconf_fhardflow)) return FALSE; } else { /* Turn off any hardware flow control before turning off carrier. */ if (! fsserial_hardflow (qconn, FALSE)) return FALSE; #ifdef TIOCNCAR /* Tell the modem to ignore carrier. */ if (ioctl (q->o, TIOCNCAR, 0) < 0) { ulog (LOG_ERROR, "ioctl (TIOCNCAR): %s", strerror (errno)); return FALSE; } #endif /* TIOCNCAR */ #if HAVE_BSD_TTY #ifdef LNOMDM /* IS68K Unix uses a local LNOMDM bit. */ { int iparam; iparam = LNOMDM; if (ioctl (q->o, TIOCLBIS, &iparam) < 0) { ulog (LOG_ERROR, "ioctl (TIOCLBIS, LNOMDM): %s", strerror (errno)); return FALSE; } } #endif /* LNOMDM */ #endif /* HAVE_BSD_TTY */ #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS /* Put the modem into local mode (ignore carrier) to start the chat script. */ q->snew.c_cflag |= CLOCAL; if (! fsetterminfo (q->o, &q->snew)) { ulog (LOG_ERROR, "Can't set CLOCAL: %s", strerror (errno)); return FALSE; } #if HAVE_CLOCAL_BUG /* On SCO and AT&T UNIX PC you have to reopen the port. */ { int onew; onew = open (q->zdevice, O_RDWR); if (onew < 0) { ulog (LOG_ERROR, "open (%s): %s", q->zdevice, strerror (errno)); return FALSE; } if (fcntl (onew, F_SETFD, fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); (void) close (onew); return FALSE; } (void) close (q->o); q->o = onew; } #endif /* HAVE_CLOCAL_BUG */ #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ } return TRUE; } /* Tell the port to use hardware flow control. There is no standard mechanism for controlling this. This implementation supports CRTSCTS and CRTSXOFF on SunOS/Solaris, RTS/CTSFLOW on 386(ish) unix, CTSCD on the 3b1, CCTS_OFLOW/CRTS_IFLOW on BSDI, TXADDCD/TXDELCD on AIX, IRTS on NCR Tower, and TCGETX/TCSETX on HP/UX. If you know how to do it on other systems, please implement it and send me the patches. */ static boolean fsserial_hardflow (qconn, fhardflow) struct sconnection *qconn; boolean fhardflow; { register struct ssysdep_conn *q; q = (struct ssysdep_conn *) qconn->psysdep; if (! q->fterminal) return TRUE; /* Don't do anything if we don't know what to do. */ #if HAVE_BSD_TTY #define HAVE_HARDFLOW 0 #endif #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS #if ! HAVE_TXADDCD #ifndef CRTSFL #ifndef CRTSCTS #ifndef CTSCD #ifndef CCTS_OFLOW #ifndef IRTS #define HAVE_HARDFLOW 0 #endif #endif #endif #endif #endif #endif #endif #ifndef HAVE_HARDFLOW #define HAVE_HARDFLOW 1 #endif #if HAVE_HARDFLOW if (fhardflow) { #if HAVE_TXADDCD /* The return value does not reliably indicate whether this actually succeeded. */ (void) ioctl (q->o, TXADDCD, "rts"); #else /* ! HAVE_TXADDCD */ #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS #ifdef CRTSFL q->snew.c_cflag |= CRTSFL; q->snew.c_cflag &=~ (RTSFLOW | CTSFLOW); #endif /* defined (CRTSFL) */ #ifdef CRTSCTS q->snew.c_cflag |= CRTSCTS; #endif /* defined (CRTSCTS) */ #ifdef CRTSXOFF q->snew.c_cflag |= CRTSXOFF; #endif /* defined (CRTSXOFF) */ #ifdef CTSCD q->snew.c_cflag |= CTSCD; #endif /* defined (CTSCD) */ #ifdef CCTS_OFLOW q->snew.c_cflag |= CCTS_OFLOW | CRTS_IFLOW; #endif #ifdef IRTS q->snew.c_iflag |= IRTS; #endif #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ if (! fsetterminfo (q->o, &q->snew)) { ulog (LOG_ERROR, "Can't enable hardware flow control: %s", strerror (errno)); return FALSE; } #if HAVE_SYS_TERMIOX #ifdef TCGETX { struct termiox tx; if (ioctl (q->o, TCGETX, &tx) < 0) { ulog (LOG_ERROR, "Can't enable hardware flow control: ioctl (TCGETX): %s", strerror (errno)); return FALSE; } tx.x_hflag |= RTSXOFF | CTSXON; if (ioctl (q->o, TCSETX, &tx) < 0) { ulog (LOG_ERROR, "Can't enable hardware flow control: ioctl (TCSETX): %s", strerror (errno)); return FALSE; } } #endif /* TCGETX */ #endif /* HAVE_SYS_TERMIOX */ #endif /* ! HAVE_TXADDCD */ } else { #if HAVE_TXADDCD /* The return value does not reliably indicate whether this actually succeeded. */ (void) ioctl (q->o, TXDELCD, "rts"); #else /* ! HAVE_TXADDCD */ #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS #ifdef CRTSFL q->snew.c_cflag &=~ CRTSFL; q->snew.c_cflag &=~ (RTSFLOW | CTSFLOW); #endif /* defined (CRTSFL) */ #ifdef CRTSCTS q->snew.c_cflag &=~ CRTSCTS; #endif /* defined (CRTSCTS) */ #ifdef CRTSXOFF q->snew.c_cflag &=~ CRTSXOFF; #endif /* defined (CRTSXOFF) */ #ifdef CTSCD q->snew.c_cflag &=~ CTSCD; #endif /* defined (CTSCD) */ #ifdef CCTS_OFLOW q->snew.c_cflag &=~ (CCTS_OFLOW | CRTS_IFLOW); #endif #ifdef IRTS q->snew.c_iflag &=~ IRTS; #endif #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ if (! fsetterminfo (q->o, &q->snew)) { ulog (LOG_ERROR, "Can't disable hardware flow control: %s", strerror (errno)); return FALSE; } #if HAVE_SYS_TERMIOX #ifdef TCGETX { struct termiox tx; if (ioctl (q->o, TCGETX, &tx) < 0) { ulog (LOG_ERROR, "Can't disable hardware flow control: ioctl (TCGETX): %s", strerror (errno)); return FALSE; } tx.x_hflag &=~ (RTSXOFF | CTSXON); if (ioctl (q->o, TCSETX, &tx) < 0) { ulog (LOG_ERROR, "Can't disable hardware flow control: ioctl (TCSETX): %s", strerror (errno)); return FALSE; } } #endif /* TCGETX */ #endif /* HAVE_SYS_TERMIOX */ #endif /* ! HAVE_TXADDCD */ } #endif /* HAVE_HARDFLOW */ return TRUE; } /* Finish dialing out on a modem by closing any dialer device and waiting for carrier. */ boolean fsysdep_modem_end_dial (qconn, qdial) struct sconnection *qconn; struct uuconf_dialer *qdial; { struct ssysdep_conn *q; q = (struct ssysdep_conn *) qconn->psysdep; if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdial_device != NULL) { (void) close (q->o); q->o = q->ohold; } if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_fcarrier && qdial->uuconf_fcarrier) { /* Tell the port that we need carrier. */ if (! fsmodem_carrier (qconn, TRUE)) return FALSE; #ifdef TIOCWONLINE /* We know how to wait for carrier, so do so. */ /* If we already got a signal, just quit now. */ if (FGOT_QUIT_SIGNAL ()) return FALSE; /* This bit of code handles signals just like fsysdep_conn_read does. See that function for a longer explanation. */ /* Use fsysdep_catch to handle a longjmp from the signal handler. */ fSalarm = FALSE; if (fsysdep_catch ()) { /* Start catching SIGALRM; normally we ignore it. */ usysdep_start_catch (); usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); (void) alarm (qdial->uuconf_ccarrier_wait); /* We really don't care if we get an error, since that will probably just mean that TIOCWONLINE isn't supported in which case there's nothing we can do anyhow. If we get SIGINT we want to keep waiting for carrier, because SIGINT just means don't start any new sessions. We don't handle SIGINT correctly if we do a longjmp in the signal handler; too bad. */ while (ioctl (q->o, TIOCWONLINE, 0) < 0 && errno == EINTR) { /* Log the signal. */ ulog (LOG_ERROR, (const char *) NULL); if (FGOT_QUIT_SIGNAL () || fSalarm) break; } } /* Turn off the pending SIGALRM and ignore SIGALARM again. */ usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); (void) alarm (0); usysdep_end_catch (); /* If we got a random signal, just return FALSE. */ if (FGOT_QUIT_SIGNAL ()) return FALSE; /* If we timed out, give an error. */ if (fSalarm) { ulog (LOG_ERROR, "Timed out waiting for carrier"); return FALSE; } #else /* ! defined (TIOCWONLINE) */ /* Try to open the port again without using O_NDELAY. In principle, the open should delay until carrier is available. This may not work on some systems, so we just ignore any errors. */ { int onew; onew = open (q->zdevice, O_RDWR); if (onew >= 0) { boolean fbad; int iflags; fbad = FALSE; if (fcntl (onew, F_SETFD, fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0) fbad = TRUE; if (! fbad) { iflags = fcntl (onew, F_GETFL, 0); if (iflags < 0 || ! fsetterminfo (onew, &q->snew)) fbad = TRUE; } if (fbad) (void) close (onew); else { (void) close (q->o); q->o = onew; q->iflags = iflags; #if HAVE_TIOCSINUSE (void) ioctl (onew, TIOCSINUSE, 0); #endif } } } #endif /* ! defined (TIOCWONLINE) */ } return TRUE; } /* Read data from a connection, with a timeout. This routine handles all types of connections, including TLI. This function should return when we have read cmin characters or the timeout has occurred. We have to work a bit to get Unix to do this efficiently on a terminal. The simple implementation schedules a SIGALRM signal and then calls read; if there is a single character available, the call to read will return immediately, so there must be a loop which terminates when the SIGALRM is delivered or the correct number of characters has been read. This can be very inefficient with a fast CPU or a low baud rate (or both!), since each call to read may return only one or two characters. Under POSIX or System V, we can specify a minimum number of characters to read, so there is no serious trouble. Under BSD, we figure out how many characters we have left to read, how long it will take for them to arrive at the current baud rate, and sleep that long. Doing this with a timeout and avoiding all possible race conditions get very hairy, though. Basically, we're going to schedule a SIGALRM for when the timeout expires. I don't really want to do a longjmp in the SIGALRM handler, though, because that may lose data. Therefore, I have the signal handler set a variable. However, this means that there will be a span of time between the time the code checks the variable and the time it calls the read system call; if the SIGALRM occurs during that time, the read might hang forever. To avoid this, the SIGALRM handler not only sets a global variable, it also schedules another SIGALRM for one second in the future (POSIX specifies that a signal handler is permitted to safely call alarm). To avoid getting a continual sequence of SIGALRM interrupts, we change the signal handler to ignore SIGALRM when we're about to exit the function. This means that every time we execute fsysdep_conn_read we make at least five system calls. It's the best I've been able to come up with, though. When fsysdep_conn_read finishes, there will be no SIGALRM scheduled and SIGALRM will be ignored. */ boolean fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport) struct sconnection *qconn; char *zbuf; size_t *pclen; size_t cmin; int ctimeout; boolean freport; { CATCH_PROTECT size_t cwant; boolean fret; register struct ssysdep_conn * const q = (struct ssysdep_conn *) qconn->psysdep; int cwouldblock; cwant = *pclen; *pclen = 0; /* Guard against a bad timeout. We return TRUE when a timeout expires. It is possible to get a negative timeout here because the calling code does not check user supplied timeouts for plausibility. */ if (ctimeout <= 0) return TRUE; /* We want to do a blocking read. */ if (! fsblock (q, TRUE)) return FALSE; fSalarm = FALSE; /* We're going to set up an alarm signal to last for the entire read. If the read system call cannot be interrupted, the signal handler will do a longjmp causing fsysdep_catch (a macro) to return FALSE. We handle that here. If read can be interrupted, fsysdep_catch will be defined to TRUE. */ if (fsysdep_catch ()) { /* Prepare to catch SIGALRM and schedule the signal. */ usysdep_start_catch (); usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); alarm (ctimeout); } else { /* We caught a signal. We don't actually have to do anything, as all the appropriate checks are made at the start of the following loop. */ } fret = FALSE; cwouldblock = 0; while (TRUE) { int cgot; #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS /* If we can tell the terminal not to return until we have a certain number of characters, do so. */ if (q->fterminal) { int csetmin; /* I'm not that confident about setting MIN to values larger than 127, although up to 255 would probably work. */ if (cmin < 127) csetmin = cmin; else csetmin = 127; if (csetmin != cSmin) { q->snew.c_cc[VMIN] = csetmin; while (! fsetterminfo (q->o, &q->snew)) { if (errno != EINTR || FGOT_QUIT_SIGNAL ()) { int ierr; /* We turn off the signal before reporting the error to minimize any problems with interrupted system calls. */ ierr = errno; usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); alarm (0); usysdep_end_catch (); ulog (LOG_ERROR, "Can't set MIN for terminal: %s", strerror (ierr)); return FALSE; } if (fSalarm) { ulog (LOG_ERROR, "Timed out when setting MIN to %d; retrying", csetmin); fSalarm = FALSE; alarm (ctimeout); } } cSmin = csetmin; } } #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ /* If we've received a signal, get out now. */ if (FGOT_QUIT_SIGNAL ()) break; /* If we've already gotten a SIGALRM, get out with whatever we've accumulated. */ if (fSalarm) { fret = TRUE; break; } /* Right here is the race condition which we avoid by having the SIGALRM handler schedule another SIGALRM. */ #if HAVE_TLI if (q->ftli) { int iflags; cgot = t_rcv (q->o, zbuf, cwant, &iflags); if (cgot < 0 && t_errno != TSYSERR) { usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); alarm (0); usysdep_end_catch (); if (freport) ulog (LOG_ERROR, "t_rcv: %s", (t_errno >= 0 && t_errno < t_nerr ? t_errlist[t_errno] : "unknown TLI error")); return FALSE; } } else #endif cgot = read (q->o, zbuf, cwant); /* If the read returned an error, check for signals. */ if (cgot < 0) { if (errno == EINTR) { /* Log the signal. */ ulog (LOG_ERROR, (const char *) NULL); } if (fSalarm) { fret = TRUE; break; } if (FGOT_QUIT_SIGNAL ()) break; } /* If read returned an error, get out. We just ignore EINTR here, since it must be from some signal we don't care about. If the read returned 0 then the line must have been hung up (normally we would have received SIGHUP, but we can't count on that). We turn off the signals before calling ulog to reduce problems with interrupted system calls. */ if (cgot > 0) cwouldblock = 0; else { if (cgot < 0 && errno == EINTR) cgot = 0; else if (cgot < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) && cwouldblock < 2) { /* Incomprehensibly, on some systems the read will return EWOULDBLOCK even though the descriptor has been set to blocking mode. We permit the read call to do this twice in a row, and then error out. We don't want to permit an arbitrary number of EWOULDBLOCK errors, since that could hang us up indefinitely. */ ++cwouldblock; cgot = 0; } else { int ierr; ierr = errno; usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); alarm (0); usysdep_end_catch (); if (freport) { if (cgot == 0) ulog (LOG_ERROR, "Line disconnected"); else ulog (LOG_ERROR, "read: %s", strerror (ierr)); } return FALSE; } } cwant -= cgot; if ((size_t) cgot >= cmin) cmin = 0; else cmin -= cgot; zbuf += cgot; *pclen += cgot; /* If we have enough data, get out now. */ if (cmin == 0) { fret = TRUE; break; } #if HAVE_BSD_TTY /* We still want more data, so sleep long enough for the rest of it to arrive. We don't this for System V or POSIX because setting MIN is good enough (we can't sleep longer than it takes to get MAX_INPUT characters anyhow). The baud rate is approximately 10 times the number of characters which will arrive in one second, so the number of milliseconds to sleep == characters * (milliseconds / character) == characters * (1000 * (seconds / character)) == characters * (1000 * (1 / (baud / 10))) == characters * (10000 / baud) We arbitrarily reduce the sleep amount by 10 milliseconds to attempt to account for the amount of time it takes to set up the sleep. This is how long it takes to get half a character at 19200 baud. We then don't bother to sleep for less than 10 milliseconds. We don't sleep if the read was interrupted. We use select to sleep. It would be easy to use poll as well, but it's unlikely that any system with BSD ttys would have poll but not select. Using select avoids hassles with the pending SIGALRM; if it hits the select will be interrupted, and otherwise the select will not affect it. */ #if ! HAVE_SELECT #error This code requires select; feel free to extend it #endif if (q->fterminal && cmin > 1 && cgot > 0) { int csleepchars; int isleep; /* We don't try to read all the way up to MAX_INPUT, since that might drop a character. */ if (cmin <= MAX_INPUT - 10) csleepchars = cmin; else csleepchars = MAX_INPUT - 10; isleep = (int) (((long) csleepchars * 10000L) / q->ibaud); isleep -= 10; if (isleep > 10) { struct timeval s; s.tv_sec = isleep / 1000; s.tv_usec = (isleep % 1000) * 1000; /* Some versions of select take a pointer to an int, while some take a pointer to an fd_set. I just cast the arguments to a generic pointer, and assume that any machine which distinguishes int * from fd_set * (I would be amazed if there are any such machines) have an appropriate prototype somewhere or other. */ (void) select (0, (pointer) NULL, (pointer) NULL, (pointer) NULL, &s); /* Here either the select finished sleeping or we got a SIGALRM. If the latter occurred, fSalarm was set to TRUE; it will be checked at the top of the loop. */ } } #endif /* HAVE_BSD_TTY */ } /* Turn off the pending SIGALRM and return. */ usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); alarm (0); usysdep_end_catch (); return fret; } /* Read from a port with separate read/write file descriptors. */ boolean fsdouble_read (qconn, zbuf, pclen, cmin, ctimeout, freport) struct sconnection *qconn; char *zbuf; size_t *pclen; size_t cmin; int ctimeout; boolean freport; { struct ssysdep_conn *qsysdep; qsysdep = (struct ssysdep_conn *) qconn->psysdep; qsysdep->o = qsysdep->ord; return fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport); } /* Write data to a connection. This routine handles all types of connections, including TLI. */ boolean fsysdep_conn_write (qconn, zwrite, cwrite) struct sconnection *qconn; const char *zwrite; size_t cwrite; { struct ssysdep_conn *q; int czero; q = (struct ssysdep_conn *) qconn->psysdep; /* We want blocking writes here. */ if (! fsblock (q, TRUE)) return FALSE; czero = 0; while (cwrite > 0) { int cdid; /* Loop until we don't get an interrupt. */ while (TRUE) { /* If we've received a signal, don't continue. */ if (FGOT_QUIT_SIGNAL ()) return FALSE; #if HAVE_TLI if (q->ftli) { cdid = t_snd (q->o, (char *) zwrite, cwrite, 0); if (cdid < 0 && t_errno != TSYSERR) { ulog (LOG_ERROR, "t_snd: %s", (t_errno >= 0 && t_errno < t_nerr ? t_errlist[t_errno] : "unknown TLI error")); return FALSE; } } else #endif cdid = write (q->o, zwrite, cwrite); if (cdid >= 0) break; if (errno != EINTR) break; /* We were interrupted by a signal. Log it. */ ulog (LOG_ERROR, (const char *) NULL); } if (cdid < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA) { ulog (LOG_ERROR, "write: %s", strerror (errno)); return FALSE; } cdid = 0; } if (cdid == 0) { /* On some systems write will return 0 if carrier is lost. If we fail to write anything ten times in a row, we assume that this has happened. This is hacked in like this because there seems to be no reliable way to tell exactly why the write returned 0. */ ++czero; if (czero >= 10) { ulog (LOG_ERROR, "Line disconnected"); return FALSE; } } else { czero = 0; cwrite -= cdid; zwrite += cdid; } } return TRUE; } /* Write to a port with separate read/write file descriptors. */ boolean fsdouble_write (qconn, zwrite, cwrite) struct sconnection *qconn; const char *zwrite; size_t cwrite; { struct ssysdep_conn *qsysdep; qsysdep = (struct ssysdep_conn *) qconn->psysdep; qsysdep->o = qsysdep->ord; if (! fsblock (qsysdep, TRUE)) return FALSE; qsysdep->o = qsysdep->owr; return fsysdep_conn_write (qconn, zwrite, cwrite); } /* The fsysdep_conn_io routine is supposed to both read and write data until it has either filled its read buffer or written out all the data it was given. This lets us write out large packets without losing incoming data. It handles all types of connections, including TLI. */ boolean fsysdep_conn_io (qconn, zwrite, pcwrite, zread, pcread) struct sconnection *qconn; const char *zwrite; size_t *pcwrite; char *zread; size_t *pcread; { struct ssysdep_conn *q; size_t cwrite, cread; int czero; q = (struct ssysdep_conn *) qconn->psysdep; cwrite = *pcwrite; *pcwrite = 0; cread = *pcread; *pcread = 0; czero = 0; while (TRUE) { int cgot, cdid; size_t cdo; /* This used to always use nonblocking writes, but it turns out that some systems don't support them on terminals. The current algorithm is: loop: unblocked read if read buffer full, return if nothing to write, return if HAVE_UNBLOCKED_WRITES write all data else write up to SINGLE_WRITE bytes if all data written, return if no data written if select works select on the write descriptor with a ten second timeout else blocked write of one byte with a ten second alarm This algorithm should work whether the system supports unblocked writes on terminals or not. If the system supports unblocked writes but HAVE_UNBLOCKED_WRITES is 0, then it will call write more often than it needs to. If the system does not support unblocked writes but HAVE_UNBLOCKED_WRITES is 1, then the write may hang so long that incoming data is lost. This is actually possible at high baud rates on any system when a blocking write is done; there is no solution, except hardware handshaking. If we were not able to write any data, then we need to block until we can write something. The code used to simply do a blocking write. However, that fails when a bidirectional protocol is permitted to push out enough bytes to fill the entire pipe between the two communicating uucico processes. They can both block on writing, because neither is reading. In this case, we use select. We could select on both the read and write descriptor, but on some systems that would lead to calling read on each byte, which would be very inefficient. Instead, we select only on the write descriptor. After the select succeeds or times out, we retry the read. Of course, some systems don't have select, and on some systems that have it it doesn't work on terminal devices. If we can't use select, then we do a blocked write of a single byte after setting an alarm. We only write a single byte to avoid any confusion as to whether or not the byte was actually written. */ /* If we are running on standard input, we switch the file descriptors by hand. */ if (q->ord >= 0) q->o = q->ord; /* Do an unblocked read. */ if (! fsblock (q, FALSE)) return FALSE; /* Loop until we get something (error or data) other than an acceptable EINTR. */ while (TRUE) { /* If we've received a signal, don't continue. */ if (FGOT_QUIT_SIGNAL ()) return FALSE; #if HAVE_TLI if (q->ftli) { int iflags; cgot = t_rcv (q->o, zread, cread, &iflags); if (cgot < 0) { if (t_errno == TNODATA) errno = EAGAIN; else if (t_errno != TSYSERR) { ulog (LOG_ERROR, "t_rcv: %s", (t_errno >= 0 && t_errno < t_nerr ? t_errlist[t_errno] : "unknown TLI error")); return FALSE; } } } else #endif cgot = read (q->o, zread, cread); if (cgot >= 0) break; if (errno != EINTR) break; /* We got interrupted by a signal. Log it. */ ulog (LOG_ERROR, (const char *) NULL); } if (cgot < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA) { ulog (LOG_ERROR, "read: %s", strerror (errno)); return FALSE; } cgot = 0; } cread -= cgot; zread += cgot; *pcread += cgot; /* If we've filled the read buffer, or we have nothing left to write, return out. */ if (cread == 0 || cwrite == 0) return TRUE; /* The port is currently unblocked. Do a write. */ cdo = cwrite; #if ! HAVE_UNBLOCKED_WRITES if (q->fterminal && cdo > SINGLE_WRITE) cdo = SINGLE_WRITE; #endif if (q->owr >= 0) q->o = q->owr; /* Loop until we get something besides EINTR. */ while (TRUE) { /* If we've received a signal, don't continue. */ if (FGOT_QUIT_SIGNAL ()) return FALSE; #if HAVE_TLI if (q->ftli) { cdid = t_snd (q->o, (char *) zwrite, cdo, 0); if (cdid < 0) { if (t_errno == TFLOW) errno = EAGAIN; else if (t_errno != TSYSERR) { ulog (LOG_ERROR, "t_snd: %s", (t_errno >= 0 && t_errno < t_nerr ? t_errlist[t_errno] : "unknown TLI error")); return FALSE; } } } else #endif cdid = write (q->o, zwrite, cdo); if (cdid >= 0) break; if (errno != EINTR) break; /* We got interrupted by a signal. Log it. */ ulog (LOG_ERROR, (const char *) NULL); } if (cdid < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA) { ulog (LOG_ERROR, "write: %s", strerror (errno)); return FALSE; } cdid = 0; } if (cdid > 0) { /* We wrote some data. If we wrote everything, return out. Otherwise loop around and do another read. */ cwrite -= cdid; zwrite += cdid; *pcwrite += cdid; if (cwrite == 0) return TRUE; czero = 0; } else { #if HAVE_SELECT struct timeval stime; #ifdef FD_ZERO fd_set smask; #else int smask; #endif int c; /* We didn't write any data. Call select. We use a timeout long enough for 1024 bytes to be sent. But we don't wait longer than the times it takes to receive cread bytes, in case our read buffer is small. secs/kbyte == (1024 bytes/kbyte * 10 bits/byte) / baud bits/sec usecs/kbyte == (((1024 bytes/kbyte * 1000000 usecs/sec) / baud bits/sec) * 10 bits/byte) */ if (q->fterminal) { unsigned long cwait; cwait = 1024; if (cwait > cread) cwait = cread; stime.tv_sec = (cwait * 10) / q->ibaud; stime.tv_usec = ((((cwait * 1000000) / q->ibaud) * 10) % 1000000); } else { /* This is some sort of network connection. We can't estimate how long it will take to write data. It also doesn't matter as much, as most systems will buffer much more incoming network data than they will incoming serial data. Sleep for a second, although normally the select will return sonner because we can write more data. */ stime.tv_sec = 1; stime.tv_usec = 0; } #ifdef FD_ZERO FD_ZERO (&smask); FD_SET (q->o, &smask); #else smask = 1 << q->o; if (smask == 0) ulog (LOG_FATAL, "fsysdep_conn_io: File descriptors too large"); #endif /* If we've received a signal, don't continue. */ if (FGOT_QUIT_SIGNAL ()) return FALSE; DEBUG_MESSAGE0 (DEBUG_PORT, "fsysdep_conn_io: Calling select"); /* We don't bother to loop on EINTR. If we get a signal, we just loop around and try the read and write again. */ c = select (q->o + 1, (pointer) NULL, (pointer) &smask, (pointer) NULL, &stime); if (c < 0 && errno == EINTR) { /* We got interrupted by a signal. Log it. */ ulog (LOG_ERROR, (const char *) NULL); } else if (c >= 0) { /* The select either discovered that we could write something, or it timed out. Either way, we go around the main read/write loop again. */ } else #endif /* HAVE_SELECT */ { int ierr; /* Either the select failed for some reason other than EINTR, or the system does not support select at all. Fall back on a timed write. We don't worry about why the select might have failed, we just assume that it will not succeed on this descriptor. */ #if HAVE_RESTARTABLE_SYSCALLS /* If HAVE_RESTARTABLE_SYSCALLS, then receiving an alarm signal in the middle of a write will not cause the write to return EINTR, and the only way to interrupt the write is to longjmp out of it (see sysh.unx). That is unreliable, because it means that we won't know whether the byte was actually written or not. However, I believe that the only system on which we need to do this longjmp is BSD 4.2, and that system supports select, so we should never execute this case. */ ulog (LOG_FATAL, "fsysdep_conn_io: Unsupported case; see code"); #endif if (q->ord >= 0) q->o = q->ord; if (! fsblock (q, TRUE)) return FALSE; DEBUG_MESSAGE0 (DEBUG_PORT, "fsysdep_conn_io: Blocking write"); if (q->owr >= 0) q->o = q->owr; /* If we've received a signal, don't continue. */ if (FGOT_QUIT_SIGNAL ()) return FALSE; /* Start up an alarm to interrupt the write. Note that we don't need to use the catch stuff, since we know that HAVE_RESTARTABLE_SYSCALLS is 0. */ usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); if (q->fterminal) alarm ((int) ((long) 10240 / q->ibaud) + 1); else alarm (1); /* There is a race condition here: on a severely loaded system, we could get the alarm before we start the write call. This would not be a disaster; often the write will succeed anyhow. */ #if HAVE_TLI if (q->ftli) { cdid = t_snd (q->o, (char *) zwrite, 1, 0); if (cdid < 0 && t_errno != TSYSERR) { usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); alarm (0); ulog (LOG_ERROR, "t_snd: %s", (t_errno >= 0 && t_errno < t_nerr ? t_errlist[t_errno] : "unknown TLI error")); return FALSE; } } else #endif cdid = write (q->o, zwrite, 1); ierr = errno; /* Note that we don't really care whether the write finished because the byte was written out or whether it finished because the alarm was triggered. Either way, we are going to loop around and try another read. */ usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); alarm (0); if (cdid < 0) { if (ierr == EINTR) { /* We got interrupted by a signal. Log it. */ ulog (LOG_ERROR, (const char *) NULL); } else { ulog (LOG_ERROR, "write: %s", strerror (ierr)); return FALSE; } } else if (cdid == 0) { /* On some systems write will return 0 if carrier is lost. If we fail to write anything ten times in a row, we assume that this has happened. This is hacked in like this because there seems to be no reliable way to tell exactly why the write returned 0. */ ++czero; if (czero >= 10) { ulog (LOG_ERROR, "Line disconnected"); return FALSE; } } else { cwrite -= cdid; zwrite += cdid; *pcwrite += cdid; czero = 0; } } } } } /* Send a break character to a serial port. */ static boolean fsserial_break (qconn) struct sconnection *qconn; { struct ssysdep_conn *q; q = (struct ssysdep_conn *) qconn->psysdep; #if HAVE_BSD_TTY (void) ioctl (q->o, TIOCSBRK, 0); sleep (2); (void) ioctl (q->o, TIOCCBRK, 0); return TRUE; #endif /* HAVE_BSD_TTY */ #if HAVE_SYSV_TERMIO (void) ioctl (q->o, TCSBRK, 0); return TRUE; #endif /* HAVE_SYSV_TERMIO */ #if HAVE_POSIX_TERMIOS return tcsendbreak (q->o, 0) == 0; #endif /* HAVE_POSIX_TERMIOS */ } /* Send a break character to a stdin port. */ static boolean fsstdin_break (qconn) struct sconnection *qconn; { struct ssysdep_conn *qsysdep; qsysdep = (struct ssysdep_conn *) qconn->psysdep; qsysdep->o = qsysdep->owr; return fsserial_break (qconn); } /* Change the setting of a serial port. */ /*ARGSUSED*/ static boolean fsserial_set (qconn, tparity, tstrip, txonxoff) struct sconnection *qconn; enum tparitysetting tparity; enum tstripsetting tstrip; enum txonxoffsetting txonxoff; { register struct ssysdep_conn *q; boolean fchanged, fdo; unsigned int iset = 0; unsigned int iclear = 0; q = (struct ssysdep_conn *) qconn->psysdep; if (! q->fterminal) return TRUE; fchanged = FALSE; /* Set the parity for output characters. */ #if HAVE_BSD_TTY /* This will also cause parity detection on input characters. */ fdo = FALSE; switch (tparity) { case PARITYSETTING_DEFAULT: break; case PARITYSETTING_NONE: #if HAVE_PARITY_BUG /* The Sony NEWS mishandles this for some reason. */ iset = 0; iclear = ANYP; #else iset = ANYP; iclear = 0; #endif fdo = TRUE; break; case PARITYSETTING_EVEN: iset = EVENP; iclear = ODDP; fdo = TRUE; break; case PARITYSETTING_ODD: iset = ODDP; iclear = EVENP; fdo = TRUE; break; case PARITYSETTING_MARK: case PARITYSETTING_SPACE: /* Not supported. */ break; } if (fdo) { if ((q->snew.stty.sg_flags & iset) != iset || (q->snew.stty.sg_flags & iclear) != 0) { q->snew.stty.sg_flags |= iset; q->snew.stty.sg_flags &=~ iclear; fchanged = TRUE; } } #else /* ! HAVE_BSD_TTY */ fdo = FALSE; switch (tparity) { case PARITYSETTING_DEFAULT: break; case PARITYSETTING_NONE: iset = CS8; iclear = PARENB | PARODD | (CSIZE &~ CS8); fdo = TRUE; break; case PARITYSETTING_EVEN: iset = PARENB | CS7; iclear = PARODD | (CSIZE &~ CS7); fdo = TRUE; break; case PARITYSETTING_ODD: iset = PARENB | PARODD | CS7; iclear = CSIZE &~ CS7; fdo = TRUE; break; case PARITYSETTING_MARK: case PARITYSETTING_SPACE: /* Not supported. */ break; } if (fdo) { if ((q->snew.c_cflag & iset) != iset || (q->snew.c_cflag & iclear) != 0) { q->snew.c_cflag |= iset; q->snew.c_cflag &=~ iclear; fchanged = TRUE; } } #endif /* ! HAVE_BSD_TTY */ /* Set whether input characters are stripped to seven bits. */ #if HAVE_BSD_TTY #ifdef LPASS8 { int i; i = LPASS8; if (tstrip == STRIPSETTING_EIGHTBITS) { i = LPASS8; (void) ioctl (q->o, TIOCLBIS, &i); } else if (tstrip == STRIPSETTING_SEVENBITS) { i = LPASS8; (void) ioctl (q->o, TIOCLBIC, &i); } } #endif #else /* ! HAVE_BSD_TTY */ fdo = FALSE; switch (tstrip) { case STRIPSETTING_DEFAULT: break; case STRIPSETTING_EIGHTBITS: iset = 0; iclear = ISTRIP; fdo = TRUE; break; case STRIPSETTING_SEVENBITS: iset = ISTRIP; iclear = 0; fdo = TRUE; break; } if (fdo) { if ((q->snew.c_iflag & iset) != iset || (q->snew.c_iflag & iclear) != 0) { q->snew.c_iflag |= iset; q->snew.c_iflag &=~ iclear; fchanged = TRUE; } } #endif /* ! HAVE_BSD_TTY */ /* Set XON/XOFF handshaking. */ #if HAVE_BSD_TTY fdo = FALSE; switch (txonxoff) { case XONXOFF_DEFAULT: break; case XONXOFF_OFF: iset = RAW; iclear = TANDEM | CBREAK; fdo = TRUE; break; case XONXOFF_ON: iset = CBREAK | TANDEM; iclear = RAW; fdo = TRUE; break; } if (fdo) { if ((q->snew.stty.sg_flags & iset) != iset || (q->snew.stty.sg_flags & iclear) != 0) { q->snew.stty.sg_flags |= iset; q->snew.stty.sg_flags &=~ iclear; fchanged = TRUE; } } #else /* ! HAVE_BSD_TTY */ fdo = FALSE; switch (txonxoff) { case XONXOFF_DEFAULT: break; case XONXOFF_OFF: iset = 0; iclear = IXON | IXOFF; fdo = TRUE; break; case XONXOFF_ON: #ifdef CRTSCTS #if HAVE_POSIX_TERMIOS /* This is system dependent, but I haven't figured out a good way around it yet. If we are doing hardware flow control, we don't send XON/XOFF characters but we do recognize them. */ if ((q->snew.c_cflag & CRTSCTS) != 0) { iset = IXON; iclear = IXOFF; fdo = TRUE; break; } #endif /* HAVE_POSIX_TERMIOS */ #endif /* defined (CRTSCTS) */ #ifdef CRTSFL if ((q->snew.c_cflag & CRTSFL) != 0) { iset = IXON; iclear = IXOFF; /* SCO says we cant have CRTSFL **and** RTSFLOW/CTSFLOW */ #ifdef RTSFLOW iclear |= RTSFLOW; #endif #ifdef CTSFLOW iclear |= CTSFLOW; #endif fdo = TRUE; break; } #endif /* defined(CRTSFL) */ iset = IXON | IXOFF; iclear = 0; fdo = TRUE; break; } if (fdo) { if ((q->snew.c_iflag & iset) != iset || (q->snew.c_iflag & iclear) != 0) { q->snew.c_iflag |= iset; q->snew.c_iflag &=~ iclear; fchanged = TRUE; } } #endif /* ! HAVE_BSD_TTY */ if (fchanged) { if (! fsetterminfodrain (q->o, &q->snew)) { ulog (LOG_ERROR, "Can't change terminal settings: %s", strerror (errno)); return FALSE; } } #if HAVE_BSD_TTY if (txonxoff == XONXOFF_ON && (q->snew.stty.sg_flags & ANYP) == ANYP) { int i; /* At least on Ultrix, we seem to have to set LLITOUT and LPASS8. This shouldn't foul things up anywhere else. As far as I can tell, this has to be done after setting the terminal into cbreak mode, not before. */ #ifndef LLITOUT #define LLITOUT 0 #endif #ifndef LPASS8 #define LPASS8 0 #endif #ifndef LAUTOFLOW #define LAUTOFLOW 0 #endif i = LLITOUT | LPASS8 | LAUTOFLOW; (void) ioctl (q->o, TIOCLBIS, &i); #if HAVE_STRIP_BUG /* Ultrix 4.0 has a peculiar problem: setting CBREAK always causes input characters to be stripped. I hope this does not apply to other BSD systems. It is possible to work around this by using the termio call. I wish this sort of stuff was not necessary!!! */ { struct termio s; if (ioctl (q->o, TCGETA, &s) >= 0) { s.c_iflag &=~ ISTRIP; (void) ioctl (q->o, TCSETA, &s); } } #endif /* HAVE_STRIP_BUG */ } #endif /* HAVE_BSD_TTY */ return TRUE; } /* Change settings of a stdin port. */ static boolean fsstdin_set (qconn, tparity, tstrip, txonxoff) struct sconnection *qconn; enum tparitysetting tparity; enum tstripsetting tstrip; enum txonxoffsetting txonxoff; { struct ssysdep_conn *qsysdep; qsysdep = (struct ssysdep_conn *) qconn->psysdep; qsysdep->o = qsysdep->ord; return fsserial_set (qconn, tparity, tstrip, txonxoff); } /* Run a chat program. */ static boolean fsrun_chat (oread, owrite, pzprog) int oread; int owrite; char **pzprog; { int aidescs[3]; FILE *e; pid_t ipid; char *z; size_t c; aidescs[0] = oread; aidescs[1] = owrite; aidescs[2] = SPAWN_READ_PIPE; /* Pass fkeepuid, fkeepenv and fshell as TRUE. This puts the responsibility of maintaing security on the chat program. */ ipid = ixsspawn ((const char **) pzprog, aidescs, TRUE, TRUE, (const char *) NULL, FALSE, TRUE, (const char *) NULL, (const char *) NULL, (const char *) NULL); if (ipid < 0) { ulog (LOG_ERROR, "ixsspawn (%s): %s", pzprog[0], strerror (errno)); return FALSE; } e = fdopen (aidescs[2], (char *) "r"); if (e == NULL) { ulog (LOG_ERROR, "fdopen: %s", strerror (errno)); (void) close (aidescs[2]); (void) kill (ipid, SIGKILL); (void) ixswait ((unsigned long) ipid, (const char *) NULL); return FALSE; } /* The FILE e now is attached to stderr of the program. Forward every line the program outputs to the log file. */ z = NULL; c = 0; while (getline (&z, &c, e) > 0) { size_t clen; clen = strlen (z); if (z[clen - 1] == '\n') z[clen - 1] = '\0'; if (*z != '\0') ulog (LOG_NORMAL, "chat: %s", z); } xfree ((pointer) z); (void) fclose (e); return ixswait ((unsigned long) ipid, "Chat program") == 0; } /* Run a chat program on a port using separate read/write file descriptors. */ boolean fsdouble_chat (qconn, pzprog) struct sconnection *qconn; char **pzprog; { struct ssysdep_conn *qsysdep; boolean fret; qsysdep = (struct ssysdep_conn *) qconn->psysdep; fret = fsrun_chat (qsysdep->ord, qsysdep->owr, pzprog); if (qsysdep->fterminal) (void) fgetterminfo (qsysdep->ord, &qsysdep->snew); return fret; } /* Run a chat program on any general type of connection. */ boolean fsysdep_conn_chat (qconn, pzprog) struct sconnection *qconn; char **pzprog; { struct ssysdep_conn *qsysdep; boolean fret; qsysdep = (struct ssysdep_conn *) qconn->psysdep; fret = fsrun_chat (qsysdep->o, qsysdep->o, pzprog); if (qsysdep->fterminal) (void) fgetterminfo (qsysdep->o, &qsysdep->snew); return fret; } /* Return baud rate of a serial port. */ static long isserial_baud (qconn) struct sconnection *qconn; { struct ssysdep_conn *qsysdep; qsysdep = (struct ssysdep_conn *) qconn->psysdep; return qsysdep->ibaud; } uucp-1.07/unix/signal.c0000664000076400007640000001232407665321761010533 /* signal.c Signal handling routines. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include /* Signal handling routines. When we catch a signal, we want to set the appropriate elements of afSignal and afLog_signal to TRUE. If we are on a system which restarts system calls, we may also want to longjmp out. On a system which does not restart system calls, these signal handling routines are well-defined by ANSI C. */ #if HAVE_RESTARTABLE_SYSCALLS volatile sig_atomic_t fSjmp; volatile jmp_buf sSjmp_buf; #endif /* HAVE_RESTARTABLE_SYSCALLS */ /* Some systems, such as SunOS, have a SA_INTERRUPT bit that must be set in the sigaction structure to force system calls to be interrupted. */ #ifndef SA_INTERRUPT #define SA_INTERRUPT 0 #endif /* The SVR3 sigset function can be called just like signal, unless system calls are restarted which is extremely unlikely; we prevent this case in sysh.unx. */ #if HAVE_SIGSET && ! HAVE_SIGACTION && ! HAVE_SIGVEC #define signal sigset #endif /* The sigvec structure changed from 4.2BSD to 4.3BSD. These macros make the 4.3 code backward compatible. */ #ifndef SV_INTERRUPT #define SV_INTERRUPT 0 #endif #if ! HAVE_SIGVEC_SV_FLAGS #define sv_flags sv_onstack #endif /* Catch a signal. Reinstall the signal handler if necessary, set the appropriate variables, and do a longjmp if necessary. */ RETSIGTYPE ussignal (isig) int isig; { int iindex; #if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET (void) signal (isig, ussignal); #endif switch (isig) { default: iindex = INDEXSIG_SIGHUP; break; #ifdef SIGINT case SIGINT: iindex = INDEXSIG_SIGINT; break; #endif #ifdef SIGQUIT case SIGQUIT: iindex = INDEXSIG_SIGQUIT; break; #endif #ifdef SIGTERM case SIGTERM: iindex = INDEXSIG_SIGTERM; break; #endif #ifdef SIGPIPE case SIGPIPE: iindex = INDEXSIG_SIGPIPE; break; #endif } afSignal[iindex] = TRUE; afLog_signal[iindex] = TRUE; #if HAVE_RESTARTABLE_SYSCALLS if (fSjmp) longjmp (sSjmp_buf, 1); #endif /* HAVE_RESTARTABLE_SYSCALLS */ } /* Prepare to catch a signal. This is basically the ANSI C routine signal, but it uses sigaction or sigvec instead if they are available. If fforce is FALSE, we do not set the signal if it is currently being ignored. If pfignored is not NULL and fforce is FALSE, then *pfignored will be set to TRUE if the signal was previously being ignored (if fforce is TRUE the value returned in *pfignored is meaningless). If we can't change the signal handler we give a fatal error. */ void usset_signal (isig, pfn, fforce, pfignored) int isig; RETSIGTYPE (*pfn) P((int)); boolean fforce; boolean *pfignored; { #if HAVE_SIGACTION struct sigaction s; if (! fforce) { (void) (sigemptyset (&s.sa_mask)); if (sigaction (isig, (struct sigaction *) NULL, &s) != 0) ulog (LOG_FATAL, "sigaction (%d): %s", isig, strerror (errno)); if (s.sa_handler == SIG_IGN) { if (pfignored != NULL) *pfignored = TRUE; return; } if (pfignored != NULL) *pfignored = FALSE; } s.sa_handler = pfn; (void) (sigemptyset (&s.sa_mask)); s.sa_flags = SA_INTERRUPT; if (sigaction (isig, &s, (struct sigaction *) NULL) != 0) ulog (LOG_FATAL, "sigaction (%d): %s", isig, strerror (errno)); #else /* ! HAVE_SIGACTION */ #if HAVE_SIGVEC struct sigvec s; if (! fforce) { if (sigvec (isig, (struct sigvec *) NULL, &s) != 0) ulog (LOG_FATAL, "sigvec (%d): %s", isig, strerror (errno)); if (s.sv_handler == SIG_IGN) { if (pfignored != NULL) *pfignored = TRUE; return; } if (pfignored != NULL) *pfignored = FALSE; } s.sv_handler = pfn; s.sv_mask = 0; s.sv_flags = SV_INTERRUPT; if (sigvec (isig, &s, (struct sigvec *) NULL) != 0) ulog (LOG_FATAL, "sigvec (%d): %s", isig, strerror (errno)); #else /* ! HAVE_SIGVEC */ if (! fforce) { if (signal (isig, SIG_IGN) == SIG_IGN) { if (pfignored != NULL) *pfignored = TRUE; return; } if (pfignored != NULL) *pfignored = FALSE; } (void) signal (isig, pfn); #endif /* ! HAVE_SIGVEC */ #endif /* ! HAVE_SIGACTION */ } /* The routine called by the system independent code, which always uses the same signal handler. */ void usysdep_signal (isig) int isig; { usset_signal (isig, ussignal, FALSE, (boolean *) NULL); } uucp-1.07/unix/sindir.c0000664000076400007640000000104407665321761010543 /* sindir.c Stick a directory and file name together. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" char * zsysdep_in_dir (zdir, zfile) const char *zdir; const char *zfile; { size_t cdir, cfile; char *zret; cdir = strlen (zdir); cfile = strlen (zfile); zret = zbufalc (cdir + cfile + 2); if (cdir == 1 && *zdir == '/') cdir = 0; else memcpy (zret, zdir, cdir); memcpy (zret + cdir + 1, zfile, cfile); zret[cdir] = '/'; zret[cdir + cfile + 1] = '\0'; return zret; } uucp-1.07/unix/size.c0000664000076400007640000000063707665321761010234 /* size.c Get the size in bytes of a file. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include long csysdep_size (zfile) const char *zfile; { struct stat s; if (stat ((char *) zfile, &s) < 0) { if (errno == ENOENT) return -1; ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); return -2; } return s.st_size; } uucp-1.07/unix/sleep.c0000664000076400007640000000100107665321761010354 /* sleep.c Sleep for a number of seconds. */ #include "uucp.h" #include "sysdep.h" #include "system.h" void usysdep_sleep (c) int c; { #if HAVE_NAPMS || HAVE_NAP || HAVE_USLEEP || HAVE_SELECT || HAVE_POLL int i; /* In this case, usysdep_pause is accurate. */ for (i = 2 * c; i > 0; i--) usysdep_pause (); #else /* On some system sleep (1) may not sleep at all. Avoid this sort of problem by always doing at least sleep (2). */ if (c < 2) c = 2; (void) sleep (c); #endif } uucp-1.07/unix/spawn.c0000664000076400007640000002665307665321761010420 /* spawn.c Spawn a program securely. Copyright (C) 1992, 1993, 1994, 1995 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif #ifndef environ extern char **environ; #endif /* Spawn a child in a fairly secure fashion. This returns the process ID of the child or -1 on error. It takes far too many arguments: pazargs -- arguments (element 0 is command) aidescs -- file descriptors for stdin, stdout and stderr fkeepuid -- TRUE if euid should be left unchanged fkeepenv -- TRUE if environment should be left unmodified zchdir -- directory to chdir to fnosigs -- TRUE if child should ignore SIGHUP, SIGINT and SIGQUIT fshell -- TRUE if should try /bin/sh if execve gets ENOEXEC zpath -- value for environment variable PATH zuu_machine -- value for environment variable UU_MACHINE zuu_user -- value for environment variable UU_USER The aidescs array is three elements long. 0 is stdin, 1 is stdout and 2 is stderr. The array may contain either file descriptor numbers to dup appropriately, or one of the following: SPAWN_NULL -- set descriptor to /dev/null SPAWN_READ_PIPE -- set aidescs element to pipe for parent to read SPAWN_WRITE_PIPE -- set aidescs element to pipe for parent to write If fkeepenv is FALSE, a standard environment is created. The environment arguments (zpath, zuu_machine and zuu_user) are only used if fkeepenv is FALSE; any of them may be NULL. This routine expects that all file descriptors have been set to close-on-exec, so it doesn't have to worry about closing them explicitly. It sets the close-on-exec flag for the new pipe descriptors it returns. */ pid_t ixsspawn (pazargs, aidescs, fkeepuid, fkeepenv, zchdir, fnosigs, fshell, zpath, zuu_machine, zuu_user) const char **pazargs; int aidescs[3]; boolean fkeepuid; boolean fkeepenv; const char *zchdir; boolean fnosigs; boolean fshell; const char *zpath; const char *zuu_machine; const char *zuu_user; { char *zshcmd; int i; char *azenv[9]; char **pazenv; boolean ferr; #if HAVE_FULLDUPLEX_PIPES boolean ffullduplex; #endif int ierr = 0; int onull; int aichild_descs[3]; int cpar_close; int aipar_close[4]; int cchild_close; int aichild_close[3]; pid_t iret = 0; const char *zcmd; /* If we might have to use the shell, allocate enough space for the quoted command before forking. Otherwise the allocation would modify the data segment and we could not safely use vfork. */ zshcmd = NULL; if (fshell) { size_t clen; clen = 0; for (i = 0; pazargs[i] != NULL; i++) clen += strlen (pazargs[i]); zshcmd = zbufalc (2 * clen + i); } /* Set up a standard environment. This is again done before forking because it will modify the data segment. */ if (fkeepenv) pazenv = environ; else { const char *zterm, *ztz; char *zspace; int ienv; if (zpath == NULL) zpath = CMDPATH; azenv[0] = zbufalc (sizeof "PATH=" + strlen (zpath)); sprintf (azenv[0], "PATH=%s", zpath); zspace = azenv[0] + sizeof "PATH=" - 1; while ((zspace = strchr (zspace, ' ')) != NULL) *zspace = ':'; azenv[1] = zbufalc (sizeof "HOME=" + strlen (zSspooldir)); sprintf (azenv[1], "HOME=%s", zSspooldir); zterm = getenv ("TERM"); if (zterm == NULL) zterm = "unknown"; azenv[2] = zbufalc (sizeof "TERM=" + strlen (zterm)); sprintf (azenv[2], "TERM=%s", zterm); azenv[3] = zbufcpy ("SHELL=/bin/sh"); azenv[4] = zbufalc (sizeof "USER=" + strlen (OWNER)); sprintf (azenv[4], "USER=%s", OWNER); ienv = 5; ztz = getenv ("TZ"); if (ztz != NULL) { azenv[ienv] = zbufalc (sizeof "TZ=" + strlen (ztz)); sprintf (azenv[ienv], "TZ=%s", ztz); ++ienv; } if (zuu_machine != NULL) { azenv[ienv] = zbufalc (sizeof "UU_MACHINE=" + strlen (zuu_machine)); sprintf (azenv[ienv], "UU_MACHINE=%s", zuu_machine); ++ienv; } if (zuu_user != NULL) { azenv[ienv] = zbufalc (sizeof "UU_USER=" + strlen (zuu_user)); sprintf (azenv[ienv], "UU_USER=%s", zuu_user); ++ienv; } azenv[ienv] = NULL; pazenv = azenv; } /* Set up any needed pipes. */ ferr = FALSE; onull = -1; cpar_close = 0; cchild_close = 0; #if HAVE_FULLDUPLEX_PIPES ffullduplex = (aidescs[0] == SPAWN_WRITE_PIPE && aidescs[1] == SPAWN_READ_PIPE); #endif for (i = 0; i < 3; i++) { if (aidescs[i] == SPAWN_NULL) { if (onull < 0) { onull = open ((char *) "/dev/null", O_RDWR); if (onull < 0 || fcntl (onull, F_SETFD, fcntl (onull, F_GETFD, 0) | FD_CLOEXEC) < 0) { ierr = errno; (void) close (onull); ferr = TRUE; break; } aipar_close[cpar_close] = onull; ++cpar_close; } aichild_descs[i] = onull; } else if (aidescs[i] != SPAWN_READ_PIPE && aidescs[i] != SPAWN_WRITE_PIPE) aichild_descs[i] = aidescs[i]; else { int aipipe[2]; #if HAVE_FULLDUPLEX_PIPES if (ffullduplex && i == 1) { /* Just use the fullduplex pipe. */ aidescs[i] = aidescs[0]; aichild_descs[i] = aichild_descs[0]; continue; } #endif if (pipe (aipipe) < 0) { ierr = errno; ferr = TRUE; break; } if (aidescs[i] == SPAWN_READ_PIPE) { aidescs[i] = aipipe[0]; aichild_close[cchild_close] = aipipe[0]; aichild_descs[i] = aipipe[1]; aipar_close[cpar_close] = aipipe[1]; } else { aidescs[i] = aipipe[1]; aichild_close[cchild_close] = aipipe[1]; aichild_descs[i] = aipipe[0]; aipar_close[cpar_close] = aipipe[0]; } ++cpar_close; ++cchild_close; if (fcntl (aipipe[0], F_SETFD, fcntl (aipipe[0], F_GETFD, 0) | FD_CLOEXEC) < 0 || fcntl (aipipe[1], F_SETFD, fcntl (aipipe[1], F_GETFD, 0) | FD_CLOEXEC) < 0) { ierr = errno; ferr = TRUE; break; } } } #if DEBUG > 1 if (! ferr && FDEBUGGING (DEBUG_EXECUTE)) { ulog (LOG_DEBUG_START, "Forking %s", pazargs[0]); for (i = 1; pazargs[i] != NULL; i++) ulog (LOG_DEBUG_CONTINUE, " %s", pazargs[i]); ulog (LOG_DEBUG_END, "%s", ""); } #endif if (! ferr) { /* This should really be vfork if available. */ iret = ixsfork (); if (iret < 0) { ferr = TRUE; ierr = errno; } } if (ferr) { for (i = 0; i < cchild_close; i++) (void) close (aichild_close[i]); iret = -1; } if (iret != 0) { /* The parent. Close the child's ends of the pipes and return the process ID, or an error. */ for (i = 0; i < cpar_close; i++) (void) close (aipar_close[i]); ubuffree (zshcmd); if (! fkeepenv) { char **pz; for (pz = azenv; *pz != NULL; pz++) ubuffree (*pz); } errno = ierr; return iret; } /* The child. */ #ifdef STDIN_FILENO #if STDIN_FILENO != 0 || STDOUT_FILENO != 1 || STDERR_FILENO != 2 #error The following code makes invalid assumptions #endif #endif for (i = 0; i < 3; i++) { if (aichild_descs[i] != i) (void) dup2 (aichild_descs[i], i); /* This should only be necessary if aichild_descs[i] == i, but some systems copy the close-on-exec flag for a dupped descriptor, which is wrong according to POSIX. */ (void) fcntl (i, F_SETFD, fcntl (i, F_GETFD, 0) &~ FD_CLOEXEC); } zcmd = pazargs[0]; pazargs[0] = strrchr (zcmd, '/'); if (pazargs[0] == NULL) pazargs[0] = zcmd; else ++pazargs[0]; if (! fkeepuid) { /* Return to the uid of the invoking user. */ (void) setuid (getuid ()); (void) setgid (getgid ()); } else { /* Try to force the UUCP uid to be both real and effective user ID, in order to present a consistent environment regardless of the invoking user. This won't work on older System V based systems, where it can cause trouble if ordinary users wind up executing uuxqt, perhaps via uucico; any program which uuxqt executes will have an arbitrary real user ID, so if the program is itself a setuid program, any security checks it does based on the real user ID will be incorrect. Fixing this problem would seem to require a special setuid root program; I have not used this approach because modern systems should not suffer from it. */ #if HAVE_SETREUID (void) setreuid (geteuid (), -1); (void) setregid (getegid (), -1); #else (void) setuid (geteuid ()); (void) setgid (getegid ()); #endif } if (zchdir != NULL) (void) chdir (zchdir); if (fnosigs) { #ifdef SIGHUP (void) signal (SIGHUP, SIG_IGN); #endif #ifdef SIGINT (void) signal (SIGINT, SIG_IGN); #endif #ifdef SIGQUIT (void) signal (SIGQUIT, SIG_IGN); #endif } #ifdef isc386 #ifdef _POSIX_SOURCE /* ISC has a remarkably stupid notion of environments. If a program is compiled in the POSIX environment, it sets a process state. If you then exec a program which expects the USG environment, the process state is not reset, so the execed program fails. The __setostype call is required to change back to the USG environment. This ought to be a switch in policy.h, but it seems too trivial, so I will leave this code here and wait for it to break in some fashion in the next version of ISC. */ __setostype (0); #endif #endif (void) execve ((char *) zcmd, (char **) pazargs, pazenv); /* The exec failed. If permitted, try using /bin/sh to execute a shell script. */ if (errno == ENOEXEC && fshell) { char *zto; const char *azshargs[4]; pazargs[0] = zcmd; zto = zshcmd; for (i = 0; pazargs[i] != NULL; i++) { const char *zfrom; for (zfrom = pazargs[i]; *zfrom != '\0'; zfrom++) { /* Some versions of /bin/sh appear to have a bug such that quoting a '/' sometimes causes an error. I don't know exactly when this happens (I can recreate it on Ultrix 4.0), but in any case it is harmless to not quote a '/'. */ if (*zfrom != '/') *zto++ = '\\'; *zto++ = *zfrom; } *zto++ = ' '; } *(zto - 1) = '\0'; azshargs[0] = "sh"; azshargs[1] = "-c"; azshargs[2] = zshcmd; azshargs[3] = NULL; (void) execve ((char *) "/bin/sh", (char **) azshargs, pazenv); } _exit (EXIT_FAILURE); /* Avoid compiler warning. */ return -1; } uucp-1.07/unix/splcmd.c0000664000076400007640000001063507665321761010543 /* splcmd.c Spool a command. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "uuconf.h" #include "sysdep.h" #include "system.h" #include #include /* Given a set of commands to execute for a remote system, create a command file holding them. This creates a single command file holding all the commands passed in. It returns a jobid. */ char * zsysdep_spool_commands (qsys, bgrade, ccmds, pascmds, pftemp) const struct uuconf_system *qsys; int bgrade; int ccmds; const struct scmd *pascmds; boolean *pftemp; { char abtempfile[sizeof "TMP1234567890"]; char *ztemp; FILE *e; int i; const struct scmd *qcmd; char *z; char *zjobid; if (pftemp != NULL) *pftemp = TRUE; #if DEBUG > 0 if (! UUCONF_GRADE_LEGAL (bgrade)) ulog (LOG_FATAL, "Bad grade %d", bgrade); #endif /* Write the commands into a temporary file and then rename it to avoid a race with uucico reading the file. */ sprintf (abtempfile, "TMP%010lx", (unsigned long) getpid ()); ztemp = zsfind_file (abtempfile, qsys->uuconf_zname, bgrade); if (ztemp == NULL) return NULL; e = esysdep_fopen (ztemp, FALSE, FALSE, TRUE); if (e == NULL) { ubuffree (ztemp); return NULL; } for (i = 0, qcmd = pascmds; i < ccmds; i++, qcmd++) { boolean fquote; const struct scmd *q; struct scmd squoted; fquote = fcmd_needs_quotes (qcmd); if (! fquote) q = qcmd; else { uquote_cmd (qcmd, &squoted); q = &squoted; } switch (q->bcmd) { case 'S': fprintf (e, "S %s %s %s -%s %s 0%o %s\n", q->zfrom, q->zto, q->zuser, q->zoptions, q->ztemp, q->imode, q->znotify == NULL ? (const char *) "" : q->znotify); break; case 'R': fprintf (e, "R %s %s %s -%s\n", q->zfrom, q->zto, q->zuser, q->zoptions); break; case 'X': fprintf (e, "X %s %s %s -%s\n", q->zfrom, q->zto, q->zuser, q->zoptions); break; case 'E': fprintf (e, "E %s %s %s -%s %s 0%o %s 0 %s\n", q->zfrom, q->zto, q->zuser, q->zoptions, q->ztemp, q->imode, q->znotify, q->zcmd); break; default: ulog (LOG_ERROR, "zsysdep_spool_commands: Unrecognized type %d", q->bcmd); (void) fclose (e); (void) remove (ztemp); ubuffree (ztemp); if (pftemp != NULL) *pftemp = FALSE; return NULL; } if (fquote) ufree_quoted_cmd (&squoted); } if (! fstdiosync (e, ztemp)) { (void) fclose (e); (void) remove (ztemp); ubuffree (ztemp); return NULL; } if (fclose (e) != 0) { ulog (LOG_ERROR, "fclose: %s", strerror (errno)); (void) remove (ztemp); ubuffree (ztemp); return NULL; } /* The filename returned by zscmd_file is subject to some unlikely race conditions, so keep trying the link until the destination file does not already exist. Each call to zscmd_file should return a file name which does not already exist, so we don't have to do anything special before calling it again. */ while (TRUE) { z = zscmd_file (qsys, bgrade); if (z == NULL) { (void) remove (ztemp); ubuffree (ztemp); return NULL; } if (link (ztemp, z) >= 0) break; if (errno != EEXIST) { ulog (LOG_ERROR, "link (%s, %s): %s", ztemp, z, strerror (errno)); (void) remove (ztemp); ubuffree (ztemp); ubuffree (z); return NULL; } ubuffree (z); } (void) remove (ztemp); ubuffree (ztemp); zjobid = zsfile_to_jobid (qsys, z, bgrade); if (zjobid == NULL) (void) remove (z); ubuffree (z); return zjobid; } uucp-1.07/unix/splnam.c0000664000076400007640000000061607665321761010551 /* splnam.c Get the full name of a file in the spool directory. */ #include "uucp.h" #include "uuconf.h" #include "sysdep.h" #include "system.h" /* Get the real name of a spool file. */ char * zsysdep_spool_file_name (qsys, zfile, pseq) const struct uuconf_system *qsys; const char *zfile; pointer pseq; { return zsfind_file (zfile, qsys->uuconf_zname, bsgrade (pseq)); } uucp-1.07/unix/spool.c0000664000076400007640000003403507665321761010415 /* spool.c Find a file in the spool directory. Copyright (C) 1991, 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char spool_rcsid[] = "$Id: spool.c,v 1.14 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "sysdep.h" #include "system.h" /* There are several types of files that go in the spool directory, and they go into various different subdirectories. Whenever the system name LOCAL appears below, it means whatever the local system name is. Command files These contain instructions for uucico indicating what files to transfer to and from what systems. Each line of a work file is a command beginning with S, R, X, or E. #if ! SPOOLDIR_TAYLOR They are named C.ssssssgqqqq, where ssssss is the system name to transfer to or from, g is the grade and qqqq is the sequence number. #if SPOOLDIR_V2 They are put in the spool directory. #elif SPOOLDIR_BSD42 || SPOOLDIR_BSD43 They are put in the directory "C.". #elif SPOOLDIR_HDB They are put in a directory named for the system for which they were created. #elif SPOOLDIR_ULTRIX If the directory sys/ssssss exists, they are put in the directory sys/ssssss/C; otherwise, they are put in the directory sys/DEFAULT/C. #elif SPOOLDIR_SVR4 They are put in the directory sys/g, where sys is the system name and g is the grade. #endif #else SPOOLDIR_TAYLOR They are named C.gqqqq, where g is the grade and qqqq is the sequence number, and are placed in the directory ssssss/C. where ssssss is the system name to transfer to or from. The sequence number for a C. file is actually a long string; it is not based on the sequence number file, but is generated via a process which attempts to produce a unique string each time it is run. #endif Data files There are files to be transferred to other systems. Some files to be transferred may not be in the spool directory, depending on how uucp was invoked. Data files are named in work files, so it is never necessary to look at them directly (except to remove old ones); it is only necessary to create them. These means that the many variations in naming are inconsequential. #if ! SPOOLDIR_TAYLOR They are named D.ssssssgqqqq where ssssss is a system name (which may be LOCAL for locally initiated transfers or a remote system for remotely initiated transfers, except that HDB appears to use the system the file is being transferred to), g is the grade and qqqq is the sequence number. Some systems use a trailing subjob ID number, but we currently do not. The grade is not important, and some systems do not use it. If the data file is to become an execution file on another system the grade (if present) will be 'X'. Otherwise Ultrix appears to use 'b'; the uux included with gnuucp 1.0 appears to use 'S'; SCO does not appear to use a grade, although it does use a subjob ID number. #if SPOOLDIR_V2 They are put in the spool directory. #elif SPOOLDIR_BSD42 If the name begins with D.LOCAL, the file is put in the directory D.LOCAL. Otherwise the file is put in the directory D.. #elif SPOOLDIR_BSD43 If the name begins with D.LOCALX, the file is put in the directory D.LOCALX. Otherwise if the name begins with D.LOCAL, the file is put in the directory D.LOCAL Otherwise the file is put in the directory "D.". #elif SPOOLDIR_HDB They are put in a directory named for the system for which they were created. #elif SPOOLDIR_ULTRIX Say the file is being transferred to system REMOTE. If the directory sys/REMOTE exists, then if the file begins with D.LOCALX it is put in sys/REMOTE/D.LOCALX, if the file begins with D.LOCAL it is put in sys/REMOTE/D.LOCAL, and otherwise it is put in "sys/REMOTE/D.". If the directory sys/REMOTE does not exist, the same applies except that DEFAULT is used instead of REMOTE. #elif SPOOLDIR_SVR4 They are put in the directory sys/g, where sys is the system name and g is the grade. #endif #else SPOOLDIR_TAYLOR If the file is to become an executable file on another system it is named D.Xqqqq, otherwise it is named D.qqqq where in both cases qqqq is a sequence number. If the corresponding C. file is in directory ssssss/C., a D.X file is placed in ssssss/D.X and a D. file is placed in "ssssss/D.". #endif Execute files These are files that specify programs to be executed. They are created by uux, perhaps as run on another system. These names are important, because a file transfer done to an execute file name causes an execution to occur. The name is X.ssssssgqqqq, where ssssss is the requesting system, g is the grade, and qqqq is a sequence number. #if SPOOLDIR_V2 || SPOOLDIR_BSD42 These files are placed in the spool directory. #elif SPOOLDIR_BSD43 These files are placed in the directory X.. #elif SPOOLDIR_HDB || SPOOLDIR_SVR4 These files are put in a directory named for the system for which the files were created. #elif SPOOLDIR_ULTRIX If there is a spool directory (sys/ssssss) for the requesting system, the files are placed in sys/ssssss/X.; otherwise, the files are placed in "sys/DEFAULT/X.". #elif SPOOLDIR_TAYLOR The system name is automatically truncated to seven characters when a file is created. The files are placed in the subdirectory X. of a directory named for the system for which the files were created. #endif Temporary receive files These are used when receiving files from another system. They are later renamed to the final name. The actual name is unimportant, although it generally begins with TM.. #if SPOOLDIR_V2 || SPOOLDIR_BSD42 These files are placed in the spool directory. #elif SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR These files are placed in the directory .Temp. #elif SPOOLDIR_HDB || SPOOLDIR_SVR4 These files are placed in a directory named for the system for which they were created. #endif System status files These are used to record when the last call was made to the system and what the status is. They are used to prevent frequent recalls to a system which is not responding. I will not attempt to recreate the format of these exactly, since they are not all that important. They will be put in the directory .Status, as in HDB, and they use the system name as the name of the file. Sequence file This is used to generate a unique sequence number. It contains an ASCII number. #if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 The file is named SEQF and is kept in the spool directory. #elif SPOOLDIR_HDB || SPOOLDIR_SVR4 A separate sequence file is kept for each system in the directory .Sequence with the name of the system. #elif SPOOLDIR_ULTRIX Each system with a file sys/ssssss has a sequence file in sys/ssssss/.SEQF. Other systems use sys/DEFAULT/.SEQF. #else SPOOLDIR_TAYLOR A sequence file named SEQF is kept in the directory ssssss for each system. #endif */ /* Given the name of a file as specified in a UUCP command, and the system for which this file has been created, return where to find it in the spool directory. The file will begin with C. (a command file), D. (a data file) or X. (an execution file). Under SPOOLDIR_SVR4 we need to know the grade of the file created by the local system; this is the bgrade argument, which is -1 for a file from a remote system. */ /*ARGSUSED*/ char * zsfind_file (zsimple, zsystem, bgrade) const char *zsimple; const char *zsystem; int bgrade ATTRIBUTE_UNUSED; { /* zsysdep_spool_commands calls this with TMPXXX which we must treat as a C. file. */ if ((zsimple[0] != 'T' || zsimple[1] != 'M' || zsimple[2] != 'P') && ! fspool_file (zsimple)) { ulog (LOG_ERROR, "Unrecognized file name %s", zsimple); return NULL; } #if ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 && ! SPOOLDIR_TAYLOR if (*zsimple == 'X') { static char *zbuf; static size_t cbuf; size_t clen, cwant; /* Files beginning with X. are execute files. It is important for security reasons that we know the system which created the X. file. This is easy under SPOOLDIR_HDB or SPOOLDIR_SVR4 SPOOLDIR_TAYLOR, because the file will be in a directory named for the system. Under other schemes, we must get the system name from the X. file name. To prevent security violations, we set the system name directly here; this will cause problems if the maximum file name length is too short, but hopefully no problem will occur since any System V systems will be using HDB or SVR4 or TAYLOR. */ clen = strlen (zsimple); if (clen < 5) { ulog (LOG_ERROR, "Bad file name (too short) %s", zsimple); return NULL; } cwant = strlen (zsystem) + 8; if (cwant > cbuf) { zbuf = (char *) xrealloc ((pointer) zbuf, cwant); cbuf = cwant; } sprintf (zbuf, "X.%s%s", zsystem, zsimple + clen - 5); zsimple = zbuf; } #endif /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 && ! SPOOLDIR_TAYLOR */ #if SPOOLDIR_V2 /* V2 never uses subdirectories. */ return zbufcpy (zsimple); #endif /* SPOOLDIR_V2 */ #if SPOOLDIR_HDB /* HDB always uses the system name as a directory. */ return zsysdep_in_dir (zsystem, zsimple); #endif /* SPOOLDIR_HDB */ #if SPOOLDIR_SVR4 /* SVR4 uses grade directories within the system directory for local command and data files. */ if (bgrade < 0 || *zsimple == 'X') return zsysdep_in_dir (zsystem, zsimple); else { char abgrade[2]; abgrade[0] = bgrade; abgrade[1] = '\0'; return zsappend3 (zsystem, abgrade, zsimple); } #endif /* SPOOLDIR_SVR4 */ #if ! SPOOLDIR_V2 && ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 switch (*zsimple) { case 'C': case 'T': #if SPOOLDIR_BSD42 || SPOOLDIR_BSD43 return zsysdep_in_dir ("C.", zsimple); #endif /* SPOOLDIR_BSD42 || SPOOLDIR_BSD43 */ #if SPOOLDIR_ULTRIX if (fsultrix_has_spool (zsystem)) return zsappend4 ("sys", zsystem, "C.", zsimple); else return zsappend4 ("sys", "DEFAULT", "C.", zsimple); #endif /* SPOOLDIR_ULTRIX */ #if SPOOLDIR_TAYLOR return zsappend3 (zsystem, "C.", zsimple); #endif /* SPOOLDIR_TAYLOR */ case 'D': #if SPOOLDIR_BSD42 || SPOOLDIR_BSD43 { size_t c; boolean ftruncated; /* D.LOCAL in D.LOCAL/, others in D./. If BSD43, D.LOCALX in D.LOCALX/. */ ftruncated = TRUE; if (strncmp (zsimple + 2, zSlocalname, strlen (zSlocalname)) == 0) { c = strlen (zSlocalname); ftruncated = FALSE; } else if (strncmp (zsimple + 2, zSlocalname, 7) == 0) c = 7; else if (strncmp (zsimple + 2, zSlocalname, 6) == 0) c = 6; else c = 0; #if SPOOLDIR_BSD43 if (c > 0 && zsimple[c + 2] == 'X') c++; #endif /* SPOOLDIR_BSD43 */ if (c > 0) { char *zalloc; zalloc = zbufalc (c + 3); memcpy (zalloc, zsimple, c + 2); zalloc[c + 2] = '\0'; /* If we truncated the system name, and there is no existing directory with the truncated name, then just use D.. */ if (! ftruncated || fsysdep_directory (zalloc)) { char *zret; zret = zsysdep_in_dir (zalloc, zsimple); ubuffree (zalloc); return zret; } ubuffree (zalloc); } return zsysdep_in_dir ("D.", zsimple); } #endif /* SPOOLDIR_BSD42 || SPOOLDIR_BSD43 */ #if SPOOLDIR_ULTRIX { size_t c; boolean ftruncated; char *zfree; const char *zdir; char *zret; /* D.LOCALX in D.LOCALX/, D.LOCAL in D.LOCAL/, others in D./. */ ftruncated = TRUE; if (strncmp (zsimple + 2, zSlocalname, strlen (zSlocalname)) == 0) { c = strlen (zSlocalname); ftruncated = FALSE; } else if (strncmp (zsimple + 2, zSlocalname, 7) == 0) c = 7; else if (strncmp (zsimple + 2, zSlocalname, 6) == 0) c = 6; else c = 0; if (c > 0 && zsimple[c + 2] == 'X') ++c; if (c > 0) { zfree = zbufalc (c + 3); memcpy (zfree, zsimple, c + 2); zfree[c + 2] = '\0'; zdir = zfree; /* If we truncated the name, and there is no directory for the truncated name, then don't use it. */ if (ftruncated) { char *zlook; zlook = zsappend3 ("sys", (fsultrix_has_spool (zsystem) ? zsystem : "DEFAULT"), zdir); if (! fsysdep_directory (zlook)) zdir = "D."; ubuffree (zlook); } } else { zfree = NULL; zdir = "D."; } zret = zsappend4 ("sys", (fsultrix_has_spool (zsystem) ? zsystem : "DEFAULT"), zdir, zsimple); ubuffree (zfree); return zret; } #endif /* SPOOLDIR_ULTRIX */ #if SPOOLDIR_TAYLOR if (zsimple[2] == 'X') return zsappend3 (zsystem, "D.X", zsimple); else return zsappend3 (zsystem, "D.", zsimple); #endif /* SPOOLDIR_TAYLOR */ case 'X': #if SPOOLDIR_BSD42 return zbufcpy (zsimple); #endif #if SPOOLDIR_BSD43 return zsysdep_in_dir ("X.", zsimple); #endif #if SPOOLDIR_ULTRIX return zsappend4 ("sys", (fsultrix_has_spool (zsystem) ? zsystem : "DEFAULT"), "X.", zsimple); #endif #if SPOOLDIR_TAYLOR return zsappend3 (zsystem, "X.", zsimple); #endif } /* This is just to avoid warnings; it will never be executed. */ return NULL; #endif /* ! SPOOLDIR_V2 && ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ } uucp-1.07/unix/srmdir.c0000664000076400007640000000464607665321761010566 /* srmdir.c Remove a directory and all its contents. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include #if HAVE_FTW_H #include #endif static int isremove_dir P((const char *, const struct stat *, int)); /* Keep a list of directories to be removed. */ struct sdirlist { struct sdirlist *qnext; char *zdir; }; static struct sdirlist *qSdirlist; /* Remove a directory and all files in it. */ boolean fsysdep_rmdir (zdir) const char *zdir; { boolean fret; struct sdirlist *q; qSdirlist = NULL; fret = TRUE; if (ftw ((char *) zdir, isremove_dir, 5) != 0) { ulog (LOG_ERROR, "ftw: %s", strerror (errno)); fret = FALSE; } q = qSdirlist; while (q != NULL) { struct sdirlist *qnext; if (rmdir (q->zdir) != 0) { ulog (LOG_ERROR, "rmdir (%s): %s", q->zdir, strerror (errno)); fret = FALSE; } ubuffree (q->zdir); qnext = q->qnext; xfree ((pointer) q); q = qnext; } return fret; } /* Remove a file in a directory. */ /*ARGSUSED*/ static int isremove_dir (zfile, qstat, iflag) const char *zfile; const struct stat *qstat ATTRIBUTE_UNUSED; int iflag; { if (iflag == FTW_D || iflag == FTW_DNR) { struct sdirlist *q; q = (struct sdirlist *) xmalloc (sizeof (struct sdirlist)); q->qnext = qSdirlist; q->zdir = zbufcpy (zfile); qSdirlist = q; } else { if (remove (zfile) != 0) ulog (LOG_ERROR, "remove (%s): %s", zfile, strerror (errno)); } return 0; } uucp-1.07/unix/statsb.c0000664000076400007640000003313207665321761010556 /* statsb.c System dependent routines for uustat. Copyright (C) 1992, 1993, 1994 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char statsb_rcsid[] = "$Id: statsb.c,v 1.20 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "uuconf.h" #include "sysdep.h" #include "system.h" #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #if HAVE_OPENDIR #if HAVE_DIRENT_H #include #else /* ! HAVE_DIRENT_H */ #include #define dirent direct #endif /* ! HAVE_DIRENT_H */ #endif /* HAVE_OPENDIR */ #if HAVE_TIME_H #include #endif #if HAVE_UTIME_H #include #endif /* Local functions. */ static int issettime P((const char *z, time_t inow)); static boolean fskill_or_rejuv P((pointer puuconf, const char *zid, boolean fkill)); /* Set file access time to the present. On many systems this could be done by passing NULL to utime, but on some that doesn't work. This routine is not time critical, so we never rely on NULL. */ static int issettime(z, inow) const char *z; time_t inow; { #if HAVE_UTIME_H struct utimbuf s; s.actime = inow; s.modtime = inow; return utime ((char *) z, &s); #else time_t ai[2]; ai[0] = inow; ai[1] = inow; return utime ((char *) z, ai); #endif } /* Kill a job, given the jobid. */ boolean fsysdep_kill_job (puuconf, zid) pointer puuconf; const char *zid; { return fskill_or_rejuv (puuconf, zid, TRUE); } /* Rejuvenate a job, given the jobid. */ boolean fsysdep_rejuvenate_job (puuconf, zid) pointer puuconf; const char *zid; { return fskill_or_rejuv (puuconf, zid, FALSE); } /* Kill or rejuvenate a job, given the jobid. */ static boolean fskill_or_rejuv (puuconf, zid, fkill) pointer puuconf; const char *zid; boolean fkill; { char *zfile; char *zsys; char bgrade; time_t inow = 0; int iuuconf; struct uuconf_system ssys; FILE *e; boolean fret; char *zline; size_t cline; int isys; zfile = zsjobid_to_file (zid, &zsys, &bgrade); if (zfile == NULL) return FALSE; if (! fkill) inow = time ((time_t *) NULL); iuuconf = uuconf_system_info (puuconf, zsys, &ssys); if (iuuconf == UUCONF_NOT_FOUND) { if (! funknown_system (puuconf, zsys, &ssys)) { ulog (LOG_ERROR, "%s: Bad job id", zid); ubuffree (zfile); ubuffree (zsys); return FALSE; } } else if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); ubuffree (zfile); ubuffree (zsys); return FALSE; } e = fopen (zfile, "r"); if (e == NULL) { if (errno == ENOENT) ulog (LOG_ERROR, "%s: Job not found", zid); else ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno)); (void) uuconf_system_free (puuconf, &ssys); ubuffree (zfile); ubuffree (zsys); return FALSE; } /* Now we have to read through the file to identify any temporary files. */ fret = TRUE; zline = NULL; cline = 0; while (getline (&zline, &cline, e) > 0) { struct scmd s; if (! fparse_cmd (zline, &s)) { ulog (LOG_ERROR, "Bad line in command file %s", zfile); fret = FALSE; continue; } /* You are only permitted to delete a job if you submitted it or if you are root or uucp. */ if (strcmp (s.zuser, zsysdep_login_name ()) != 0 && ! fsysdep_privileged ()) { ulog (LOG_ERROR, "%s: Not submitted by you", zid); xfree ((pointer) zline); (void) fclose (e); (void) uuconf_system_free (puuconf, &ssys); ubuffree (zfile); ubuffree (zsys); return FALSE; } if (s.bcmd == 'S' || s.bcmd == 'E') { char *ztemp; ztemp = zsfind_file (s.ztemp, ssys.uuconf_zname, bgrade); if (ztemp == NULL) fret = FALSE; else { if (fkill) isys = remove (ztemp); else isys = issettime (ztemp, inow); if (isys != 0 && errno != ENOENT) { ulog (LOG_ERROR, "%s (%s): %s", fkill ? "remove" : "utime", ztemp, strerror (errno)); fret = FALSE; } ubuffree (ztemp); } } } xfree ((pointer) zline); (void) fclose (e); (void) uuconf_system_free (puuconf, &ssys); ubuffree (zsys); if (fkill) isys = remove (zfile); else isys = issettime (zfile, inow); if (isys != 0 && errno != ENOENT) { ulog (LOG_ERROR, "%s (%s): %s", fkill ? "remove" : "utime", zfile, strerror (errno)); fret = FALSE; } ubuffree (zfile); return fret; } /* Get the time a work job was queued. */ long ixsysdep_work_time (qsys, pseq) const struct uuconf_system *qsys; pointer pseq; { char *zjobid, *zfile; long iret; zjobid = zsysdep_jobid (qsys, pseq); zfile = zsjobid_to_file (zjobid, (char **) NULL, (char *) NULL); if (zfile == NULL) return 0; ubuffree (zjobid); iret = ixsysdep_file_time (zfile); ubuffree (zfile); return iret; } /* Get the time a file was created (actually, the time it was last modified). */ long ixsysdep_file_time (zfile) const char *zfile; { struct stat s; if (stat ((char *) zfile, &s) < 0) { if (errno != ENOENT) ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); return ixsysdep_time ((long *) NULL); } return (long) s.st_mtime; } /* Set the time of a file to the current time. */ boolean fsysdep_touch_file (zfile) const char *zfile; { if (issettime (zfile, time ((time_t *) NULL)) != 0) { ulog (LOG_ERROR, "utime (%s): %s", zfile, strerror (errno)); return FALSE; } return TRUE; } /* Start getting the status files. */ boolean fsysdep_all_status_init (phold) pointer *phold; { DIR *qdir; qdir = opendir ((char *) ".Status"); if (qdir == NULL) { ulog (LOG_ERROR, "opendir (.Status): %s", strerror (errno)); return FALSE; } *phold = (pointer) qdir; return TRUE; } /* Get the next status file. */ char * zsysdep_all_status (phold, pferr, qstat) pointer phold; boolean *pferr; struct sstatus *qstat; { DIR *qdir = (DIR *) phold; struct dirent *qentry; while (TRUE) { errno = 0; qentry = readdir (qdir); if (qentry == NULL) { if (errno == 0) *pferr = FALSE; else { ulog (LOG_ERROR, "readdir: %s", strerror (errno)); *pferr = TRUE; } return NULL; } if (qentry->d_name[0] != '.') { struct uuconf_system ssys; /* Hack seriously; fsysdep_get_status only looks at the zname element of the qsys argument, so if we fake that we can read the status file. This should really be done differently. */ ssys.uuconf_zname = qentry->d_name; if (fsysdep_get_status (&ssys, qstat, (boolean *) NULL)) return zbufcpy (qentry->d_name); /* If fsysdep_get_status fails, it will output an error message. We just continue with the next entry, so that most of the status files will be displayed. */ } } } /* Finish getting the status file. */ void usysdep_all_status_free (phold) pointer phold; { DIR *qdir = (DIR *) phold; (void) closedir (qdir); } /* Get the status of all processes holding lock files. We do this by invoking ps after we've figured out the process entries to use. */ boolean fsysdep_lock_status () { DIR *qdir; struct dirent *qentry; int calc; pid_t *pai; #if HAVE_QNX_LOCKFILES nid_t *painid; #endif int cgot; int aidescs[3]; char *zcopy, *ztok; int cargs, iarg; char **pazargs; qdir = opendir ((char *) zSlockdir); if (qdir == NULL) { ulog (LOG_ERROR, "opendir (%s): %s", zSlockdir, strerror (errno)); return FALSE; } /* We look for entries that start with "LCK.." and ignore everything else. This won't find all possible lock files, but it should find all the locks on terminals and systems. */ calc = 0; pai = NULL; cgot = 0; #if HAVE_QNX_LOCKFILES painid = NULL; #endif while ((qentry = readdir (qdir)) != NULL) { char *zname; int o; #if HAVE_QNX_LOCKFILES nid_t inid; char ab[23]; char *zend; #else #if HAVE_V2_LOCKFILES int i; #else char ab[12]; #endif #endif int cread; int ierr; pid_t ipid; int icheck; if (strncmp (qentry->d_name, "LCK..", sizeof "LCK.." - 1) != 0) continue; zname = zsysdep_in_dir (zSlockdir, qentry->d_name); o = open ((char *) zname, O_RDONLY | O_NOCTTY, 0); if (o < 0) { if (errno != ENOENT) ulog (LOG_ERROR, "open (%s): %s", zname, strerror (errno)); ubuffree (zname); continue; } #if HAVE_V2_LOCKFILES cread = read (o, &i, sizeof i); #else cread = read (o, ab, sizeof ab - 1); #endif ierr = errno; (void) close (o); if (cread < 0) { ulog (LOG_ERROR, "read %s: %s", zname, strerror (ierr)); ubuffree (zname); continue; } ubuffree (zname); #if HAVE_QNX_LOCKFILES ab[cread] = '\0'; ipid = (pid_t) strtol (ab, &zend, 10); inid = (nid_t) strtol (zend, (char **) NULL, 10); #else #if HAVE_V2_LOCKFILES ipid = (pid_t) i; #else ab[cread] = '\0'; ipid = (pid_t) strtol (ab, (char **) NULL, 10); #endif #endif #if HAVE_QNX_LOCKFILES printf ("%s: %ld %ld\n", qentry->d_name, (long) inid, (long) ipid); #else printf ("%s: %ld\n", qentry->d_name, (long) ipid); #endif for (icheck = 0; icheck < cgot; icheck++) if (pai[icheck] == ipid) break; if (icheck < cgot) continue; if (cgot >= calc) { calc += 10; pai = (pid_t *) xrealloc ((pointer) pai, calc * sizeof (pid_t)); #if HAVE_QNX_LOCKFILES painid = (nid_t *) xrealloc ((pointer) painid, calc * sizeof (nid_t)); #endif } pai[cgot] = ipid; #if HAVE_QNX_LOCKFILES painid[cgot] = inid; #endif ++cgot; } if (cgot == 0) return TRUE; aidescs[0] = SPAWN_NULL; aidescs[1] = 1; aidescs[2] = 2; /* Parse PS_PROGRAM into an array of arguments. */ zcopy = zbufcpy (PS_PROGRAM); cargs = 0; for (ztok = strtok (zcopy, " \t"); ztok != NULL; ztok = strtok ((char *) NULL, " \t")) ++cargs; pazargs = (char **) xmalloc ((cargs + 1) * sizeof (char *)); memcpy (zcopy, PS_PROGRAM, sizeof PS_PROGRAM); for (ztok = strtok (zcopy, " \t"), iarg = 0; ztok != NULL; ztok = strtok ((char *) NULL, " \t"), ++iarg) pazargs[iarg] = ztok; pazargs[iarg] = NULL; #if ! HAVE_PS_MULTIPLE /* We have to invoke ps multiple times. */ { int i; char *zlast, *zset; #if HAVE_QNX_LOCKFILES char *zpenultimate, *zsetnid; #endif /* HAVE_QNX_LOCKFILES */ zlast = pazargs[cargs - 1]; zset = zbufalc (strlen (zlast) + 20); #if HAVE_QNX_LOCKFILES /* We assume in this case that PS_PROGRAM ends with " -n -p". Thus, the last argument is "-p" and the second-to-last (penultimate) argument is "-n". We modify them to read "-n###" and "-p###" where "###" is the node ID and the process ID, respectively. This seems like quite a roundabout way of doing things. Why don't we just leave the " -n -p" part out of PS_PROGRAM and construct the "-n###" and "-p###" arguments here from scratch? Because that would not fit as well with how the code works for the other systems and would require larger changes. */ zpenultimate = pazargs[cargs - 2]; zsetnid = zbufalc (strlen (zpenultimate) + 20); #endif for (i = 0; i < cgot; i++) { pid_t ipid; sprintf (zset, "%s%ld", zlast, (long) pai[i]); pazargs[cargs - 1] = zset; #if HAVE_QNX_LOCKFILES sprintf (zsetnid, "%s%ld", zpenultimate, (long) painid[i]); pazargs[cargs - 2] = zsetnid; #endif ipid = ixsspawn ((const char **) pazargs, aidescs, FALSE, FALSE, (const char *) NULL, FALSE, TRUE, (const char *) NULL, (const char *) NULL, (const char *) NULL); if (ipid < 0) ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); else (void) ixswait ((unsigned long) ipid, PS_PROGRAM); } ubuffree (zset); #if HAVE_QNX_LOCKFILES ubuffree (zsetnid); #endif } #else { char *zlast; int i; pid_t ipid; zlast = zbufalc (strlen (pazargs[cargs - 1]) + cgot * 20 + 1); strcpy (zlast, pazargs[cargs - 1]); for (i = 0; i < cgot; i++) { char ab[20]; sprintf (ab, "%ld", (long) pai[i]); strcat (zlast, ab); if (i + 1 < cgot) strcat (zlast, ","); } pazargs[cargs - 1] = zlast; ipid = ixsspawn ((const char **) pazargs, aidescs, FALSE, FALSE, (const char *) NULL, FALSE, TRUE, (const char *) NULL, (const char *) NULL, (const char *) NULL); if (ipid < 0) ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); else (void) ixswait ((unsigned long) ipid, PS_PROGRAM); ubuffree (zlast); } #endif ubuffree (zcopy); xfree ((pointer) pazargs); return TRUE; } uucp-1.07/unix/status.c0000664000076400007640000001310307665321761010575 /* status.c Routines to get and set the status for a system. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "uuconf.h" #include "sysdep.h" #include "system.h" #include #include #if SPOOLDIR_HDB || SPOOLDIR_SVR4 /* If we are using HDB spool layout, store status using HDB status values. SVR4 is a variant of HDB. */ #define MAP_STATUS 1 static const int aiMapstatus[] = { 0, 13, 7, 6, 20, 4, 3, 2 }; #define CMAPENTRIES (sizeof (aiMapstatus) / sizeof (aiMapstatus[0])) #else /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ #define MAP_STATUS 0 #endif /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ /* Get the status of a system. This assumes that we are in the spool directory. */ boolean fsysdep_get_status (qsys, qret, pfnone) const struct uuconf_system *qsys; struct sstatus *qret; boolean *pfnone; { char *zname; FILE *e; char *zline; char *zend, *znext; boolean fbad; int istat; if (pfnone != NULL) *pfnone = FALSE; zname = zsysdep_in_dir (".Status", qsys->uuconf_zname); e = fopen (zname, "r"); if (e == NULL) { if (errno != ENOENT) { ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno)); ubuffree (zname); return FALSE; } zline = NULL; } else { size_t cline; zline = NULL; cline = 0; if (getline (&zline, &cline, e) <= 0) { xfree ((pointer) zline); zline = NULL; } (void) fclose (e); } if (zline == NULL) { /* There is either no status file for this system, or it's been truncated, so fake a good status. */ qret->ttype = STATUS_COMPLETE; qret->cretries = 0; qret->ilast = 0; qret->cwait = 0; qret->zstring = NULL; if (pfnone != NULL) *pfnone = TRUE; ubuffree (zname); return TRUE; } /* It turns out that scanf is not used much in this program, so for the benefit of small computers we avoid linking it in. This is basically sscanf (zline, "%d %d %ld %d", &qret->ttype, &qret->cretries, &qret->ilast, &qret->cwait); except that it's done with strtol. */ fbad = FALSE; istat = (int) strtol (zline, &zend, 10); if (zend == zline) fbad = TRUE; #if MAP_STATUS /* On some systems it may be appropriate to map system dependent status values on to our status values. */ { int i; for (i = 0; i < CMAPENTRIES; ++i) { if (aiMapstatus[i] == istat) { istat = i; break; } } } #endif /* MAP_STATUS */ if (istat < 0 || istat >= (int) STATUS_VALUES) istat = (int) STATUS_COMPLETE; qret->ttype = (enum tstatus_type) istat; znext = zend; qret->cretries = (int) strtol (znext, &zend, 10); if (zend == znext) fbad = TRUE; znext = zend; qret->ilast = strtol (znext, &zend, 10); if (zend == znext) fbad = TRUE; znext = zend; qret->cwait = (int) strtol (znext, &zend, 10); if (zend == znext) fbad = TRUE; if (! fbad) { znext = zend; while (isspace (BUCHAR (*znext))) ++znext; if (*znext == '\0') qret->zstring = NULL; else { if (*znext == '"') ++znext; qret->zstring = zbufcpy (znext); zend = qret->zstring + strlen (qret->zstring); while (zend != qret->zstring && *zend != ' ') --zend; if (zend != qret->zstring && zend[-1] == '"') --zend; if (zend != qret->zstring) *zend = '\0'; else { ubuffree (qret->zstring); qret->zstring = NULL; } } } xfree ((pointer) zline); if (fbad) { ulog (LOG_ERROR, "%s: Bad status file format", zname); ubuffree (zname); return FALSE; } ubuffree (zname); return TRUE; } /* Set the status of a remote system. This assumes the system is locked when this is called, and that the program is in the spool directory. */ boolean fsysdep_set_status (qsys, qset) const struct uuconf_system *qsys; const struct sstatus *qset; { char *zname; FILE *e; int istat; zname = zsysdep_in_dir (".Status", qsys->uuconf_zname); e = esysdep_fopen (zname, TRUE, FALSE, TRUE); ubuffree (zname); if (e == NULL) return FALSE; istat = (int) qset->ttype; #if MAP_STATUS /* On some systems it may be appropriate to map istat onto a system dependent number. */ if (istat >= 0 && istat < CMAPENTRIES) istat = aiMapstatus[istat]; #endif /* MAP_STATUS */ fprintf (e, "%d %d %ld %d ", istat, qset->cretries, qset->ilast, qset->cwait); #if SPOOLDIR_SVR4 fprintf (e, "\"%s\"", azStatus[(int) qset->ttype]); #else fprintf (e, "%s", azStatus[(int) qset->ttype]); #endif fprintf (e, " %s\n", qsys->uuconf_zname); if (fclose (e) != 0) { ulog (LOG_ERROR, "fclose: %s", strerror (errno)); return FALSE; } return TRUE; } uucp-1.07/unix/sync.c0000664000076400007640000000114407665321761010230 /* sync.c Sync a file to disk, if FSYNC_ON_CLOSE is set. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include boolean fsysdep_sync (e, zmsg) openfile_t e; const char *zmsg; { int o; #if USE_STDIO if (fflush (e) == EOF) { ulog (LOG_ERROR, "%s: fflush: %s", zmsg, strerror (errno)); return FALSE; } #endif #if USE_STDIO o = fileno (e); #else o = e; #endif #if FSYNC_ON_CLOSE if (fsync (o) < 0) { ulog (LOG_ERROR, "%s: fsync: %s", zmsg, strerror (errno)); return FALSE; } #endif return TRUE; } uucp-1.07/unix/tcp.c0000664000076400007640000004072607665321761010053 /* tcp.c Code to handle TCP connections. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char tcp_rcsid[] = "$Id: tcp.c,v 1.12 2002/03/05 19:10:42 ian Rel $"; #endif #if HAVE_TCP #include "uudefs.h" #include "uuconf.h" #include "sysdep.h" #include "conn.h" #include "system.h" #include #if HAVE_SYS_TYPES_TCP_H #include #endif #include #include #include #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif #if HAVE_STRUCT_SOCKADDR_STORAGE typedef struct sockaddr_storage sockaddr_storage; #else typedef struct sockaddr_in sockaddr_storage; #endif /* This code handles TCP connections. It assumes a Berkeley socket interface. */ /* The normal "uucp" port number. */ #define IUUCP_PORT (540) #define ZUUCP_PORT ("540") /* Local functions. */ static void utcp_free P((struct sconnection *qconn)); #if HAVE_GETADDRINFO static boolean ftcp_set_hints P((int iversion, struct addrinfo *qhints)); #endif static boolean ftcp_set_flags P((struct ssysdep_conn *qsysdep)); static boolean ftcp_open P((struct sconnection *qconn, long ibaud, boolean fwait, boolean fuser)); static boolean ftcp_close P((struct sconnection *qconn, pointer puuconf, struct uuconf_dialer *qdialer, boolean fsuccess)); static boolean ftcp_dial P((struct sconnection *qconn, pointer puuconf, const struct uuconf_system *qsys, const char *zphone, struct uuconf_dialer *qdialer, enum tdialerfound *ptdialer)); static int itcp_port_number P((const char *zport)); /* The command table for a TCP connection. */ static const struct sconncmds stcpcmds = { utcp_free, NULL, /* pflock */ NULL, /* pfunlock */ ftcp_open, ftcp_close, ftcp_dial, fsysdep_conn_read, fsysdep_conn_write, fsysdep_conn_io, NULL, /* pfbreak */ NULL, /* pfset */ NULL, /* pfcarrier */ fsysdep_conn_chat, NULL /* pibaud */ }; /* Initialize a TCP connection. */ boolean fsysdep_tcp_init (qconn) struct sconnection *qconn; { struct ssysdep_conn *q; q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn)); q->o = -1; q->ord = -1; q->owr = -1; q->zdevice = NULL; q->iflags = -1; q->iwr_flags = -1; q->fterminal = FALSE; q->ftli = FALSE; q->ibaud = 0; qconn->psysdep = (pointer) q; qconn->qcmds = &stcpcmds; return TRUE; } /* Free a TCP connection. */ static void utcp_free (qconn) struct sconnection *qconn; { xfree (qconn->psysdep); } #if HAVE_GETADDRINFO /* Set the hints for an addrinfo structure from the IP version. */ static boolean ftcp_set_hints (iversion, qhints) int iversion; struct addrinfo *qhints; { switch (iversion) { case 0: qhints->ai_family = 0; break; case 4: qhints->ai_family = PF_INET; break; case 6: #ifdef PF_INET6 qhints->ai_family = PF_INET6; #else ulog (LOG_ERROR, "IPv6 requested but not supported"); return FALSE; #endif break; default: ulog (LOG_ERROR, "Invalid IP version number %d", iversion); return FALSE; } return TRUE; } #endif /* HAVE_GETADDRINFO */ /* Set the close on exec flag for a socket. */ static boolean ftcp_set_flags (qsysdep) struct ssysdep_conn *qsysdep; { if (fcntl (qsysdep->o, F_SETFD, fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); (void) close (qsysdep->o); qsysdep->o = -1; return FALSE; } qsysdep->iflags = fcntl (qsysdep->o, F_GETFL, 0); if (qsysdep->iflags < 0) { ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); (void) close (qsysdep->o); qsysdep->o = -1; return FALSE; } return TRUE; } /* Open a TCP connection. If the fwait argument is TRUE, we are running as a server. Otherwise we are just trying to reach another system. */ static boolean ftcp_open (qconn, ibaud, fwait, fuser) struct sconnection *qconn; long ibaud ATTRIBUTE_UNUSED; boolean fwait; boolean fuser ATTRIBUTE_UNUSED; { struct ssysdep_conn *qsysdep; const char *zport; uid_t ieuid; gid_t iegid; boolean fswap; #if HAVE_GETADDRINFO struct addrinfo shints; struct addrinfo *qres; struct addrinfo *quse; int ierr; #endif ulog_device ("TCP"); qsysdep = (struct ssysdep_conn *) qconn->psysdep; qsysdep->o = -1; /* We save our process ID in the qconn structure. This is checked in ftcp_close. */ qsysdep->ipid = getpid (); /* If we aren't waiting for a connection, we're done. */ if (! fwait) return TRUE; zport = qconn->qport->uuconf_u.uuconf_stcp.uuconf_zport; #if HAVE_GETADDRINFO bzero ((pointer) &shints, sizeof shints); if (! ftcp_set_hints (qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion, &shints)) return FALSE; shints.ai_socktype = SOCK_STREAM; shints.ai_flags = AI_PASSIVE; ierr = getaddrinfo (NULL, zport, &shints, &qres); if (ierr == EAI_SERVICE && strcmp (zport, "uucp") == 0) ierr = getaddrinfo (NULL, ZUUCP_PORT, &shints, &qres); /* If getaddrinfo failed (i.e., ierr != 0), just fall back on using an IPv4 port. Likewise if we can't create a socket using any of the resulting addrinfo structures. */ if (ierr != 0) { ulog (LOG_ERROR, "getaddrinfo: %s", gai_strerror (ierr)); qres = NULL; quse = NULL; } else { for (quse = qres; quse != NULL; quse = quse->ai_next) { qsysdep->o = socket (quse->ai_family, quse->ai_socktype, quse->ai_protocol); if (qsysdep->o >= 0) break; } } #endif /* HAVE_GETADDRINFO */ if (qsysdep->o < 0) { if (qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion != 0 && qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion != 4) { #ifdef HAVE_GETADDRINFO ulog (LOG_ERROR, "Could not get IPv6 socket"); #else ulog (LOG_ERROR, "Only IPv4 sockets are supported"); #endif return FALSE; } qsysdep->o = socket (AF_INET, SOCK_STREAM, 0); if (qsysdep->o < 0) { ulog (LOG_ERROR, "socket: %s", strerror (errno)); return FALSE; } } if (! ftcp_set_flags (qsysdep)) return FALSE; #if HAVE_GETADDRINFO #ifdef IPV6_BINDV6ONLY if (quse != NULL && quse->ai_family == AF_INET6) { int iflag; if (qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion == 0) iflag = 0; else iflag = 1; if (setsockopt (qsysdep->o, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *) &iflag, sizeof (iflag)) < 0) { ulog (LOG_ERROR, "setsockopt (IPV6_BINDONLY): %s", strerror (errno)); (void) close (qsysdep->o); qsysdep->o = -1; freeaddrinfo (qres); return FALSE; } } #endif /* defined (IPV6_BINDV6ONLY) */ #endif /* HAVE_GETADDRINFO */ /* Run as a server and wait for a new connection. The code in uucico.c has already detached us from our controlling terminal. From this point on if the server gets an error we exit; we only return if we have received a connection. It would be more robust to respawn the server if it fails; someday. */ /* Swap to our real user ID when doing the bind call. This will permit the server to use privileged TCP ports when invoked by root. We only swap if our effective user ID is not root, so that the program can also be made suid root in order to get privileged ports when invoked by anybody. */ fswap = geteuid () != 0; if (fswap) { if (! fsuser_perms (&ieuid, &iegid)) { (void) close (qsysdep->o); qsysdep->o = -1; #ifdef HAVE_GETADDRINFO if (qres != NULL) freeaddrinfo (qres); #endif return FALSE; } } #if HAVE_GETADDRINFO if (quse != NULL) { if (bind (qsysdep->o, quse->ai_addr, quse->ai_addrlen) < 0) { if (fswap) (void) fsuucp_perms ((long) ieuid, (long) iegid); ulog (LOG_FATAL, "bind: %s", strerror (errno)); } } else #endif { struct sockaddr_in sin; bzero ((pointer) &sin, sizeof sin); sin.sin_family = AF_INET; sin.sin_port = itcp_port_number (zport); sin.sin_addr.s_addr = htonl (INADDR_ANY); if (bind (qsysdep->o, (struct sockaddr *) &sin, sizeof sin) < 0) { if (fswap) (void) fsuucp_perms ((long) ieuid, (long) iegid); ulog (LOG_FATAL, "bind: %s", strerror (errno)); } } #if HAVE_GETADDRINFO if (qres != NULL) freeaddrinfo (qres); #endif /* Now swap back to the uucp user ID. */ if (fswap) { if (! fsuucp_perms ((long) ieuid, (long) iegid)) ulog (LOG_FATAL, "Could not swap back to UUCP user permissions"); } if (listen (qsysdep->o, 5) < 0) ulog (LOG_FATAL, "listen: %s", strerror (errno)); while (! FGOT_SIGNAL ()) { sockaddr_storage speer; size_t clen; int onew; pid_t ipid; DEBUG_MESSAGE0 (DEBUG_PORT, "ftcp_open: Waiting for connections"); clen = sizeof speer; onew = accept (qsysdep->o, (struct sockaddr *) &speer, &clen); if (onew < 0) ulog (LOG_FATAL, "accept: %s", strerror (errno)); DEBUG_MESSAGE0 (DEBUG_PORT, "ftcp_open: Got connection; forking"); ipid = ixsfork (); if (ipid < 0) ulog (LOG_FATAL, "fork: %s", strerror (errno)); if (ipid == 0) { (void) close (qsysdep->o); qsysdep->o = onew; /* Now we fork and let our parent die, so that we become a child of init. This lets the main server code wait for its child and then continue without accumulating zombie children. */ ipid = ixsfork (); if (ipid < 0) { ulog (LOG_ERROR, "fork: %s", strerror (errno)); _exit (EXIT_FAILURE); } if (ipid != 0) _exit (EXIT_SUCCESS); ulog_id (getpid ()); return TRUE; } (void) close (onew); /* Now wait for the child. */ (void) ixswait ((unsigned long) ipid, (const char *) NULL); } /* We got a signal. */ usysdep_exit (FALSE); /* Avoid compiler warnings. */ return FALSE; } /* Close the port. */ /*ARGSUSED*/ static boolean ftcp_close (qconn, puuconf, qdialer, fsuccess) struct sconnection *qconn; pointer puuconf ATTRIBUTE_UNUSED; struct uuconf_dialer *qdialer ATTRIBUTE_UNUSED; boolean fsuccess ATTRIBUTE_UNUSED; { struct ssysdep_conn *qsysdep; boolean fret; qsysdep = (struct ssysdep_conn *) qconn->psysdep; fret = TRUE; if (qsysdep->o >= 0 && close (qsysdep->o) < 0) { ulog (LOG_ERROR, "close: %s", strerror (errno)); fret = FALSE; } qsysdep->o = -1; /* If the current pid is not the one we used to open the port, then we must have forked up above and we are now the child. In this case, we are being called from within the fendless loop in uucico.c. We return FALSE to force the loop to end and the child to exit. This should be handled in a cleaner fashion. */ if (qsysdep->ipid != getpid ()) fret = FALSE; return fret; } /* Dial out on a TCP port, so to speak: connect to a remote computer. */ /*ARGSUSED*/ static boolean ftcp_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialer) struct sconnection *qconn; pointer puuconf; const struct uuconf_system *qsys; const char *zphone; struct uuconf_dialer *qdialer; enum tdialerfound *ptdialer; { struct ssysdep_conn *qsysdep; const char *zhost; const char *zport; char **pzdialer; #if HAVE_GETADDRINFO struct addrinfo shints; struct addrinfo *qres; struct addrinfo *quse; int ierr; #endif qsysdep = (struct ssysdep_conn *) qconn->psysdep; qsysdep->o = -1; *ptdialer = DIALERFOUND_FALSE; zhost = zphone; if (zhost == NULL) { if (qsys == NULL) { ulog (LOG_ERROR, "No address for TCP connection"); return FALSE; } zhost = qsys->uuconf_zname; } zport = qconn->qport->uuconf_u.uuconf_stcp.uuconf_zport; #if HAVE_GETADDRINFO bzero ((pointer) &shints, sizeof shints); if (! ftcp_set_hints (qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion, &shints)) return FALSE; shints.ai_socktype = SOCK_STREAM; ierr = getaddrinfo (zhost, zport, &shints, &qres); if (ierr == EAI_SERVICE && strcmp (zport, "uucp") == 0) ierr = getaddrinfo (zhost, ZUUCP_PORT, &shints, &qres); /* If getaddrinfo failed (i.e., ierr != 0), just fall back on using an IPv4 port. Likewise if we can't connect using any of the resulting addrinfo structures. */ if (ierr != 0) { ulog (LOG_ERROR, "getaddrinfo: %s", gai_strerror (ierr)); qres = NULL; quse = NULL; ierr = 0; } else { ierr = 0; for (quse = qres; quse != NULL; quse = quse->ai_next) { qsysdep->o = socket (quse->ai_family, quse->ai_socktype, quse->ai_protocol); if (qsysdep->o >= 0) { if (connect (qsysdep->o, quse->ai_addr, quse->ai_addrlen) >= 0) break; ierr = errno; close (qsysdep->o); qsysdep->o = -1; } } if (qres != NULL) freeaddrinfo (qres); } #endif if (qsysdep->o < 0) { struct hostent *qhost; struct sockaddr_in sin; if (qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion != 0 && qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion != 4) { #if HAVE_GETADDRINFO if (ierr != 0) ulog (LOG_ERROR, "connect: %s", strerror (ierr)); else ulog (LOG_ERROR, "Could not get IPv6 address or socket"); #else ulog (LOG_ERROR, "Only IPv4 sockets are supported"); #endif return FALSE; } qsysdep->o = socket (AF_INET, SOCK_STREAM, 0); if (qsysdep->o < 0) { ulog (LOG_ERROR, "socket: %s", strerror (errno)); return FALSE; } errno = 0; bzero ((pointer) &sin, sizeof sin); qhost = gethostbyname ((char *) zhost); if (qhost != NULL) { sin.sin_family = qhost->h_addrtype; memcpy (&sin.sin_addr.s_addr, qhost->h_addr, (size_t) qhost->h_length); } else { if (errno != 0) { ulog (LOG_ERROR, "gethostbyname (%s): %s", zhost, strerror (errno)); return FALSE; } sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr ((char *) zhost); if ((long) sin.sin_addr.s_addr == (long) -1) { ulog (LOG_ERROR, "%s: unknown host name", zhost); return FALSE; } } sin.sin_port = itcp_port_number (zport); if (connect (qsysdep->o, (struct sockaddr *) &sin, sizeof sin) < 0) { ulog (LOG_ERROR, "connect: %s", strerror (errno)); (void) close (qsysdep->o); qsysdep->o = -1; return FALSE; } } if (! ftcp_set_flags (qsysdep)) return FALSE; /* Handle the dialer sequence, if any. */ pzdialer = qconn->qport->uuconf_u.uuconf_stcp.uuconf_pzdialer; if (pzdialer != NULL && *pzdialer != NULL) { if (! fconn_dial_sequence (qconn, puuconf, pzdialer, qsys, zphone, qdialer, ptdialer)) return FALSE; } return TRUE; } /* Get the port number given a name. The argument will almost always be "uucp" so we cache that value. The return value is always in network byte order. This returns -1 on error. */ static int itcp_port_number (zname) const char *zname; { boolean fuucp; static int iuucp; int i; char *zend; struct servent *q; fuucp = strcmp (zname, "uucp") == 0; if (fuucp && iuucp != 0) return iuucp; /* Try it as a number first. */ i = strtol ((char *) zname, &zend, 10); if (i != 0 && *zend == '\0') return htons (i); q = getservbyname ((char *) zname, (char *) "tcp"); if (q == NULL) { /* We know that the "uucp" service should be 540, even if isn't in /etc/services. */ if (fuucp) { iuucp = htons (IUUCP_PORT); return iuucp; } ulog (LOG_ERROR, "getservbyname (%s): %s", zname, strerror (errno)); return -1; } if (fuucp) iuucp = q->s_port; return q->s_port; } #endif /* HAVE_TCP */ uucp-1.07/unix/time.c0000664000076400007640000000117207665321761010213 /* time.c Get the current time. */ #include "uucp.h" #if HAVE_TIME_H #include #endif #include "system.h" #ifndef time extern time_t time (); #endif /* Get the time in seconds since the epoch, with optional microseconds. We use ixsysdep_process_time to get the microseconds if it will work (it won't if it uses times, since that returns a time based only on the process). */ long ixsysdep_time (pimicros) long *pimicros; { #if HAVE_GETTIMEOFDAY || HAVE_FTIME return ixsysdep_process_time (pimicros); #else if (pimicros != NULL) *pimicros = 0; return (long) time ((time_t *) NULL); #endif } uucp-1.07/unix/tli.c0000664000076400007640000003703207665321761010051 /* tli.c Code to handle TLI connections. Copyright (C) 1992, 1993, 1994, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char tli_rcsid[] = "$Id: tli.c,v 1.8 2002/03/05 19:10:42 ian Rel $"; #endif #if HAVE_TLI #include "sysdep.h" #include "uudefs.h" #include "uuconf.h" #include "conn.h" #include "system.h" #include #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_TIUSER_H #include #else #if HAVE_XTI_H #include #else #if HAVE_SYS_TLI_H #include #endif #endif #endif #if HAVE_STROPTS_H #include #endif #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif /* The arguments to t_alloca have two different names. I want the SVID ones, not the XPG3 ones. */ #ifndef T_BIND #define T_BIND T_BIND_STR #endif #ifndef T_CALL #define T_CALL T_CALL_STR #endif /* Hopefully these externs will not cause any trouble. This is how they are shown in the SVID. */ extern int t_errno; extern char *t_errlist[]; extern int t_nerr; #ifndef HAVE_TIUSER_H #ifndef t_alloc extern pointer t_alloc (); #endif #endif /* This code handles TLI connections. It's Unix specific. It's largely based on code from Unix Network Programming, by W. Richard Stevens. */ /* Local functions. */ static const char *ztlierror P((void)); static void utli_free P((struct sconnection *qconn)); static boolean ftli_push P((struct sconnection *qconn)); static boolean ftli_open P((struct sconnection *qconn, long ibaud, boolean fwait, boolean fuser)); static boolean ftli_close P((struct sconnection *qconn, pointer puuconf, struct uuconf_dialer *qdialer, boolean fsuccess)); static boolean ftli_dial P((struct sconnection *qconn, pointer puuconf, const struct uuconf_system *qsys, const char *zphone, struct uuconf_dialer *qdialer, enum tdialerfound *ptdialer)); /* The command table for a TLI connection. */ static const struct sconncmds stlicmds = { utli_free, NULL, /* pflock */ NULL, /* pfunlock */ ftli_open, ftli_close, ftli_dial, fsysdep_conn_read, fsysdep_conn_write, fsysdep_conn_io, NULL, /* pfbreak */ NULL, /* pfset */ NULL, /* pfcarrier */ fsysdep_conn_chat, NULL /* pibaud */ }; /* Get a TLI error string. */ static const char * ztlierror () { if (t_errno == TSYSERR) return strerror (errno); if (t_errno < 0 || t_errno >= t_nerr) return "Unknown TLI error"; return t_errlist[t_errno]; } /* Initialize a TLI connection. This may be called with qconn->qport NULL, when opening standard input as a TLI connection. */ boolean fsysdep_tli_init (qconn) struct sconnection *qconn; { struct ssysdep_conn *q; q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn)); q->o = -1; q->ord = -1; q->owr = -1; q->zdevice = NULL; q->iflags = -1; q->iwr_flags = -1; q->fterminal = FALSE; q->ftli = TRUE; q->ibaud = 0; qconn->psysdep = (pointer) q; qconn->qcmds = &stlicmds; return TRUE; } /* Free a TLI connection. */ static void utli_free (qconn) struct sconnection *qconn; { xfree (qconn->psysdep); } /* Push all desired modules onto a TLI stream. If the user requests a STREAMS connection without giving a list of modules, we just push tirdwr. If the I_PUSH ioctl is not defined on this system, we just ignore any list of modules. */ static boolean ftli_push (qconn) struct sconnection *qconn; { #ifdef I_PUSH struct ssysdep_conn *qsysdep; qsysdep = (struct ssysdep_conn *) qconn->psysdep; if (qconn->qport->uuconf_u.uuconf_stli.uuconf_pzpush != NULL) { char **pz; for (pz = qconn->qport->uuconf_u.uuconf_stli.uuconf_pzpush; *pz != NULL; pz++) { if (ioctl (qsysdep->o, I_PUSH, *pz) < 0) { ulog (LOG_ERROR, "ioctl (I_PUSH, %s): %s", *pz, strerror (errno)); return FALSE; } } } else if (qconn->qport->uuconf_u.uuconf_stli.uuconf_fstream) { if (ioctl (qsysdep->o, I_PUSH, "tirdwr") < 0) { ulog (LOG_ERROR, "ioctl (I_PUSH, tirdwr): %s", strerror (errno)); return FALSE; } } /* If we have just put the connection into stream mode, we must turn off the TLI flag to avoid using TLI calls on it. */ if (qconn->qport->uuconf_u.uuconf_stli.uuconf_fstream) qsysdep->ftli = FALSE; #endif /* defined (I_PUSH) */ return TRUE; } /* Open a TLI connection. If the fwait argument is TRUE, we are running as a server. Otherwise we are just trying to reach another system. */ static boolean ftli_open (qconn, ibaud, fwait, fuser) struct sconnection *qconn; long ibaud; boolean fwait; boolean fuser ATTRIBUTE_UNUSED; { struct ssysdep_conn *qsysdep; const char *zdevice; char *zfreedev; const char *zservaddr; char *zfreeaddr; uid_t ieuid; gid_t iegid; boolean fswap; struct t_bind *qtbind; struct t_call *qtcall; /* Unlike most other device types, we don't bother to call ulog_device here, because fconn_open calls it with the name of the port anyhow. */ qsysdep = (struct ssysdep_conn *) qconn->psysdep; zdevice = qconn->qport->uuconf_u.uuconf_stli.uuconf_zdevice; if (zdevice == NULL) zdevice = qconn->qport->uuconf_zname; zfreedev = NULL; if (*zdevice != '/') { zfreedev = zbufalc (sizeof "/dev/" + strlen (zdevice)); sprintf (zfreedev, "/dev/%s", zdevice); zdevice = zfreedev; } /* If we are acting as a server, swap to our real user ID before calling t_open. This will permit the server to use privileged TCP ports when invoked by root. We only swap if our effective user ID is not root, so that the program can also be made suid root in order to get privileged ports when invoked by anybody. */ fswap = fwait && geteuid () != 0; if (fswap) { if (! fsuser_perms (&ieuid, &iegid)) { ubuffree (zfreedev); return FALSE; } } qsysdep->o = t_open (zdevice, O_RDWR, (struct t_info *) NULL); if (qsysdep->o < 0) { if (fswap) (void) fsuucp_perms ((long) ieuid, (long) iegid); ulog (LOG_ERROR, "t_open (%s): %s", zdevice, ztlierror ()); ubuffree (zfreedev); return FALSE; } if (fcntl (qsysdep->o, F_SETFD, fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0) { if (fswap) (void) fsuucp_perms ((long) ieuid, (long) iegid); ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); ubuffree (zfreedev); (void) t_close (qsysdep->o); qsysdep->o = -1; return FALSE; } qsysdep->iflags = fcntl (qsysdep->o, F_GETFL, 0); if (qsysdep->iflags < 0) { if (fswap) (void) fsuucp_perms ((long) ieuid, (long) iegid); ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); ubuffree (zfreedev); (void) t_close (qsysdep->o); qsysdep->o = -1; return FALSE; } /* We save our process ID in the qconn structure. This is checked in ftli_close. */ qsysdep->ipid = getpid (); /* If we aren't waiting for a connection, we can bind to any local address, and then we're finished. */ if (! fwait) { /* fswap is known to be FALSE here. */ ubuffree (zfreedev); if (t_bind (qsysdep->o, (struct t_bind *) NULL, (struct t_bind *) NULL) < 0) { ulog (LOG_ERROR, "t_bind: %s", ztlierror ()); (void) t_close (qsysdep->o); qsysdep->o = -1; return FALSE; } return TRUE; } /* Run as a server and wait for a new connection. The code in uucico.c has already detached us from our controlling terminal. From this point on if the server gets an error we exit; we only return if we have received a connection. It would be more robust to respawn the server if it fails; someday. */ qtbind = (struct t_bind *) t_alloc (qsysdep->o, T_BIND, T_ALL); if (qtbind == NULL) { if (fswap) (void) fsuucp_perms ((long) ieuid, (long) iegid); ulog (LOG_FATAL, "t_alloc (T_BIND): %s", ztlierror ()); } zservaddr = qconn->qport->uuconf_u.uuconf_stli.uuconf_zservaddr; if (zservaddr == NULL) { if (fswap) (void) fsuucp_perms ((long) ieuid, (long) iegid); ulog (LOG_FATAL, "Can't run as TLI server; no server address"); } zfreeaddr = zbufcpy (zservaddr); qtbind->addr.len = cescape (zfreeaddr); if (qtbind->addr.len > qtbind->addr.maxlen) { if (fswap) (void) fsuucp_perms ((long) ieuid, (long) iegid); ulog (LOG_FATAL, "%s: TLI server address too long (max %d)", zservaddr, qtbind->addr.maxlen); } memcpy (qtbind->addr.buf, zfreeaddr, qtbind->addr.len); ubuffree (zfreeaddr); qtbind->qlen = 5; if (t_bind (qsysdep->o, qtbind, (struct t_bind *) NULL) < 0) { if (fswap) (void) fsuucp_perms ((long) ieuid, (long) iegid); ulog (LOG_FATAL, "t_bind (%s): %s", zservaddr, ztlierror ()); } if (fswap) { if (! fsuucp_perms ((long) ieuid, (long) iegid)) ulog (LOG_FATAL, "Could not swap back to UUCP user permissions"); } (void) t_free ((pointer) qtbind, T_BIND); qtcall = (struct t_call *) t_alloc (qsysdep->o, T_CALL, T_ALL); if (qtcall == NULL) ulog (LOG_FATAL, "t_alloc (T_CALL): %s", ztlierror ()); while (! FGOT_SIGNAL ()) { int onew; pid_t ipid; DEBUG_MESSAGE0 (DEBUG_PORT, "ftli_open: Waiting for connections"); if (t_listen (qsysdep->o, qtcall) < 0) ulog (LOG_FATAL, "t_listen: %s", ztlierror ()); onew = t_open (zdevice, O_RDWR, (struct t_info *) NULL); if (onew < 0) ulog (LOG_FATAL, "t_open (%s): %s", zdevice, ztlierror ()); if (fcntl (onew, F_SETFD, fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0) ulog (LOG_FATAL, "fcntl (FD_CLOEXEC): %s", strerror (errno)); if (t_bind (onew, (struct t_bind *) NULL, (struct t_bind *) NULL) < 0) ulog (LOG_FATAL, "t_bind: %s", ztlierror ()); if (t_accept (qsysdep->o, onew, qtcall) < 0) { /* We may have received a disconnect. */ if (t_errno != TLOOK) ulog (LOG_FATAL, "t_accept: %s", ztlierror ()); if (t_rcvdis (qsysdep->o, (struct t_discon *) NULL) < 0) ulog (LOG_FATAL, "t_rcvdis: %s", ztlierror ()); (void) t_close (onew); continue; } DEBUG_MESSAGE0 (DEBUG_PORT, "ftli_open: Got connection; forking"); ipid = ixsfork (); if (ipid < 0) ulog (LOG_FATAL, "fork: %s", strerror (errno)); if (ipid == 0) { ulog_close (); (void) t_close (qsysdep->o); qsysdep->o = onew; /* Push any desired modules. */ if (! ftli_push (qconn)) _exit (EXIT_FAILURE); /* Now we fork and let our parent die, so that we become a child of init. This lets the main server code wait for its child and then continue without accumulating zombie children. */ ipid = ixsfork (); if (ipid < 0) { ulog (LOG_ERROR, "fork: %s", strerror (errno)); _exit (EXIT_FAILURE); } if (ipid != 0) _exit (EXIT_SUCCESS); ulog_id (getpid ()); return TRUE; } (void) t_close (onew); /* Now wait for the child. */ (void) ixswait ((unsigned long) ipid, (const char *) NULL); } /* We got a signal. */ usysdep_exit (FALSE); /* Avoid compiler warnings. */ return FALSE; } /* Close the port. */ /*ARGSUSED*/ static boolean ftli_close (qconn, puuconf, qdialer, fsuccess) struct sconnection *qconn; pointer puuconf; struct uuconf_dialer *qdialer; boolean fsuccess; { struct ssysdep_conn *qsysdep; boolean fret; qsysdep = (struct ssysdep_conn *) qconn->psysdep; fret = TRUE; if (qsysdep->o >= 0) { if (qsysdep->ftli) { if (t_close (qsysdep->o) < 0) { ulog (LOG_ERROR, "t_close: %s", ztlierror ()); fret = FALSE; } } else { if (close (qsysdep->o) < 0) { ulog (LOG_ERROR, "close: %s", strerror (errno)); fret = FALSE; } } qsysdep->o = -1; } /* If the current pid is not the one we used to open the port, then we must have forked up above and we are now the child. In this case, we are being called from within the fendless loop in uucico.c. We return FALSE to force the loop to end and the child to exit. This should be handled in a cleaner fashion. */ if (qsysdep->ipid != getpid ()) fret = FALSE; return fret; } /* Dial out on a TLI port, so to speak: connect to a remote computer. */ /*ARGSUSED*/ static boolean ftli_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound) struct sconnection *qconn; pointer puuconf; const struct uuconf_system *qsys; const char *zphone; struct uuconf_dialer *qdialer; enum tdialerfound *ptdialerfound; { struct ssysdep_conn *qsysdep; char **pzdialer; const char *zaddr; struct t_call *qtcall; char *zescape; qsysdep = (struct ssysdep_conn *) qconn->psysdep; *ptdialerfound = DIALERFOUND_FALSE; pzdialer = qconn->qport->uuconf_u.uuconf_stli.uuconf_pzdialer; if (*pzdialer == NULL) pzdialer = NULL; /* If the first dialer is "TLI" or "TLIS", we use the first token (pzdialer[1]) as the address to connect to. */ zaddr = zphone; if (pzdialer != NULL && (strcmp (pzdialer[0], "TLI") == 0 || strcmp (pzdialer[0], "TLIS") == 0)) { if (pzdialer[1] == NULL) ++pzdialer; else { if (strcmp (pzdialer[1], "\\D") != 0 && strcmp (pzdialer[1], "\\T") != 0) zaddr = pzdialer[1]; pzdialer += 2; } } if (zaddr == NULL) { ulog (LOG_ERROR, "No address for TLI connection"); return FALSE; } qtcall = (struct t_call *) t_alloc (qsysdep->o, T_CALL, T_ADDR); if (qtcall == NULL) { ulog (LOG_ERROR, "t_alloc (T_CALL): %s", ztlierror ()); return FALSE; } zescape = zbufcpy (zaddr); qtcall->addr.len = cescape (zescape); if (qtcall->addr.len > qtcall->addr.maxlen) { ulog (LOG_ERROR, "%s: TLI address too long (max %d)", zaddr, qtcall->addr.maxlen); ubuffree (zescape); return FALSE; } memcpy (qtcall->addr.buf, zescape, qtcall->addr.len); ubuffree (zescape); if (t_connect (qsysdep->o, qtcall, (struct t_call *) NULL) < 0) { if (t_errno != TLOOK) ulog (LOG_ERROR, "t_connect: %s", ztlierror ()); else { if (t_rcvdis (qsysdep->o, (struct t_discon *) NULL) < 0) ulog (LOG_ERROR, "t_rcvdis: %s", ztlierror ()); else ulog (LOG_ERROR, "Connection refused"); } return FALSE; } /* We've connected to the remote. Push any desired modules. */ if (! ftli_push (qconn)) return FALSE; /* Handle the rest of the dialer sequence. */ if (pzdialer != NULL && *pzdialer != NULL) { if (! fconn_dial_sequence (qconn, puuconf, pzdialer, qsys, zphone, qdialer, ptdialerfound)) return FALSE; } return TRUE; } #endif /* HAVE_TLI */ uucp-1.07/unix/tmpfil.c0000664000076400007640000000373007665321761010552 /* tmpfil.c Get a temporary file name. Copyright (C) 1991, 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "sysdep.h" #define ZDIGS \ "0123456789abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-" #define CDIGS (sizeof ZDIGS - 1) /*ARGSUSED*/ char * zstemp_file (qsys) const struct uuconf_system *qsys ATTRIBUTE_UNUSED; { static unsigned int icount; const char *const zdigs = ZDIGS; char ab[14]; pid_t ime; int iset; ab[0] = 'T'; ab[1] = 'M'; ab[2] = '.'; ime = getpid (); iset = 3; while (ime > 0 && iset < 10) { ab[iset] = zdigs[ime % CDIGS]; ime /= CDIGS; ++iset; } ab[iset] = '.'; ++iset; ab[iset] = zdigs[icount / CDIGS]; ++iset; ab[iset] = zdigs[icount % CDIGS]; ++iset; ab[iset] = '\0'; ++icount; if (icount >= CDIGS * CDIGS) icount = 0; #if SPOOLDIR_V2 || SPOOLDIR_BSD42 return zbufcpy (ab); #endif #if SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR return zsysdep_in_dir (".Temp", ab); #endif #if SPOOLDIR_HDB || SPOOLDIR_SVR4 return zsysdep_in_dir (qsys->uuconf_zname, ab); #endif } uucp-1.07/unix/trunc.c0000664000076400007640000000631207665321761010411 /* trunc.c Truncate a file to zero length. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif /* External functions. */ #ifndef lseek extern off_t lseek (); #endif /* Truncate a file to zero length. If this fails, it closes and removes the file. We support a number of different means of truncation, which is probably a waste of time since this function is currently only called when the 'f' protocol resends a file. */ #if HAVE_FTRUNCATE #undef HAVE_LTRUNC #define HAVE_LTRUNC 0 #endif #if ! HAVE_FTRUNCATE && ! HAVE_LTRUNC #ifdef F_CHSIZE #define HAVE_F_CHSIZE 1 #else /* ! defined (F_CHSIZE) */ #ifdef F_FREESP #define HAVE_F_FREESP 1 #endif /* defined (F_FREESP) */ #endif /* ! defined (F_CHSIZE) */ #endif /* ! HAVE_FTRUNCATE && ! HAVE_LTRUNC */ openfile_t esysdep_truncate (e, zname) openfile_t e; const char *zname; { int o; #if HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP int itrunc; if (! ffilerewind (e)) { ulog (LOG_ERROR, "rewind: %s", strerror (errno)); (void) ffileclose (e); (void) remove (zname); return EFILECLOSED; } #if USE_STDIO o = fileno (e); #else o = e; #endif #if HAVE_FTRUNCATE itrunc = ftruncate (o, 0); #endif #if HAVE_LTRUNC itrunc = ltrunc (o, (long) 0, SEEK_SET); #endif #if HAVE_F_CHSIZE itrunc = fcntl (o, F_CHSIZE, (off_t) 0); #endif #if HAVE_F_FREESP /* This selection is based on an implementation of ftruncate by kucharsk@Solbourne.com (William Kucharski). */ { struct flock fl; fl.l_whence = 0; fl.l_len = 0; fl.l_start = 0; fl.l_type = F_WRLCK; itrunc = fcntl (o, F_FREESP, &fl); } #endif if (itrunc != 0) { #if HAVE_FTRUNCATE ulog (LOG_ERROR, "ftruncate: %s", strerror (errno)); #endif #ifdef HAVE_LTRUNC ulog (LOG_ERROR, "ltrunc: %s", strerror (errno)); #endif #ifdef HAVE_F_CHSIZE ulog (LOG_ERROR, "fcntl (F_CHSIZE): %s", strerror (errno)); #endif #ifdef HAVE_F_FREESP ulog (LOG_ERROR, "fcntl (F_FREESP): %s", strerror (errno)); #endif (void) ffileclose (e); (void) remove (zname); return EFILECLOSED; } return e; #else /* ! (HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP) */ (void) ffileclose (e); (void) remove (zname); o = creat ((char *) zname, IPRIVATE_FILE_MODE); if (o == -1) { ulog (LOG_ERROR, "creat (%s): %s", zname, strerror (errno)); return EFILECLOSED; } if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); (void) close (o); return EFILECLOSED; } #if USE_STDIO e = fdopen (o, (char *) BINWRITE); if (e == NULL) { ulog (LOG_ERROR, "fdopen (%s): %s", zname, strerror (errno)); (void) close (o); (void) remove (zname); return NULL; } #else /* ! USE_STDIO */ e = o; #endif /* ! USE_STDIO */ return e; #endif /* ! (HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP) */ } uucp-1.07/unix/uacces.c0000664000076400007640000001065407665321761010525 /* uacces.c Check access to a file by user name. Copyright (C) 1992, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include #include #if HAVE_GETGRENT #include #if GETGRENT_DECLARATION_OK #ifndef getgrent extern struct group *getgrent (); #endif #endif #endif /* HAVE_GETGRENT */ #if GETPWNAM_DECLARATION_OK #ifndef getpwnam extern struct passwd *getpwnam (); #endif #endif /* Do access(2) on a stat structure, except that the user name is provided. If the user name in zuser is NULL, require the file to be accessible to the world. Return TRUE if access is permitted, FALSE otherwise. This does not log an error message. */ boolean fsuser_access (q, imode, zuser) const struct stat *q; int imode; const char *zuser; { static char *zuser_hold; static uid_t iuid_hold; static gid_t igid_hold; static int cgroups_hold; static gid_t *paigroups_hold; unsigned int ir, iw, ix, iand; if (imode == F_OK) return TRUE; if (zuser != NULL) { /* We keep static variables around for the last user we did, to avoid looking up a user multiple times. */ if (zuser_hold == NULL || strcmp (zuser_hold, zuser) != 0) { struct passwd *qpwd; if (zuser_hold != NULL) { ubuffree (zuser_hold); zuser_hold = NULL; cgroups_hold = 0; xfree ((pointer) paigroups_hold); paigroups_hold = NULL; } qpwd = getpwnam ((char *) zuser); if (qpwd == NULL) { /* Check this as a remote request. */ zuser = NULL; } else { #if HAVE_GETGRENT struct group *qg; #endif zuser_hold = zbufcpy (zuser); iuid_hold = qpwd->pw_uid; igid_hold = qpwd->pw_gid; #if HAVE_GETGRENT /* Get the list of groups for this user. This is definitely more appropriate for BSD than for System V. It may just be a waste of time, and perhaps it should be configurable. */ setgrent (); while ((qg = getgrent ()) != NULL) { const char **pz; if (qg->gr_gid == igid_hold) continue; for (pz = (const char **) qg->gr_mem; *pz != NULL; pz++) { if ((*pz)[0] == *zuser && strcmp (*pz, zuser) == 0) { paigroups_hold = ((gid_t *) (xrealloc ((pointer) paigroups_hold, ((cgroups_hold + 1) * sizeof (gid_t))))); paigroups_hold[cgroups_hold] = qg->gr_gid; ++cgroups_hold; break; } } } endgrent (); #endif } } } /* Now do the actual access check. */ if (zuser != NULL) { /* The superuser can do anything. */ if (iuid_hold == 0) return TRUE; /* If this is the uid we're running under, there's no point to checking access further, because when we actually try the operation the system will do the checking for us. */ if (iuid_hold == geteuid ()) return TRUE; } ir = S_IROTH; iw = S_IWOTH; ix = S_IXOTH; if (zuser != NULL) { if (iuid_hold == q->st_uid) { ir = S_IRUSR; iw = S_IWUSR; ix = S_IXUSR; } else { boolean fgroup; fgroup = FALSE; if (igid_hold == q->st_gid) fgroup = TRUE; else { int i; for (i = 0; i < cgroups_hold; i++) { if (paigroups_hold[i] == q->st_gid) { fgroup = TRUE; break; } } } if (fgroup) { ir = S_IRGRP; iw = S_IWGRP; ix = S_IXGRP; } } } iand = 0; if ((imode & R_OK) != 0) iand |= ir; if ((imode & W_OK) != 0) iand |= iw; if ((imode & X_OK) != 0) iand |= ix; return (q->st_mode & iand) == iand; } uucp-1.07/unix/ufopen.c0000664000076400007640000000577707665321761010570 /* ufopen.c Open a file with the permissions of the invoking user. Copyright (C) 1992, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif /* Open a file with the permissions of the invoking user. Ignore the fbinary argument since Unix has no distinction between text and binary files. */ /*ARGSUSED*/ openfile_t esysdep_user_fopen (zfile, frd, fbinary) const char *zfile; boolean frd; boolean fbinary ATTRIBUTE_UNUSED; { uid_t ieuid; gid_t iegid; openfile_t e; const char *zerr; int o = 0; if (! fsuser_perms (&ieuid, &iegid)) return EFILECLOSED; zerr = NULL; #if USE_STDIO e = fopen (zfile, frd ? "r" : "w"); if (e == NULL) zerr = "fopen"; else o = fileno (e); #else if (frd) { e = open ((char *) zfile, O_RDONLY | O_NOCTTY, 0); zerr = "open"; } else { e = creat ((char *) zfile, IPUBLIC_FILE_MODE); zerr = "creat"; } if (e >= 0) { o = e; zerr = NULL; } #endif if (! fsuucp_perms ((long) ieuid, (long) iegid)) { if (ffileisopen (e)) (void) ffileclose (e); return EFILECLOSED; } if (zerr != NULL) { ulog (LOG_ERROR, "%s (%s): %s", zerr, zfile, strerror (errno)); #if ! HAVE_SETREUID /* Are these error messages helpful or confusing? */ #if HAVE_SAVED_SETUID if (errno == EACCES && getuid () == 0) ulog (LOG_ERROR, "The superuser may only transfer files that are readable by %s", OWNER); #else if (errno == EACCES) ulog (LOG_ERROR, "You may only transfer files that are readable by %s", OWNER); #endif #endif /* ! HAVE_SETREUID */ return EFILECLOSED; } if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); (void) ffileclose (e); return EFILECLOSED; } return e; } uucp-1.07/unix/uid.c0000664000076400007640000001004307665321761010033 /* uid.c Switch back and forth between UUCP and user permissions. Copyright (C) 1992, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include /* NetBSD apparently does not support setuid as required by POSIX when using saved setuid, so use seteuid instead. */ #if HAVE_SETEUID #define setuid seteuid #endif /* Switch to permissions of the invoking user. */ boolean fsuser_perms (pieuid, piegid) uid_t *pieuid; gid_t *piegid; { uid_t ieuid, iuid; gid_t iegid, igid; ieuid = geteuid (); iuid = getuid (); if (pieuid != NULL) *pieuid = ieuid; iegid = getegid (); igid = getgid (); if (piegid != NULL) *piegid = iegid; #if HAVE_SETREUID /* Swap the effective user id and the real user id. We can then swap them back again when we want to return to the uucp user's permissions. */ if (setregid (iegid, igid) < 0) { ulog (LOG_ERROR, "setregid (%ld, %ld): %s", (long) iegid, (long) igid, strerror (errno)); return FALSE; } if (setreuid (ieuid, iuid) < 0) { ulog (LOG_ERROR, "setreuid (%ld, %ld): %s", (long) ieuid, (long) iuid, strerror (errno)); return FALSE; } #else /* ! HAVE_SETREUID */ #if HAVE_SAVED_SETUID /* Set the effective user id to the real user id. Since the effective user id is saved (it's the saved setuid) we will able to set back to it later. If the real user id is root we will not be able to switch back and forth, so don't even try. */ if (iuid != 0) { if (setgid (igid) < 0) { ulog (LOG_ERROR, "setgid (%ld): %s", (long) igid, strerror (errno)); return FALSE; } if (setuid (iuid) < 0) { ulog (LOG_ERROR, "setuid (%ld): %s", (long) iuid, strerror (errno)); return FALSE; } } #else /* ! HAVE_SAVED_SETUID */ /* There's no way to switch between real permissions and effective permissions. Just try to open the file with the uucp permissions. */ #endif /* ! HAVE_SAVED_SETUID */ #endif /* ! HAVE_SETREUID */ return TRUE; } /* Restore the uucp permissions. */ /*ARGSUSED*/ boolean fsuucp_perms (ieuid, iegid) long ieuid ATTRIBUTE_UNUSED; long iegid ATTRIBUTE_UNUSED; { #if HAVE_SETREUID /* Swap effective and real user id's back to what they were. */ if (! fsuser_perms ((uid_t *) NULL, (gid_t *) NULL)) return FALSE; #else /* ! HAVE_SETREUID */ #if HAVE_SAVED_SETUID /* Set ourselves back to our original effective user id. */ if (setgid ((gid_t) iegid) < 0) { ulog (LOG_ERROR, "setgid (%ld): %s", (long) iegid, strerror (errno)); /* Is this error message helpful or confusing? */ if (errno == EPERM) ulog (LOG_ERROR, "Probably HAVE_SAVED_SETUID in policy.h should be set to 0"); return FALSE; } if (setuid ((uid_t) ieuid) < 0) { ulog (LOG_ERROR, "setuid (%ld): %s", (long) ieuid, strerror (errno)); /* Is this error message helpful or confusing? */ if (errno == EPERM) ulog (LOG_ERROR, "Probably HAVE_SAVED_SETUID in policy.h should be set to 0"); return FALSE; } #else /* ! HAVE_SAVED_SETUID */ /* We didn't switch, no need to switch back. */ #endif /* ! HAVE_SAVED_SETUID */ #endif /* ! HAVE_SETREUID */ return TRUE; } uucp-1.07/unix/ultspl.c0000664000076400007640000000054607665321761010604 /* ultspl.c See whether there is an Ultrix spool directory for a system. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" boolean fsultrix_has_spool (zsystem) const char *zsystem; { char *z; boolean fret; z = zsysdep_in_dir ("sys", zsystem); fret = fsysdep_directory (z); ubuffree (z); return fret; } uucp-1.07/unix/umode.c0000664000076400007640000000131707665321761010367 /* umode.c Get the Unix file mode of a file using the user's permissions. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include unsigned int ixsysdep_user_file_mode (zfile) const char *zfile; { uid_t ieuid; gid_t iegid; int iret; struct stat s; if (! fsuser_perms (&ieuid, &iegid)) return 0; iret = stat ((char *) zfile, &s); if (! fsuucp_perms ((long) ieuid, (long) iegid)) return 0; if (iret != 0) { ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); return 0; } /* We can't return 0, since that indicates an error. */ if ((s.st_mode & 0777) == 0) return 0400; return s.st_mode & 0777; } uucp-1.07/unix/unknwn.c0000664000076400007640000000173207665321761010577 /* unknwn.c Check remote.unknown shell script. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include /* Run the remote.unknown shell script. If it succeeds, we return FALSE because that means that the system is not permitted to log in. If the execution fails, we return TRUE. */ boolean fsysdep_unknown_caller (zscript, zsystem) const char *zscript; const char *zsystem; { const char *azargs[3]; int aidescs[3]; pid_t ipid; azargs[0] = zscript; azargs[1] = zsystem; azargs[2] = NULL; aidescs[0] = SPAWN_NULL; aidescs[1] = SPAWN_NULL; aidescs[2] = SPAWN_NULL; ipid = ixsspawn (azargs, aidescs, TRUE, TRUE, (const char *) NULL, FALSE, TRUE, (const char *) NULL, (const char *) NULL, (const char *) NULL); if (ipid < 0) { ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); return FALSE; } return ixswait ((unsigned long) ipid, (const char *) NULL) != 0; } uucp-1.07/unix/uuto.c0000664000076400007640000000123007665321761010244 /* uuto.c Translate a destination for uuto. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" /* Translate a uuto destination for Unix. */ char * zsysdep_uuto (zdest, zlocalname) const char *zdest; const char *zlocalname; { const char *zexclam; char *zto; zexclam = strrchr (zdest, '!'); if (zexclam == NULL) return NULL; zto = (char *) zbufalc (zexclam - zdest + sizeof "!~/receive///" + strlen (zexclam) + strlen (zlocalname)); memcpy (zto, zdest, (size_t) (zexclam - zdest)); sprintf (zto + (zexclam - zdest), "!~/receive/%s/%s/", zexclam + 1, zlocalname); return zto; } uucp-1.07/unix/walk.c0000664000076400007640000000211307665321761010207 /* walk.c Walk a directory tree. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #if HAVE_FTW_H #include #endif static int iswalk_dir P((const char *zname, const struct stat *qstat, int iflag)); /* Walk a directory tree. */ static size_t cSlen; static void (*puSfn) P((const char *zfull, const char *zrelative, pointer pinfo)); static pointer pSinfo; boolean usysdep_walk_tree (zdir, pufn, pinfo) const char *zdir; void (*pufn) P((const char *zfull, const char *zrelative, pointer pinfo)); pointer pinfo; { cSlen = strlen (zdir) + 1; puSfn = pufn; pSinfo = pinfo; return ftw ((char *) zdir, iswalk_dir, 5) == 0; } /* Pass a file found in the directory tree to the system independent function. */ /*ARGSUSED*/ static int iswalk_dir (zname, qstat, iflag) const char *zname; const struct stat *qstat ATTRIBUTE_UNUSED; int iflag; { char *zcopy; if (iflag != FTW_F) return 0; zcopy = zbufcpy (zname + cSlen); (*puSfn) (zname, zcopy, pSinfo); ubuffree (zcopy); return 0; } uucp-1.07/unix/wldcrd.c0000664000076400007640000001060107665321761010531 /* wldcrd.c Expand wildcards. Copyright (C) 1991, 1992, 1993, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "uudefs.h" #include "sysdep.h" #include "system.h" #include #include #if HAVE_GLOB && ! HAVE_GLOB_H #undef HAVE_GLOB #define HAVE_GLOB 0 #endif #if HAVE_GLOB #include #endif /* Local variables to hold the wildcard in progress. */ #if HAVE_GLOB static glob_t sSglob; static unsigned int iSglob; #else static char *zSwildcard_alloc; static char *zSwildcard; #endif /* Start getting a wildcarded file spec. Use the glob function if it is available, and otherwise use the shell. */ boolean fsysdep_wildcard_start (zfile) const char *zfile; { #if HAVE_GLOB #if DEBUG > 0 if (*zfile != '/') ulog (LOG_FATAL, "fsysdep_wildcard: %s: Can't happen", zfile); #endif if (glob (zfile, 0, (int (*) ()) NULL, &sSglob) != 0) sSglob.gl_pathc = 0; iSglob = 0; return TRUE; #else /* ! HAVE_GLOB */ char *zcmd, *zto; const char *zfrom; size_t c; const char *azargs[4]; FILE *e; pid_t ipid; #if DEBUG > 0 if (*zfile != '/') ulog (LOG_FATAL, "fsysdep_wildcard: %s: Can't happen", zfile); #endif zSwildcard_alloc = NULL; zSwildcard = NULL; zcmd = zbufalc (sizeof ECHO_PROGRAM + sizeof " " + 2 * strlen (zfile)); memcpy (zcmd, ECHO_PROGRAM, sizeof ECHO_PROGRAM - 1); zto = zcmd + sizeof ECHO_PROGRAM - 1; *zto++ = ' '; zfrom = zfile; while (*zfrom != '\0') { /* To avoid shell trickery, we quote all characters except letters, digits, and wildcard specifiers. We don't quote '/' to avoid an Ultrix sh bug. */ if (! isalnum (*zfrom) && *zfrom != '*' && *zfrom != '?' && *zfrom != '[' && *zfrom != ']' && *zfrom != '/') *zto++ = '\\'; *zto++ = *zfrom++; } *zto = '\0'; azargs[0] = "/bin/sh"; azargs[1] = "-c"; azargs[2] = zcmd; azargs[3] = NULL; e = espopen (azargs, TRUE, &ipid); ubuffree (zcmd); if (e == NULL) { ulog (LOG_ERROR, "espopen: %s", strerror (errno)); return FALSE; } zSwildcard_alloc = NULL; c = 0; if (getline (&zSwildcard_alloc, &c, e) <= 0) { xfree ((pointer) zSwildcard_alloc); zSwildcard_alloc = NULL; } if (ixswait ((unsigned long) ipid, ECHO_PROGRAM) != 0) { xfree ((pointer) zSwildcard_alloc); return FALSE; } if (zSwildcard_alloc == NULL) return FALSE; DEBUG_MESSAGE1 (DEBUG_EXECUTE, "fsysdep_wildcard_start: got \"%s\"", zSwildcard_alloc); zSwildcard = zSwildcard_alloc; return TRUE; #endif /* ! HAVE_GLOB */ } /* Get the next wildcard spec. */ /*ARGSUSED*/ char * zsysdep_wildcard (zfile) const char *zfile ATTRIBUTE_UNUSED; { #if HAVE_GLOB char *zret; if (iSglob >= sSglob.gl_pathc) return NULL; zret = zbufcpy (sSglob.gl_pathv[iSglob]); ++iSglob; return zret; #else /* ! HAVE_GLOB */ char *zret; if (zSwildcard_alloc == NULL || zSwildcard == NULL) return NULL; zret = zSwildcard; while (*zSwildcard != '\0' && ! isspace (BUCHAR (*zSwildcard))) ++zSwildcard; if (*zSwildcard != '\0') { *zSwildcard = '\0'; ++zSwildcard; while (*zSwildcard != '\0' && isspace (BUCHAR (*zSwildcard))) ++zSwildcard; } if (*zSwildcard == '\0') zSwildcard = NULL; return zbufcpy (zret); #endif /* ! HAVE_GLOB */ } /* Finish up getting wildcard specs. */ boolean fsysdep_wildcard_end () { #if HAVE_GLOB globfree (&sSglob); return TRUE; #else /* ! HAVE_GLOB */ xfree ((pointer) zSwildcard_alloc); zSwildcard_alloc = NULL; zSwildcard = NULL; return TRUE; #endif /* ! HAVE_GLOB */ } uucp-1.07/unix/work.c0000664000076400007640000004754507665321761010255 /* work.c Routines to read command files. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char work_rcsid[] = "$Id: work.c,v 1.24 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "sysdep.h" #include #include #if HAVE_OPENDIR #if HAVE_DIRENT_H #include #else /* ! HAVE_DIRENT_H */ #include #define dirent direct #endif /* ! HAVE_DIRENT_H */ #endif /* HAVE_OPENDIR */ /* Local functions. */ static char *zswork_directory P((const char *zsystem)); static boolean fswork_file P((const char *zsystem, const char *zfile, char *pbgrade)); static int iswork_cmp P((constpointer pkey, constpointer pdatum)); /* These functions can support multiple actions going on at once. This allows the UUCP package to send and receive multiple files at the same time. */ /* The ssfilename structure holds the name of a work file, as well as its grade. */ struct ssfilename { char *zfile; char bgrade; /* Some compiler may need this, and it won't normally hurt. */ char bdummy; }; /* The ssfile structure holds a command file name and all the lines read in from that command file. The union within the ssline structure initially holds a line from the file and then holds a pointer back to the ssfile structure; a pointer to this union is used as a sequence pointer. The ztemp entry of the ssline structure holds the name of a temporary file to delete, if any. */ #define CFILELINES (10) struct ssline { char *zline; struct ssfile *qfile; char *ztemp; }; struct ssfile { char *zfile; char bgrade; /* bdummy is needed for some buggy compilers. */ char bdummy; int clines; int cdid; struct ssline aslines[CFILELINES]; }; /* Static variables for the work scan. */ static struct ssfilename *asSwork_files; static size_t cSwork_files; static size_t iSwork_file; static struct ssfile *qSwork_file; /* Given a system name, return a directory to search for work. */ static char * zswork_directory (zsystem) const char *zsystem; { #if SPOOLDIR_V2 return zbufcpy ("."); #endif /* SPOOLDIR_V2 */ #if SPOOLDIR_BSD42 || SPOOLDIR_BSD43 return zbufcpy ("C."); #endif /* SPOOLDIR_BSD42 || SPOOLDIR_BSD43 */ #if SPOOLDIR_HDB || SPOOLDIR_SVR4 return zbufcpy (zsystem); #endif /* SPOOLDIR_HDB || SPOOLDIR_SVR4 */ #if SPOOLDIR_ULTRIX return zsappend3 ("sys", (fsultrix_has_spool (zsystem) ? zsystem : "DEFAULT"), "C."); #endif /* SPOOLDIR_ULTRIX */ #if SPOOLDIR_TAYLOR return zsysdep_in_dir (zsystem, "C."); #endif /* SPOOLDIR_TAYLOR */ } /* See whether a file name from the directory returned by zswork_directory is really a command for a particular system. Return the command grade. */ /*ARGSUSED*/ static boolean fswork_file (zsystem, zfile, pbgrade) const char *zsystem ATTRIBUTE_UNUSED; const char *zfile; char *pbgrade; { #if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX int cfilesys, csys; /* The file name should be C.ssssssgqqqq, where g is exactly one letter and qqqq is exactly four numbers. The system name may be truncated to six or seven characters. The system name of the file must match the system name we're looking for, since there could be work files for several systems in one directory. */ if (zfile[0] != 'C' || zfile[1] != '.') return FALSE; csys = strlen (zsystem); cfilesys = strlen (zfile) - 7; if (csys != cfilesys && (csys < 6 || (cfilesys != 6 && cfilesys != 7))) return FALSE; *pbgrade = zfile[cfilesys + 2]; return strncmp (zfile + 2, zsystem, cfilesys) == 0; #endif /* V2 || BSD42 || BSD43 || ULTRIX */ #if SPOOLDIR_HDB || SPOOLDIR_SVR4 int clen; /* The HDB file name should be C.ssssssgqqqq where g is exactly one letter and qqqq is exactly four numbers or letters. We don't check the system name, because it is guaranteed by the directory we are looking in and some versions of uucp set it to the local system rather than the remote one. I'm not sure of the exact format of the SVR4 file name, but it does not include the grade at all. */ if (zfile[0] != 'C' || zfile[1] != '.') return FALSE; clen = strlen (zfile); if (clen < 7) return FALSE; #if ! SPOOLDIR_SVR4 *pbgrade = zfile[clen - 5]; #endif return TRUE; #endif /* SPOOLDIR_HDB || SPOOLDIR_SVR4 */ #if SPOOLDIR_TAYLOR /* We don't keep the system name in the file name, since that forces truncation. Our file names are always C.gqqqq. */ *pbgrade = zfile[2]; return (zfile[0] == 'C' && zfile[1] == '.' && zfile[2] != '\0'); #endif /* SPOOLDIR_TAYLOR */ } /* A comparison function to look through the list of file names. */ static int iswork_cmp (pkey, pdatum) constpointer pkey; constpointer pdatum; { const struct ssfilename *qkey = (const struct ssfilename *) pkey; const struct ssfilename *qdatum = (const struct ssfilename *) pdatum; return strcmp (qkey->zfile, qdatum->zfile); } /* See whether there is any work to do for a particular system. */ boolean fsysdep_has_work (qsys) const struct uuconf_system *qsys; { char *zdir; DIR *qdir; struct dirent *qentry; #if SPOOLDIR_SVR4 DIR *qgdir; struct dirent *qgentry; #endif zdir = zswork_directory (qsys->uuconf_zname); if (zdir == NULL) return FALSE; qdir = opendir ((char *) zdir); if (qdir == NULL) { ubuffree (zdir); return FALSE; } #if SPOOLDIR_SVR4 qgdir = qdir; while ((qgentry = readdir (qgdir)) != NULL) { char *zsub; if (qgentry->d_name[0] == '.' || qgentry->d_name[1] != '\0') continue; zsub = zsysdep_in_dir (zdir, qgentry->d_name); qdir = opendir (zsub); ubuffree (zsub); if (qdir == NULL) continue; #endif while ((qentry = readdir (qdir)) != NULL) { char bgrade; if (fswork_file (qsys->uuconf_zname, qentry->d_name, &bgrade)) { closedir (qdir); #if SPOOLDIR_SVR4 closedir (qgdir); #endif ubuffree (zdir); return TRUE; } } #if SPOOLDIR_SVR4 closedir (qdir); } qdir = qgdir; #endif closedir (qdir); ubuffree (zdir); return FALSE; } /* Initialize the work scan. We have to read all the files in the work directory, so that we can sort them by work grade. The bgrade argument is the minimum grade to consider. We don't want to return files that we have already considered; usysdep_get_work_free will clear the data out when we are done with the system. This returns FALSE on error. */ #define CWORKFILES (10) boolean fsysdep_get_work_init (qsys, bgrade, cmax) const struct uuconf_system *qsys; int bgrade; unsigned int cmax; { char *zdir; DIR *qdir; struct dirent *qentry; size_t chad; size_t callocated; #if SPOOLDIR_SVR4 DIR *qgdir; struct dirent *qgentry; #endif zdir = zswork_directory (qsys->uuconf_zname); if (zdir == NULL) return FALSE; qdir = opendir (zdir); if (qdir == NULL) { boolean fret; if (errno == ENOENT) fret = TRUE; else { ulog (LOG_ERROR, "opendir (%s): %s", zdir, strerror (errno)); fret = FALSE; } ubuffree (zdir); return fret; } chad = cSwork_files; callocated = cSwork_files; /* Sort the files we already know about so that we can check the new ones with bsearch. It would be faster to use a hash table, and the code should be probably be changed. The sort done at the end of this function does not suffice because it only includes the files added last time, and does not sort the entire array. Some (bad) qsort implementations are very slow when given a sorted array, which causes particularly bad effects here. */ if (chad > 0) qsort ((pointer) asSwork_files, chad, sizeof (struct ssfilename), iswork_cmp); #if SPOOLDIR_SVR4 qgdir = qdir; while ((qgentry = readdir (qgdir)) != NULL) { char *zsub; if (qgentry->d_name[0] == '.' || qgentry->d_name[1] != '\0' || UUCONF_GRADE_CMP (bgrade, qgentry->d_name[0]) < 0) continue; zsub = zsysdep_in_dir (zdir, qgentry->d_name); qdir = opendir (zsub); if (qdir == NULL) { if (errno != ENOTDIR && errno != ENOENT) { ulog (LOG_ERROR, "opendir (%s): %s", zsub, strerror (errno)); ubuffree (zsub); return FALSE; } ubuffree (zsub); continue; } ubuffree (zsub); #endif while ((qentry = readdir (qdir)) != NULL) { char bfilegrade; char *zname; struct ssfilename slook; #if ! SPOOLDIR_SVR4 zname = zbufcpy (qentry->d_name); #else zname = zsysdep_in_dir (qgentry->d_name, qentry->d_name); bfilegrade = qgentry->d_name[0]; #endif slook.zfile = zname; if (! fswork_file (qsys->uuconf_zname, qentry->d_name, &bfilegrade) || UUCONF_GRADE_CMP (bgrade, bfilegrade) < 0 || (asSwork_files != NULL && bsearch ((pointer) &slook, (pointer) asSwork_files, chad, sizeof (struct ssfilename), iswork_cmp) != NULL)) ubuffree (zname); else { DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, "fsysdep_get_work_init: Found %s", zname); if (cSwork_files >= callocated) { callocated += CWORKFILES; asSwork_files = ((struct ssfilename *) xrealloc ((pointer) asSwork_files, (callocated * sizeof (struct ssfilename)))); } asSwork_files[cSwork_files].zfile = zname; asSwork_files[cSwork_files].bgrade = bfilegrade; ++cSwork_files; if (cmax != 0 && cSwork_files - chad > cmax) break; } } #if SPOOLDIR_SVR4 closedir (qdir); if (cmax != 0 && cSwork_files - chad > cmax) break; } qdir = qgdir; #endif closedir (qdir); ubuffree (zdir); /* Sorting the files alphabetically will get the grades in the right order, since all the file prefixes are the same. */ if (cSwork_files > iSwork_file) qsort ((pointer) (asSwork_files + iSwork_file), cSwork_files - iSwork_file, sizeof (struct ssfilename), iswork_cmp); return TRUE; } /* Get the next work entry for a system. This must parse the next line in the next work file. The type of command is set into qcmd->bcmd If there are no more commands, qcmd->bcmd is set to 'H'. Each field in the structure is set to point to a spot in an malloced string. The grade argument is never used; it has been used by fsysdep_get_work_init. */ /*ARGSUSED*/ boolean fsysdep_get_work (qsys, bgrade, cmax, qcmd) const struct uuconf_system *qsys; int bgrade ATTRIBUTE_UNUSED; unsigned int cmax ATTRIBUTE_UNUSED; struct scmd *qcmd; { char *zdir; if (qSwork_file != NULL && qSwork_file->cdid >= qSwork_file->clines) qSwork_file = NULL; if (asSwork_files == NULL) { qcmd->bcmd = 'H'; return TRUE; } zdir = NULL; /* This loop continues until a line is returned. */ while (TRUE) { /* This loop continues until a file is opened and read in. */ while (qSwork_file == NULL) { FILE *e; struct ssfile *qfile; int iline, callocated; char *zline; size_t cline; char *zname; char bfilegrade; /* Read all the lines of a command file into memory. */ do { if (iSwork_file >= cSwork_files) { qcmd->bcmd = 'H'; ubuffree (zdir); return TRUE; } if (zdir == NULL) { zdir = zswork_directory (qsys->uuconf_zname); if (zdir == NULL) return FALSE; } zname = zsysdep_in_dir (zdir, asSwork_files[iSwork_file].zfile); bfilegrade = asSwork_files[iSwork_file].bgrade; ++iSwork_file; e = fopen (zname, "r"); if (e == NULL) { ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno)); ubuffree (zname); } } while (e == NULL); qfile = (struct ssfile *) xmalloc (sizeof (struct ssfile)); callocated = CFILELINES; iline = 0; zline = NULL; cline = 0; while (getline (&zline, &cline, e) > 0) { if (iline >= callocated) { /* The sizeof (struct ssfile) includes CFILELINES entries already, so using callocated * sizeof (struct ssline) will give us callocated * CFILELINES entries. */ qfile = ((struct ssfile *) xrealloc ((pointer) qfile, (sizeof (struct ssfile) + (callocated * sizeof (struct ssline))))); callocated += CFILELINES; } qfile->aslines[iline].zline = zbufcpy (zline); qfile->aslines[iline].qfile = NULL; qfile->aslines[iline].ztemp = NULL; iline++; } xfree ((pointer) zline); if (fclose (e) != 0) ulog (LOG_ERROR, "fclose: %s", strerror (errno)); if (iline == 0) { /* There were no lines in the file; this is a poll file, for which we return a 'P' command. */ qfile->aslines[0].zline = zbufcpy ("P"); qfile->aslines[0].qfile = NULL; qfile->aslines[0].ztemp = NULL; iline = 1; } qfile->zfile = zname; qfile->bgrade = bfilegrade; qfile->clines = iline; qfile->cdid = 0; qSwork_file = qfile; } /* This loop continues until all the lines from the current file are used up, or a line is returned. */ while (TRUE) { int iline; if (qSwork_file->cdid >= qSwork_file->clines) { /* We don't want to free qSwork_file here, since it must remain until all the lines have been completed. It is freed in fsysdep_did_work. */ qSwork_file = NULL; /* Go back to the main loop which finds another file. */ break; } iline = qSwork_file->cdid; ++qSwork_file->cdid; /* Now parse the line into a command. */ if (! fparse_cmd (qSwork_file->aslines[iline].zline, qcmd)) { ulog (LOG_ERROR, "Bad line in command file %s", qSwork_file->zfile); ubuffree (qSwork_file->aslines[iline].zline); qSwork_file->aslines[iline].zline = NULL; continue; } qcmd->bgrade = qSwork_file->bgrade; qSwork_file->aslines[iline].qfile = qSwork_file; qcmd->pseq = (pointer) (&qSwork_file->aslines[iline]); if (qcmd->bcmd == 'S' || qcmd->bcmd == 'E') { char *zreal; zreal = zsysdep_spool_file_name (qsys, qcmd->ztemp, qcmd->pseq); if (zreal == NULL) { ubuffree (qSwork_file->aslines[iline].zline); qSwork_file->aslines[iline].zline = NULL; ubuffree (zdir); return FALSE; } qSwork_file->aslines[iline].ztemp = zreal; } ubuffree (zdir); return TRUE; } } } /* When a command has been complete, fsysdep_did_work is called. The sequence entry was set above to be the address of an aslines structure whose pfile entry points to the ssfile corresponding to this file. We can then check whether all the lines have been completed (they will have been if the pfile entry is NULL) and remove the file if they have been. This means that we only remove a command file if we manage to complete every transfer it specifies in a single UUCP session. I don't know if this is how regular UUCP works. */ boolean fsysdep_did_work (pseq) pointer pseq; { struct ssfile *qfile; struct ssline *qline; int i; qline = (struct ssline *) pseq; ubuffree (qline->zline); qline->zline = NULL; qfile = qline->qfile; qline->qfile = NULL; /* Remove the temporary file, if there is one. It really doesn't matter if this fails, and not checking the return value lets us attempt to remove D.0 or whatever an unused temporary file is called without complaining. */ if (qline->ztemp != NULL) { (void) remove (qline->ztemp); ubuffree (qline->ztemp); qline->ztemp = NULL; } /* If not all the lines have been returned from fsysdep_get_work, we can't remove the file yet. */ if (qfile->cdid < qfile->clines) return TRUE; /* See whether all the commands have been completed. */ for (i = 0; i < qfile->clines; i++) if (qfile->aslines[i].qfile != NULL) return TRUE; /* All commands have finished. */ if (remove (qfile->zfile) != 0) { ulog (LOG_ERROR, "remove (%s): %s", qfile->zfile, strerror (errno)); return FALSE; } ubuffree (qfile->zfile); xfree ((pointer) qfile); if (qfile == qSwork_file) qSwork_file = NULL; return TRUE; } /* Free up the results of a work scan, when we're done with this system. */ /*ARGSUSED*/ void usysdep_get_work_free (qsys) const struct uuconf_system *qsys ATTRIBUTE_UNUSED; { if (asSwork_files != NULL) { size_t i; for (i = 0; i < cSwork_files; i++) ubuffree ((pointer) asSwork_files[i].zfile); xfree ((pointer) asSwork_files); asSwork_files = NULL; cSwork_files = 0; iSwork_file = 0; } if (qSwork_file != NULL) { int i; ubuffree (qSwork_file->zfile); for (i = 0; i < qSwork_file->cdid; i++) { ubuffree (qSwork_file->aslines[i].zline); ubuffree (qSwork_file->aslines[i].ztemp); } for (i = qSwork_file->cdid; i < qSwork_file->clines; i++) ubuffree (qSwork_file->aslines[i].zline); xfree ((pointer) qSwork_file); qSwork_file = NULL; } } /* Save the temporary file used by a send command, and return an informative message to mail to the requestor. This is called when a file transfer failed, to make sure that the potentially valuable file is not completely lost. */ const char * zsysdep_save_temp_file (pseq) pointer pseq; { struct ssline *qline = (struct ssline *) pseq; char *zto, *zslash; size_t cwant; static char *zbuf; static size_t cbuf; if (! fsysdep_file_exists (qline->ztemp)) return NULL; zslash = strrchr (qline->ztemp, '/'); if (zslash == NULL) zslash = qline->ztemp; else ++zslash; zto = zbufalc (sizeof PRESERVEDIR + sizeof "/" + strlen (zslash)); sprintf (zto, "%s/%s", PRESERVEDIR, zslash); if (! fsysdep_move_file (qline->ztemp, zto, TRUE, FALSE, FALSE, (const char *) NULL)) { /* Leave the file where it was, not that is much help. */ ubuffree (zto); return "Could not move file to preservation directory"; } cwant = sizeof "File saved as\n\t/" + strlen (zSspooldir) + strlen (zto); if (cwant > cbuf) { ubuffree (zbuf); zbuf = zbufalc (cwant); cbuf = cwant; } sprintf (zbuf, "File saved as\n\t%s/%s", zSspooldir, zto); ubuffree (zto); return zbuf; } /* Get the jobid of a work file. This is needed by uustat. */ char * zsysdep_jobid (qsys, pseq) const struct uuconf_system *qsys; pointer pseq; { return zsfile_to_jobid (qsys, ((struct ssline *) pseq)->qfile->zfile, bsgrade (pseq)); } /* Get the grade of a work file. The pseq argument can be NULL when this is called from zsysdep_spool_file_name, and simply means that this is a remote file; returning -1 will cause zsfind_file to do the right thing. */ int bsgrade (pseq) pointer pseq; { const char *zfile; char bgrade; if (pseq == NULL) return -1; zfile = ((struct ssline *) pseq)->qfile->zfile; #if SPOOLDIR_TAYLOR bgrade = *(strrchr (zfile, '/') + 3); #else #if ! SPOOLDIR_SVR4 bgrade = zfile[strlen (zfile) - CSEQLEN - 1]; #else bgrade = *(strchr (zfile, '/') + 1); #endif #endif return bgrade; } uucp-1.07/unix/xqtfil.c0000664000076400007640000001573707665321761010600 /* xqtfil.c Routines to read execute files. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char xqtfil_rcsid[] = "$Id: xqtfil.c,v 1.13 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "sysdep.h" #include "system.h" #include #if HAVE_OPENDIR #if HAVE_DIRENT_H #include #else /* ! HAVE_DIRENT_H */ #include #define dirent direct #endif /* ! HAVE_DIRENT_H */ #endif /* HAVE_OPENDIR */ /* Under the V2 or BSD42 spool directory scheme, all execute files are in the main spool directory. Under the BSD43 scheme, they are all in the directory X.. Under the HDB or SVR4 scheme, they are in directories named after systems. Under the ULTRIX scheme, they are in X. subdirectories of subdirectories of sys. Under the TAYLOR scheme, they are all in the subdirectory X. of a directory named after the system. This means that for HDB, ULTRIX, SVR4 or TAYLOR, we have to search directories of directories. */ #if SPOOLDIR_V2 || SPOOLDIR_BSD42 #define ZDIR "." #define SUBDIRS 0 #endif #if SPOOLDIR_HDB || SPOOLDIR_SVR4 || SPOOLDIR_TAYLOR #define ZDIR "." #define SUBDIRS 1 #endif #if SPOOLDIR_ULTRIX #define ZDIR "sys" #define SUBDIRS 1 #endif #if SPOOLDIR_BSD43 #define ZDIR "X." #define SUBDIRS 0 #endif /* Static variables for the execute file scan. */ static DIR *qSxqt_topdir; #if ! SUBDIRS static const char *zSdir; #else /* SUBDIRS */ static boolean fSone_dir; static char *zSdir; static DIR *qSxqt_dir; static char *zSsystem; #endif /* SUBDIRS */ /* Initialize the scan for execute files. The function usysdep_get_xqt_free will clear the data out when we are done with the system. This returns FALSE on error. */ /*ARGSUSED*/ boolean fsysdep_get_xqt_init (zsystem) const char *zsystem; { usysdep_get_xqt_free ((const char *) NULL); #if SUBDIRS if (zsystem != NULL) { #if SPOOLDIR_HDB || SPOOLDIR_SVR4 zSdir = zbufcpy (zsystem); #endif #if SPOOLDIR_ULTRIX zSdir = zsappend3 ("sys", zsystem, "X."); #endif #if SPOOLDIR_TAYLOR zSdir = zsysdep_in_dir (zsystem, "X."); #endif qSxqt_dir = opendir ((char *) zSdir); if (qSxqt_dir != NULL) { qSxqt_topdir = qSxqt_dir; fSone_dir = TRUE; zSsystem = zbufcpy (zsystem); return TRUE; } } fSone_dir = FALSE; #endif qSxqt_topdir = opendir ((char *) ZDIR); if (qSxqt_topdir == NULL) { if (errno == ENOENT) return TRUE; ulog (LOG_ERROR, "opendir (%s): %s", ZDIR, strerror (errno)); return FALSE; } return TRUE; } /* Return the name of the next execute file to read and process. If this returns NULL, *pferr must be checked. If will be TRUE on error, FALSE if there are no more files. On a successful return *pzsystem will be set to the system for which the execute file was created. */ /*ARGSUSED*/ char * zsysdep_get_xqt (zsystem, pzsystem, pferr) const char *zsystem ATTRIBUTE_UNUSED; char **pzsystem; boolean *pferr; { *pferr = FALSE; if (qSxqt_topdir == NULL) return NULL; /* This loop continues until we find a file. */ while (TRUE) { DIR *qdir; struct dirent *q; #if ! SUBDIRS zSdir = ZDIR; qdir = qSxqt_topdir; #else /* SUBDIRS */ /* This loop continues until we find a subdirectory to read. */ while (qSxqt_dir == NULL) { struct dirent *qtop; qtop = readdir (qSxqt_topdir); if (qtop == NULL) { (void) closedir (qSxqt_topdir); qSxqt_topdir = NULL; return NULL; } /* No system name may start with a dot This allows us to quickly skip impossible directories. */ if (qtop->d_name[0] == '.') continue; DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, "zsysdep_get_xqt: Found %s in top directory", qtop->d_name); ubuffree (zSdir); #if SPOOLDIR_HDB || SPOOLDIR_SVR4 zSdir = zbufcpy (qtop->d_name); #endif #if SPOOLDIR_ULTRIX zSdir = zsappend3 ("sys", qtop->d_name, "X."); #endif #if SPOOLDIR_TAYLOR zSdir = zsysdep_in_dir (qtop->d_name, "X."); #endif ubuffree (zSsystem); zSsystem = zbufcpy (qtop->d_name); qSxqt_dir = opendir (zSdir); if (qSxqt_dir == NULL && errno != ENOTDIR && errno != ENOENT) ulog (LOG_ERROR, "opendir (%s): %s", zSdir, strerror (errno)); } qdir = qSxqt_dir; #endif /* SUBDIRS */ q = readdir (qdir); #if DEBUG > 1 if (q != NULL) DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, "zsysdep_get_xqt: Found %s in subdirectory %s", q->d_name, zSdir); #endif /* If we've found an execute file, return it. We have to get the system name, which is easy for HDB or TAYLOR. For other spool directory schemes, we have to pull it out of the X. file name; this would be insecure, except that zsfind_file clobbers the file name to include the real system name. */ if (q != NULL && q->d_name[0] == 'X' && q->d_name[1] == '.') { char *zret; #if SPOOLDIR_HDB || SPOOLDIR_SVR4 || SPOOLDIR_TAYLOR *pzsystem = zbufcpy (zSsystem); #else { size_t clen; clen = strlen (q->d_name) - 7; *pzsystem = zbufalc (clen + 1); memcpy (*pzsystem, q->d_name + 2, clen); (*pzsystem)[clen] = '\0'; } #endif zret = zsysdep_in_dir (zSdir, q->d_name); #if DEBUG > 1 DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, "zsysdep_get_xqt: Returning %s (system %s)", zret, *pzsystem); #endif return zret; } /* If we've reached the end of the directory, then if we are using subdirectories loop around to read the next one, otherwise we are finished. */ if (q == NULL) { (void) closedir (qdir); #if SUBDIRS qSxqt_dir = NULL; if (! fSone_dir) continue; #endif qSxqt_topdir = NULL; return NULL; } } } /* Free up the results of an execute file scan, when we're done with this system. */ /*ARGSUSED*/ void usysdep_get_xqt_free (zsystem) const char *zsystem ATTRIBUTE_UNUSED; { if (qSxqt_topdir != NULL) { (void) closedir (qSxqt_topdir); qSxqt_topdir = NULL; } #if SUBDIRS if (qSxqt_dir != NULL) { (void) closedir (qSxqt_dir); qSxqt_dir = NULL; } ubuffree (zSdir); zSdir = NULL; ubuffree (zSsystem); zSsystem = NULL; fSone_dir = FALSE; #endif } uucp-1.07/unix/xqtsub.c0000664000076400007640000003522607665321761010612 /* xqtsub.c System dependent functions used only by uuxqt. Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #if USE_RCS_ID const char xqtsub_rcsid[] = "$Id: xqtsub.c,v 1.24 2002/03/05 19:10:42 ian Rel $"; #endif #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "sysdep.h" #include #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif #if HAVE_OPENDIR #if HAVE_DIRENT_H #include #else /* ! HAVE_DIRENT_H */ #include #define dirent direct #endif /* ! HAVE_DIRENT_H */ #endif /* HAVE_OPENDIR */ /* Get a value for EX_TEMPFAIL. */ #if HAVE_SYSEXITS_H #include #endif #ifndef EX_TEMPFAIL #define EX_TEMPFAIL 75 #endif static boolean fclean_uuxqt_dir P((const char *zxqtdir)); /* Get the full pathname of the command to execute, given the list of permitted commands and the allowed path. */ char * zsysdep_find_command (zcmd, pzcmds, pzpath, pferr) const char *zcmd; char **pzcmds; char **pzpath; boolean *pferr; { char **pz; struct stat s; *pferr = FALSE; for (pz = pzcmds; *pz != NULL; pz++) { char *zslash; if (strcmp (*pz, "ALL") == 0) break; zslash = strrchr (*pz, '/'); if (zslash != NULL) ++zslash; else zslash = *pz; if (strcmp (zslash, zcmd) == 0 || strcmp (*pz, zcmd) == 0) { /* If we already have an absolute path, we can get out immediately. */ if (**pz == '/') { /* Quick error check. */ if (stat (*pz, &s) != 0) { ulog (LOG_ERROR, "%s: %s", *pz, strerror (errno)); *pferr = TRUE; return NULL; } return zbufcpy (*pz); } break; } } /* If we didn't find this command, get out. */ if (*pz == NULL) return NULL; /* We didn't find an absolute pathname, so we must look through the path. */ for (pz = pzpath; *pz != NULL; pz++) { char *zname; zname = zsysdep_in_dir (*pz, zcmd); if (stat (zname, &s) == 0) return zname; } return NULL; } /* Expand a local filename for uuxqt. This is special because uuxqt only wants to expand filenames that start with ~ (it does not want to prepend the current directory to other names) and if the ~ is double, it is turned into a single ~. This returns NULL to indicate that no change was required; it has no way to return error. */ char * zsysdep_xqt_local_file (qsys, zfile) const struct uuconf_system *qsys; const char *zfile; { if (*zfile != '~') return NULL; if (zfile[1] == '~') { size_t clen; char *zret; clen = strlen (zfile); zret = zbufalc (clen); memcpy (zret, zfile + 1, clen); return zret; } return zsysdep_local_file (zfile, qsys->uuconf_zpubdir, (boolean *) NULL); } #if ! ALLOW_FILENAME_ARGUMENTS /* Check to see whether an argument specifies a file name; if it does, make sure that the file may legally be sent and/or received. For Unix, we do not permit any occurrence of "/../" in the name, nor may it start with "../". Otherwise, if it starts with "/" we check against the list of permitted files. */ boolean fsysdep_xqt_check_file (qsys, zfile) const struct uuconf_system *qsys; const char *zfile; { size_t clen; /* Disallow exact "..", prefix "../", suffix "/..", internal "/../", and restricted absolute paths. */ clen = strlen (zfile); if ((clen == sizeof ".." - 1 && strcmp (zfile, "..") == 0) || strncmp (zfile, "../", sizeof "../" - 1) == 0 || (clen >= sizeof "/.." - 1 && strcmp (zfile + clen - (sizeof "/.." - 1), "/..") == 0) || strstr (zfile, "/../") != NULL || (*zfile == '/' && (! fin_directory_list (zfile, qsys->uuconf_pzremote_send, qsys->uuconf_zpubdir, TRUE, FALSE, (const char *) NULL) || ! fin_directory_list (zfile, qsys->uuconf_pzremote_receive, qsys->uuconf_zpubdir, TRUE, FALSE, (const char *) NULL)))) { ulog (LOG_ERROR, "Not permitted to refer to file \"%s\"", zfile); return FALSE; } return TRUE; } #endif /* ! ALLOW_FILENAME_ARGUMENTS */ /* Invoke the command specified by an execute file. */ /*ARGSUSED*/ boolean fsysdep_execute (qsys, zuser, pazargs, zfullcmd, zinput, zoutput, fshell, iseq, pzerror, pftemp) const struct uuconf_system *qsys; const char *zuser; const char **pazargs; const char *zfullcmd ATTRIBUTE_UNUSED; const char *zinput; const char *zoutput; boolean fshell; int iseq; char **pzerror; boolean *pftemp; { int aidescs[3]; boolean ferr; pid_t ipid; int ierr; char abxqtdir[sizeof XQTDIR + 4]; const char *zxqtdir; int istat; char *zpath; #if ALLOW_SH_EXECUTION const char *azshargs[4]; #endif *pzerror = NULL; *pftemp = FALSE; aidescs[0] = SPAWN_NULL; aidescs[1] = SPAWN_NULL; aidescs[2] = SPAWN_NULL; ferr = FALSE; if (zinput != NULL) { aidescs[0] = open ((char *) zinput, O_RDONLY | O_NOCTTY, 0); if (aidescs[0] < 0) { ulog (LOG_ERROR, "open (%s): %s", zinput, strerror (errno)); ferr = TRUE; } else if (fcntl (aidescs[0], F_SETFD, fcntl (aidescs[0], F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); ferr = TRUE; } } if (! ferr && zoutput != NULL) { aidescs[1] = creat ((char *) zoutput, IPRIVATE_FILE_MODE); if (aidescs[1] < 0) { if (errno == ENOENT && zoutput[0] != '/') { if (! fsysdep_make_dirs (zoutput, FALSE)) { *pftemp = TRUE; ferr = TRUE; } else aidescs[1] = creat ((char *) zoutput, IPRIVATE_FILE_MODE); } if (! ferr && aidescs[1] < 0) { ulog (LOG_ERROR, "creat (%s): %s", zoutput, strerror (errno)); *pftemp = TRUE; ferr = TRUE; } } if (! ferr && fcntl (aidescs[1], F_SETFD, fcntl (aidescs[1], F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); ferr = TRUE; } } if (! ferr) { *pzerror = zstemp_file (qsys); aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE); if (aidescs[2] < 0) { if (errno == ENOENT) { if (! fsysdep_make_dirs (*pzerror, FALSE)) { *pftemp = TRUE; ferr = TRUE; } else aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE); } if (! ferr && aidescs[2] < 0) { ulog (LOG_ERROR, "creat (%s): %s", *pzerror, strerror (errno)); *pftemp = TRUE; ferr = TRUE; } } if (! ferr && fcntl (aidescs[2], F_SETFD, fcntl (aidescs[2], F_GETFD, 0) | FD_CLOEXEC) < 0) { ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); ferr = TRUE; } } if (iseq == 0) zxqtdir = XQTDIR; else { sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); zxqtdir = abxqtdir; } if (ferr) { if (aidescs[0] != SPAWN_NULL) (void) close (aidescs[0]); if (aidescs[1] != SPAWN_NULL) (void) close (aidescs[1]); if (aidescs[2] != SPAWN_NULL) (void) close (aidescs[2]); ubuffree (*pzerror); return FALSE; } #if ALLOW_SH_EXECUTION if (fshell) { azshargs[0] = "/bin/sh"; azshargs[1] = "-c"; azshargs[2] = zfullcmd; azshargs[3] = NULL; pazargs = azshargs; } #else fshell = FALSE; #endif if (qsys->uuconf_pzpath == NULL) zpath = NULL; else { size_t c; char **pz; c = 0; for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++) c += strlen (*pz) + 1; zpath = zbufalc (c); *zpath = '\0'; for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++) { strcat (zpath, *pz); if (pz[1] != NULL) strcat (zpath, ":"); } } /* Pass zchdir as zxqtdir, fnosigs as TRUE, fshell as TRUE if we aren't already using the shell. */ ipid = ixsspawn (pazargs, aidescs, TRUE, FALSE, zxqtdir, TRUE, ! fshell, zpath, qsys->uuconf_zname, zuser); ierr = errno; ubuffree (zpath); if (aidescs[0] != SPAWN_NULL) (void) close (aidescs[0]); if (aidescs[1] != SPAWN_NULL) (void) close (aidescs[1]); if (aidescs[2] != SPAWN_NULL) (void) close (aidescs[2]); if (ipid < 0) { ulog (LOG_ERROR, "ixsspawn: %s", strerror (ierr)); *pftemp = TRUE; return FALSE; } istat = ixswait ((unsigned long) ipid, "Execution"); if (istat == EX_TEMPFAIL) *pftemp = TRUE; return istat == 0; } /* Lock a uuxqt process. */ int ixsysdep_lock_uuxqt (zcmd, cmaxuuxqts) const char *zcmd; int cmaxuuxqts; { char ab[sizeof "LCK.XQT.9999"]; int i; if (cmaxuuxqts <= 0 || cmaxuuxqts >= 10000) cmaxuuxqts = 9999; for (i = 0; i < cmaxuuxqts; i++) { sprintf (ab, "LCK.XQT.%d", i); if (fsdo_lock (ab, TRUE, (boolean *) NULL)) break; } if (i >= cmaxuuxqts) return -1; if (zcmd != NULL) { char abcmd[sizeof "LXQ.123456789"]; sprintf (abcmd, "LXQ.%.9s", zcmd); abcmd[strcspn (abcmd, " \t/")] = '\0'; if (! fsdo_lock (abcmd, TRUE, (boolean *) NULL)) { (void) fsdo_unlock (ab, TRUE); return -1; } } return i; } /* Unlock a uuxqt process. */ boolean fsysdep_unlock_uuxqt (iseq, zcmd, cmaxuuxqts) int iseq; const char *zcmd; int cmaxuuxqts ATTRIBUTE_UNUSED; { char ab[sizeof "LCK.XQT.9999"]; boolean fret; fret = TRUE; sprintf (ab, "LCK.XQT.%d", iseq); if (! fsdo_unlock (ab, TRUE)) fret = FALSE; if (zcmd != NULL) { char abcmd[sizeof "LXQ.123456789"]; sprintf (abcmd, "LXQ.%.9s", zcmd); abcmd[strcspn (abcmd, " \t/")] = '\0'; if (! fsdo_unlock (abcmd, TRUE)) fret = FALSE; } return fret; } /* See whether a particular uuxqt command is locked (this depends on the implementation of fsdo_lock). */ boolean fsysdep_uuxqt_locked (zcmd) const char *zcmd; { char ab[sizeof "LXQ.123456789"]; struct stat s; sprintf (ab, "LXQ.%.9s", zcmd); return stat (ab, &s) == 0; } /* Lock a particular execute file. */ boolean fsysdep_lock_uuxqt_file (zfile) const char *zfile; { char *zcopy, *z; boolean fret; zcopy = zbufcpy (zfile); z = strrchr (zcopy, '/'); if (z == NULL) *zcopy = 'L'; else *(z + 1) = 'L'; fret = fsdo_lock (zcopy, TRUE, (boolean *) NULL); ubuffree (zcopy); return fret; } /* Unlock a particular execute file. */ boolean fsysdep_unlock_uuxqt_file (zfile) const char *zfile; { char *zcopy, *z; boolean fret; zcopy = zbufcpy (zfile); z = strrchr (zcopy, '/'); if (z == NULL) *zcopy = 'L'; else *(z + 1) = 'L'; fret = fsdo_unlock (zcopy, TRUE); ubuffree (zcopy); return fret; } /* Lock the execute directory. Since we use a different directory depending on which LCK.XQT.dddd file we got, there is actually no need to create a lock file. We do make sure that the directory exists, though, and that it is empty. */ boolean fsysdep_lock_uuxqt_dir (iseq) int iseq; { const char *zxqtdir; char abxqtdir[sizeof XQTDIR + 4]; if (iseq == 0) zxqtdir = XQTDIR; else { sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); zxqtdir = abxqtdir; } if (mkdir (zxqtdir, S_IRWXU) < 0 && errno != EEXIST && errno != EISDIR) { ulog (LOG_ERROR, "mkdir (%s): %s", zxqtdir, strerror (errno)); return FALSE; } return fclean_uuxqt_dir (zxqtdir); } /* Unlock the execute directory and clear it out. The lock is actually the LCK.XQT.dddd file, so we don't unlock it, but we do remove all the files. */ boolean fsysdep_unlock_uuxqt_dir (iseq) int iseq; { const char *zxqtdir; char abxqtdir[sizeof XQTDIR + 4]; if (iseq == 0) zxqtdir = XQTDIR; else { sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); zxqtdir = abxqtdir; } return fclean_uuxqt_dir (zxqtdir); } static boolean fclean_uuxqt_dir (zxqtdir) const char *zxqtdir; { DIR *qdir; qdir = opendir ((char *) zxqtdir); if (qdir != NULL) { struct dirent *qentry; while ((qentry = readdir (qdir)) != NULL) { char *z; if (strcmp (qentry->d_name, ".") == 0 || strcmp (qentry->d_name, "..") == 0) continue; z = zsysdep_in_dir (zxqtdir, qentry->d_name); if (remove (z) < 0) { int ierr; ierr = errno; if (! fsysdep_directory (z)) ulog (LOG_ERROR, "remove (%s): %s", z, strerror (ierr)); else (void) fsysdep_rmdir (z); } ubuffree (z); } closedir (qdir); } return TRUE; } /* Move files into the execution directory. */ boolean fsysdep_copy_uuxqt_files (cfiles, pzfrom, pzto, iseq, pzinput) int cfiles; const char *const *pzfrom; const char *const *pzto; int iseq; char **pzinput; { char *zinput; const char *zxqtdir; char abxqtdir[sizeof XQTDIR + 4]; int i; if (pzinput == NULL) zinput = NULL; else zinput = *pzinput; if (iseq == 0) zxqtdir = XQTDIR; else { sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); zxqtdir = abxqtdir; } for (i = 0; i < cfiles; i++) { const char *zfrom, *zto; char *zfree; if (pzto[i] == NULL) continue; zfree = zsysdep_in_dir (zxqtdir, pzto[i]); zfrom = pzfrom[i]; zto = zfree; if (zinput != NULL && strcmp (zinput, zfrom) == 0) { *pzinput = zbufcpy (zto); zinput = NULL; } if (link (zfrom, zto) < 0) { if (errno != EXDEV && errno != EEXIST && errno != EMLINK) { ulog (LOG_ERROR, "link (%s, %s): %s", zfrom, zto, strerror (errno)); ubuffree (zfree); return FALSE; } if (! fcopy_file (zfrom, zto, FALSE, FALSE, FALSE)) { ubuffree (zfree); return FALSE; } } (void) chmod (zto, IPUBLIC_FILE_MODE); ubuffree (zfree); } return TRUE; } uucp-1.07/unix/fsusg.h0000664000076400007640000000245507665321761010416 /* fsusage.h -- declarations for filesystem space usage info Copyright (C) 1991, 1992 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. This files was modified slightly by Ian Lance Taylor for use with Taylor UUCP. */ /* Space usage statistics for a filesystem. Blocks are 512-byte. */ struct fs_usage { long fsu_blocks; /* Total blocks. */ long fsu_bfree; /* Free blocks available to superuser. */ long fsu_bavail; /* Free blocks available to non-superuser. */ long fsu_files; /* Total file nodes. */ long fsu_ffree; /* Free file nodes. */ }; extern int get_fs_usage P((char *path, char *disk, struct fs_usage *fsp)); uucp-1.07/unix/getcwd.c0000664000076400007640000000154107665321761010532 /* getcwd.c Replacement for the getcwd function that just calls /bin/pwd. */ #include "uucp.h" #include "sysdep.h" #include char * getcwd (zbuf, cbuf) char *zbuf; size_t cbuf; { const char *azargs[2]; FILE *e; pid_t ipid; int cread; int ierr; azargs[0] = PWD_PROGRAM; azargs[1] = NULL; e = espopen (azargs, TRUE, &ipid); if (e == NULL) return NULL; ierr = 0; cread = fread (zbuf, sizeof (char), cbuf, e); if (cread == 0) ierr = errno; (void) fclose (e); if (ixswait ((unsigned long) ipid, (const char *) NULL) != 0) { ierr = EACCES; cread = 0; } if (cread != 0) { if (zbuf[cread - 1] == '\n') zbuf[cread - 1] = '\0'; else { ierr = ERANGE; cread = 0; } } if (cread == 0) { errno = ierr; return NULL; } return zbuf; } uucp-1.07/unix/mkdir.c0000664000076400007640000000235607665321761010370 /* mkdir.c Create a directory. We must go through a subsidiary program to force our real uid to be the uucp owner before invoking the setuid /bin/mkdir program. */ #include "uucp.h" #include "sysdep.h" #include int mkdir (zdir, imode) const char *zdir; int imode; { struct stat s; const char *azargs[3]; int aidescs[3]; pid_t ipid; /* Make sure the directory does not exist, since we will otherwise get the wrong errno value. */ if (stat (zdir, &s) == 0) { errno = EEXIST; return -1; } /* /bin/mkdir will create the directory with mode 777, so we set our umask to get the mode we want. */ (void) umask ((~ imode) & (S_IRWXU | S_IRWXG | S_IRWXO)); azargs[0] = UUDIR_PROGRAM; azargs[1] = zdir; azargs[2] = NULL; aidescs[0] = SPAWN_NULL; aidescs[1] = SPAWN_NULL; aidescs[2] = SPAWN_NULL; ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, (const char *) NULL, TRUE, FALSE, (const char *) NULL, (const char *) NULL, (const char *) NULL); (void) umask (0); if (ipid < 0) return -1; if (ixswait ((unsigned long) ipid, (const char *) NULL) != 0) { /* Make up an errno value. */ errno = EACCES; return -1; } return 0; } uucp-1.07/unix/rmdir.c0000664000076400007640000000161407665321761010373 /* rmdir.c Remove a directory on a system which doesn't have the rmdir system call. This is only called by uupick, which is not setuid, so we don't have to worry about the problems of invoking the setuid /bin/rmdir program. */ #include "uucp.h" #include "sysdep.h" #include int rmdir (zdir) const char *zdir; { const char *azargs[3]; int aidescs[3]; pid_t ipid; azargs[0] = RMDIR_PROGRAM; azargs[1] = zdir; azargs[2] = NULL; aidescs[0] = SPAWN_NULL; aidescs[1] = SPAWN_NULL; aidescs[2] = SPAWN_NULL; ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, (const char *) NULL, TRUE, TRUE, (const char *) NULL, (const char *) NULL, (const char *) NULL); if (ipid < 0) return -1; if (ixswait ((unsigned long) ipid, (const char *) NULL) != 0) { /* Make up an errno value. */ errno = EBUSY; return -1; } return 0; } uucp-1.07/unix/dirent.c0000664000076400007640000000472307665321760010546 /* dirent.c Replacements for opendir, readdir and closedir for the original Unix filesystem only. Copyright (C) 1992 Ian Lance Taylor This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "sysdep.h" #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif /* Simple emulations of opendir/readdir/closedir for systems which have the original format of Unix directories. It's probably better to get Doug Gwyn's public domain set of emulation functions. */ DIR * opendir (zdir) const char *zdir; { int o; struct stat s; DIR *qret; o = open ((char *) zdir, O_RDONLY | O_NOCTTY, 0); if (o < 0) return NULL; if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0 || fstat (o, &s) < 0) { int isave; isave = errno; (void) close (o); errno = isave; return NULL; } if (! S_ISDIR (s.st_mode)) { (void) close (o); errno = ENOTDIR; return NULL; } qret = (DIR *) xmalloc (sizeof (DIR)); qret->o = o; return qret; } struct dirent * readdir (q) DIR *q; { struct direct sdir; int cgot; do { cgot = read (q->o, &sdir, sizeof (struct direct)); if (cgot <= 0) return NULL; if (cgot != sizeof (struct direct)) { errno = ENOENT; return NULL; } } while (sdir.d_ino == 0); strncpy (q->s.d_name, sdir.d_name, DIRSIZ); q->s.d_name[DIRSIZ] = '\0'; return &q->s; } int closedir (q) DIR *q; { int o; o = q->o; xfree (q); return close (o); } uucp-1.07/unix/dup2.c0000664000076400007640000000324607665321760010132 /* dup2.c The Unix dup2 function, for systems which only have dup. Copyright (C) 1985, 1986, 1987, 1988, 1990 Free Software Foundation, Inc. This file is part of the Taylor UUCP package. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com. */ #include "uucp.h" #include "sysdep.h" #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif /* I basically took this from the emacs 18.57 distribution, although I cleaned it up a bit and made it POSIX compliant. */ int dup2 (oold, onew) int oold; int onew; { if (oold == onew) return onew; (void) close (onew); #ifdef F_DUPFD return fcntl (oold, F_DUPFD, onew); #else { int onext, oret, isave; onext = dup (oold); if (onext == onew) return onext; if (onext < 0) return -1; oret = dup2 (oold, onew); isave = errno; (void) close (onext); errno = isave; return oret; } #endif } uucp-1.07/unix/ftw.c0000664000076400007640000001131207665321761010052 /* Copyright (C) 1991, 1992 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ian Lance Taylor (ian@airs.com). The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Modified by Ian Lance Taylor for Taylor UUCP, June 1992, and October 1993. */ #include "uucp.h" #include "sysdep.h" #include #if HAVE_LIMITS_H #include #endif #if HAVE_SYS_PARAM_H #include #endif #if HAVE_OPENDIR #if HAVE_DIRENT_H #include #else /* ! HAVE_DIRENT_H */ #include #define dirent direct #endif /* ! HAVE_DIRENT_H */ #endif /* HAVE_OPENDIR */ #if HAVE_FTW_H #include #endif #ifndef PATH_MAX #ifdef MAXPATHLEN #define PATH_MAX MAXPATHLEN #else #define PATH_MAX 1024 #endif #endif /* Traverse one level of a directory tree. */ static int ftw_dir (dirs, level, descriptors, dir, len, func) DIR **dirs; int level; int descriptors; char *dir; size_t len; int (*func) P((const char *file, const struct stat *status, int flag)); { int got; struct dirent *entry; got = 0; errno = 0; while ((entry = readdir (dirs[level])) != NULL) { size_t namlen; struct stat s; int flag, ret, newlev = 0; ++got; namlen = strlen (entry->d_name); if (entry->d_name[0] == '.' && (namlen == 1 || (namlen == 2 && entry->d_name[1] == '.'))) { errno = 0; continue; } if (namlen + len + 1 > PATH_MAX) { #ifdef ENAMETOOLONG errno = ENAMETOOLONG; #else errno = ENOMEM; #endif return -1; } dir[len] = '/'; memcpy ((dir + len + 1), entry->d_name, namlen + 1); if (stat (dir, &s) < 0) { if (errno != EACCES) return -1; flag = FTW_NS; } else if (S_ISDIR (s.st_mode)) { newlev = (level + 1) % descriptors; if (dirs[newlev] != NULL) closedir (dirs[newlev]); dirs[newlev] = opendir (dir); if (dirs[newlev] != NULL) flag = FTW_D; else { if (errno != EACCES) return -1; flag = FTW_DNR; } } else flag = FTW_F; ret = (*func) (dir, &s, flag); if (flag == FTW_D) { if (ret == 0) ret = ftw_dir (dirs, newlev, descriptors, dir, namlen + len + 1, func); if (dirs[newlev] != NULL) { int save; save = errno; closedir (dirs[newlev]); errno = save; dirs[newlev] = NULL; } } if (ret != 0) return ret; if (dirs[level] == NULL) { int skip; dir[len] = '\0'; dirs[level] = opendir (dir); if (dirs[level] == NULL) return -1; skip = got; while (skip-- != 0) { errno = 0; if (readdir (dirs[level]) == NULL) return errno == 0 ? 0 : -1; } } errno = 0; } return errno == 0 ? 0 : -1; } /* Call a function on every element in a directory tree. */ int ftw (dir, func, descriptors) const char *dir; int (*func) P((const char *file, const struct stat *status, int flag)); int descriptors; { DIR **dirs; int c; DIR **p; size_t len; char buf[PATH_MAX + 1]; struct stat s; int flag, ret; if (descriptors <= 0) descriptors = 1; dirs = (DIR **) malloc (descriptors * sizeof (DIR *)); if (dirs == NULL) return -1; c = descriptors; p = dirs; while (c-- != 0) *p++ = NULL; len = strlen (dir); memcpy (buf, dir, len + 1); if (stat (dir, &s) < 0) { if (errno != EACCES) { free ((pointer) dirs); return -1; } flag = FTW_NS; } else if (S_ISDIR (s.st_mode)) { dirs[0] = opendir (dir); if (dirs[0] != NULL) flag = FTW_D; else { if (errno != EACCES) { free ((pointer) dirs); return -1; } flag = FTW_DNR; } } else flag = FTW_F; ret = (*func) (buf, &s, flag); if (flag == FTW_D) { if (ret == 0) { if (len == 1 && *buf == '/') len = 0; ret = ftw_dir (dirs, 0, descriptors, buf, len, func); } if (dirs[0] != NULL) { int save; save = errno; closedir (dirs[0]); errno = save; } } free ((pointer) dirs); return ret; } uucp-1.07/unix/remove.c0000664000076400007640000000024607665321761010553 /* remove.c Remove a file (Unix specific implementation). */ #include "uucp.h" #include "sysdep.h" int remove (z) const char *z; { return unlink (z); } uucp-1.07/unix/rename.c0000664000076400007640000000076507665321761010533 /* rename.c Rename a file to a new name (Unix specific implementation). */ #include "uucp.h" #include "sysdep.h" #include /* This implementation will not work on directories, but fortunately we never want to rename directories. */ int rename (zfrom, zto) const char *zfrom; const char *zto; { if (link (zfrom, zto) < 0) { if (errno != EEXIST) return -1; if (unlink (zto) < 0 || link (zfrom, zto) < 0) return -1; } return unlink (zfrom); } uucp-1.07/unix/strerr.c0000664000076400007640000000055007665321761010575 /* strerr.c Return a string for a Unix errno value. */ #include "uucp.h" #include #ifndef sys_nerr extern int sys_nerr; #endif #ifndef sys_errlist extern char *sys_errlist[]; #endif #undef strerror char * strerror (ierr) int ierr; { if (ierr >= 0 && ierr < sys_nerr) return sys_errlist[ierr]; return (char *) "unknown error"; } uucp-1.07/uucp.info0000664000076400007640000001104507665322530007752 This is uucp.info, produced by makeinfo version 4.1 from uucp.texi. START-INFO-DIR-ENTRY * UUCP: (uucp). Transfer mail and news across phone lines. END-INFO-DIR-ENTRY This file documents Taylor UUCP, version 1.07. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "Copying" are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "Copying" may be included in a translation approved by the author instead of in the original English.  Indirect: uucp.info-1: 1096 uucp.info-2: 50839 uucp.info-3: 100147 uucp.info-4: 149117 uucp.info-5: 197494 uucp.info-6: 241473 uucp.info-7: 285750 uucp.info-8: 333507  Tag Table: (Indirect) Node: Top1096 Node: Copying8521 Node: Introduction10769 Node: Invoking the UUCP Programs19718 Node: Standard Options20638 Node: Invoking uucp22051 Node: uucp Description22320 Node: uucp Options24342 Node: Invoking uux26952 Node: uux Description27270 Node: uux Options30313 Node: uux Examples33052 Node: Invoking uustat33967 Node: uustat Description34302 Node: uustat Options36064 Node: uustat Examples42643 Node: Invoking uuname45411 Node: Invoking uulog46370 Node: Invoking uuto48579 Node: Invoking uupick49533 Node: Invoking cu50839 Node: cu Description51215 Node: cu Commands52601 Node: cu Variables54985 Node: cu Options57140 Node: Invoking uucico58510 Node: uucico Description58786 Node: uucico Options61428 Node: Invoking uuxqt64880 Node: Invoking uuchk66058 Node: Invoking uuconv66644 Node: Invoking uusched68492 Node: Installing Taylor UUCP68930 Node: Compilation69460 Node: Testing the Compilation76413 Node: Installing the Binaries80373 Node: Configuration81395 Node: Testing the Installation83569 Node: Using Taylor UUCP86701 Node: Calling Other Systems87150 Node: Accepting Calls89674 Node: Mail and News91636 Node: Sending mail or news92729 Node: Receiving mail or news94178 Node: The Spool Directory Layout94942 Node: System Spool Directories95857 Node: Status Directory97807 Node: Execution Subdirectories100147 Node: Other Spool Subdirectories101426 Node: Spool Lock Files104404 Node: Spool Directory Cleaning106953 Node: Configuration Files108145 Node: Configuration Overview109728 Node: Configuration File Format111880 Node: Configuration Examples113553 Node: config File Examples114122 Node: Leaf Example118001 Node: Gateway Example120687 Node: Time Strings125052 Node: Chat Scripts126868 Node: config File136057 Node: Miscellaneous (config)137121 Node: Configuration File Names142943 Node: Log File Names147880 Node: Debugging Levels149117 Node: sys File151983 Node: Defaults and Alternates152863 Node: Naming the System155955 Node: Calling Out157796 Node: When to Call158153 Node: Placing the Call163763 Node: Logging In167285 Node: Accepting a Call170896 Node: Protocol Selection173708 Node: File Transfer Control187452 Node: Miscellaneous (sys)193744 Node: Default sys File Values196872 Node: port File197494 Node: dial File209257 Node: UUCP Over TCP215278 Node: TCP Client215782 Node: TCP Server217554 Node: Security219008 Node: Protocols224051 Node: UUCP Protocol Sources225671 Node: UUCP Grades227919 Node: UUCP Lock Files232212 Node: Execution File Format235609 Node: UUCP Protocol240973 Node: The Initial Handshake241473 Node: UUCP Protocol Commands249866 Node: The S Command251888 Node: The R Command258938 Node: The X Command263032 Node: The E Command264950 Node: The H Command268063 Node: The Final Handshake268896 Node: g Protocol269558 Node: f Protocol279982 Node: t Protocol283060 Node: e Protocol284114 Node: Big G Protocol284902 Node: i Protocol285750 Node: j Protocol294764 Node: x Protocol301031 Node: y Protocol301587 Node: d Protocol306138 Node: h Protocol306503 Node: v Protocol306807 Node: Hacking307298 Node: System Dependence307666 Node: Naming Conventions309054 Node: Patches311229 Node: Acknowledgements313179 Node: Index (concepts)326563 Node: Index (configuration file)333507  End Tag Table uucp-1.07/uucp.info-10000664000076400007640000014322707665322530010120 This is uucp.info, produced by makeinfo version 4.1 from uucp.texi. START-INFO-DIR-ENTRY * UUCP: (uucp). Transfer mail and news across phone lines. END-INFO-DIR-ENTRY This file documents Taylor UUCP, version 1.07. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "Copying" are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "Copying" may be included in a translation approved by the author instead of in the original English.  File: uucp.info, Node: Top, Next: Copying, Prev: (dir), Up: (dir) Taylor UUCP 1.07 **************** This is the documentation for the Taylor UUCP package, version 1.07. The programs were written by Ian Lance Taylor. The author can be reached at . There is a mailing list for discussion of the package. The list is hosted by Eric Schnoebelen at . To join (or get off) the list, send mail to . Mail to this address is answered by the majordomo program. To join the list, send the message `subscribe ADDRESS' where ADDRESS is your e-mail address. To send a message to the list, send it to . There is an archive of all messages sent to the mailing list at . * Menu: * Copying:: Taylor UUCP Copying Conditions * Introduction:: Introduction to Taylor UUCP * Invoking the UUCP Programs:: Invoking the UUCP Programs * Installing Taylor UUCP:: Installing Taylor UUCP * Using Taylor UUCP:: Using Taylor UUCP * Configuration Files:: Taylor UUCP Configuration Files * Protocols:: UUCP Protocol Descriptions * Hacking:: Hacking Taylor UUCP * Acknowledgements:: Acknowledgements * Index (concepts):: Concept Index * Index (configuration file):: Index to New Configuration Files --- The Detailed Node Listing --- Invoking the UUCP Programs * Standard Options:: Standard Options for the UUCP Programs * Invoking uucp:: Invoking uucp * Invoking uux:: Invoking uux * Invoking uustat:: Invoking uustat * Invoking uuname:: Invoking uuname * Invoking uulog:: Invoking uulog * Invoking uuto:: Invoking uuto * Invoking uupick:: Invoking uupick * Invoking cu:: Invoking cu * Invoking uucico:: Invoking uucico * Invoking uuxqt:: Invoking uuxqt * Invoking uuchk:: Invoking uuchk * Invoking uuconv:: Invoking uuconv * Invoking uusched:: Invoking uusched Invoking uucp * uucp Description:: Description of uucp * uucp Options:: Options Supported by uucp Invoking uux * uux Description:: Description of uux * uux Options:: Options Supported by uux * uux Examples:: Examples of uux Usage Invoking uustat * uustat Description:: Description of uustat * uustat Options:: Options Supported by uustat * uustat Examples:: Examples of uustat Usage Invoking cu * cu Description:: Description of cu * cu Commands:: Commands Supported by cu * cu Variables:: Variables Supported by cu * cu Options:: Options Supported by cu Invoking uucico * uucico Description:: Description of uucico * uucico Options:: Options Supported by uucico Installing Taylor UUCP * Compilation:: Compiling Taylor UUCP * Testing the Compilation:: Testing the Compilation * Installing the Binaries:: Installing the Binaries * Configuration:: Configuring Taylor UUCP * Testing the Installation:: Testing the Installation Using Taylor UUCP * Calling Other Systems:: Calling Other Systems * Accepting Calls:: Accepting Calls * Mail and News:: Using UUCP for Mail and News * The Spool Directory Layout:: The Spool Directory Layout * Spool Directory Cleaning:: Cleaning the UUCP Spool Directory Using UUCP for Mail and News. * Sending mail or news:: Sending mail or news via UUCP * Receiving mail or news:: Receiving mail or news via UUCP The Spool Directory Layout * System Spool Directories:: System Spool Directories * Status Directory:: Status Spool Directory * Execution Subdirectories:: Execution Spool Subdirectories * Other Spool Subdirectories:: Other Spool Subdirectories * Spool Lock Files:: Spool Directory Lock Files Taylor UUCP Configuration Files * Configuration Overview:: Configuration File Overview * Configuration File Format:: Configuration File Format * Configuration Examples:: Examples of Configuration Files * Time Strings:: How to Write Time Strings * Chat Scripts:: How to Write Chat Scripts * config File:: The Main Configuration File * sys File:: The System Configuration File * port File:: The Port Configuration Files * dial File:: The Dialer Configuration Files * UUCP Over TCP:: UUCP Over TCP * Security:: Security Issues Examples of Configuration Files * config File Examples:: Examples of the Main Configuration File * Leaf Example:: Call a Single Remote Site * Gateway Example:: The Gateway for Several Local Systems The Main Configuration File * Miscellaneous (config):: Miscellaneous config File Commands * Configuration File Names:: Using Different Configuration Files * Log File Names:: Using Different Log Files * Debugging Levels:: Debugging Levels The System Configuration File * Defaults and Alternates:: Using Defaults and Alternates * Naming the System:: Naming the System * Calling Out:: Calling Out * Accepting a Call:: Accepting a Call * Protocol Selection:: Protocol Selection * File Transfer Control:: File Transfer Control * Miscellaneous (sys):: Miscellaneous sys File Commands * Default sys File Values:: Default Values Calling Out * When to Call:: When to Call * Placing the Call:: Placing the Call * Logging In:: Logging In UUCP Over TCP * TCP Client:: Connecting to Another System Over TCP * TCP Server:: Running a TCP Server UUCP Protocol Internals * UUCP Protocol Sources:: Sources for UUCP Protocol Information * UUCP Grades:: UUCP Grades * UUCP Lock Files:: UUCP Lock Files * Execution File Format:: Execution File Format * UUCP Protocol:: UUCP Protocol * g Protocol:: g protocol * f Protocol:: f protocol * t Protocol:: t protocol * e Protocol:: e protocol * Big G Protocol:: G protocol * i Protocol:: i protocol * j Protocol:: j protocol * x Protocol:: x protocol * y Protocol:: y protocol * d Protocol:: d protocol * h Protocol:: h protocol * v Protocol:: v protocol UUCP Protocol * The Initial Handshake:: The Initial Handshake * UUCP Protocol Commands:: UUCP Protocol Commands * The Final Handshake:: The Final Handshake UUCP Protocol Commands * The S Command:: The S Command * The R Command:: The R Command * The X Command:: The X Command * The E Command:: The E Command * The H Command:: The H Command Hacking Taylor UUCP * System Dependence:: System Dependence * Naming Conventions:: Naming Conventions * Patches:: Patches  File: uucp.info, Node: Copying, Next: Introduction, Prev: Top, Up: Top Taylor UUCP Copying Conditions ****************************** This package is covered by the GNU Public License. See the file `COPYING' for details. If you would like to do something with this package that you feel is reasonable, but you feel is prohibited by the license, contact me to see if we can work it out. The rest of this section is some descriptive text from the Free Software Foundation. All the programs, scripts and documents relating to Taylor UUCP are "free"; this means that everyone is free to use them and free to redistribute them on a free basis. The Taylor UUCP-related programs are not in the public domain; they are copyrighted and there are restrictions on their distribution, but these restrictions are designed to permit everything that a good cooperating citizen would want to do. What is not allowed is to try to prevent others from further sharing any version of these programs that they might get from you. Specifically, we want to make sure that you have the right to give away copies of the programs that relate to Taylor UUCP, that you receive source code or else can get it if you want it, that you can change these programs or use pieces of them in new free programs, and that you know you can do these things. To make sure that everyone has such rights, we have to forbid you to deprive anyone else of these rights. For example, if you distribute copies of the Taylor UUCP related programs, 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 tell them their rights. Also, for our own protection, we must make certain that everyone finds out that there is no warranty for the programs that relate to Taylor UUCP. If these programs are modified by someone else and passed on, we want their recipients to know that what they have is not what we distributed, so that any problems introduced by others will not reflect on our reputation. The precise conditions of the licenses for the programs currently being distributed that relate to Taylor UUCP are found in the General Public Licenses that accompany them.  File: uucp.info, Node: Introduction, Next: Invoking the UUCP Programs, Prev: Copying, Up: Top Introduction to Taylor UUCP *************************** General introductions to UUCP are available, and perhaps one day I will write one. In the meantime, here is a very brief one that concentrates on the programs provided by Taylor UUCP. Taylor UUCP is a complete UUCP package. It is covered by the GNU Public License, which means that the source code is always available. It is composed of several programs; most of the names of these programs are based on earlier UUCP packages. `uucp' The `uucp' program is used to copy file between systems. It is similar to the standard Unix `cp' program, except that you can refer to a file on a remote system by using `system!' before the file name. For example, to copy the file `notes.txt' to the system `airs', you would say `uucp notes.txt airs!~/notes.txt'. In this example `~' is used to name the UUCP public directory on `airs'. For more details, see *Note uucp: Invoking uucp. `uux' The `uux' program is used to request the execution of a program on a remote system. This is how mail and news are transferred over UUCP. As with `uucp', programs and files on remote systems may be named by using `system!'. For example, to run the `rnews' program on `airs', passing it standard input, you would say `uux - airs!rnews'. The `-' means to read standard input and set things up such that when `rnews' runs on `airs' it will receive the same standard input. For more details, see *Note uux: Invoking uux. Neither `uucp' nor `uux' actually do any work immediately. Instead, they queue up requests for later processing. They then start a daemon process which processes the requests and calls up the appropriate systems. Normally the system will also start the daemon periodically to check if there is any work to be done. The advantage of this approach is that it all happens automatically. You don't have to sit around waiting for the files to be transferred. The disadvantage is that if anything goes wrong it might be a while before anybody notices. `uustat' The `uustat' program does many things. By default it will simply list all the jobs you have queued with `uucp' or `uux' that have not yet been processed. You can use `uustat' to remove any of your jobs from the queue. You can also it use it to show the status of the UUCP system in various ways, such as showing the connection status of all the remote systems your system knows about. The system administrator can use `uustat' to automatically discard old jobs while sending mail to the user who requested them. For more details, see *Note uustat: Invoking uustat. `uuname' The `uuname' program by default lists all the remote systems your system knows about. You can also use it to get the name of your local system. It is mostly useful for shell scripts. For more details, see *Note uuname: Invoking uuname. `uulog' The `uulog' program can be used to display entries in the UUCP log file. It can select the entries for a particular system or a particular user. You can use it to see what has happened to your queued jobs in the past. For more details, see *Note uulog: Invoking uulog. `uuto' `uupick' `uuto' is a simple shell script interface to `uucp'. It will transfer a file, or the contents of a directory, to a remote system, and notify a particular user on the remote system when it arrives. The remote user can then retrieve the file(s) with `uupick'. For more details, see *Note uuto: Invoking uuto, and see *Note uupick: Invoking uupick. `cu' The `cu' program can be used to call up another system and communicate with it as though you were directly connected. It can also do simple file transfers, though it does not provide any error checking. For more details, *Note cu: Invoking cu. These eight programs just described, `uucp', `uux', `uuto', `uupick', `uustat', `uuname', `uulog', and `cu' are the user programs provided by Taylor UUCP. `uucp', `uux', and `uuto' add requests to the work queue, `uupick' extracts files from the UUCP public directory, `uustat' examines the work queue, `uuname' examines the configuration files, `uulog' examines the log files, and `cu' just uses the UUCP configuration files. The real work is actually done by two daemon processes, which are normally run automatically rather than by a user. `uucico' The `uucico' daemon is the program which actually calls the remote system and transfers files and requests. `uucico' is normally started automatically by `uucp' and `uux'. Most systems will also start it periodically to make sure that all work requests are handled. `uucico' checks the queue to see what work needs to be done, and then calls the appropriate systems. If the call fails, perhaps because the phone line is busy, `uucico' leaves the requests in the queue and goes on to the next system to call. It is also possible to force `uucico' to call a remote system even if there is no work to be done for it, so that it can pick up any work that may be queued up remotely. For more details, see *Note uucico: Invoking uucico. `uuxqt' The `uuxqt' daemon processes execution requests made by the `uux' program on remote systems. It also processes requests made on the local system which require files from a remote system. It is normally started by `uucico'. For more details, see *Note uuxqt: Invoking uuxqt. Suppose you, on the system `bantam', want to copy a file to the system `airs'. You would run the `uucp' command locally, with a command like `uucp notes.txt airs!~/notes.txt'. This would queue up a request on `bantam' for `airs', and would then start the `uucico' daemon. `uucico' would see that there was a request for `airs' and attempt to call it. When the call succeeded, another copy of `uucico' would be started on `airs'. The two copies of `uucico' would tell each other what they had to do and transfer the file from `bantam' to `airs'. When the file transfer was complete the `uucico' on `airs' would move it into the UUCP public directory. UUCP is often used to transfer mail. This is normally done automatically by mailer programs. When `bantam' has a mail message to send to `ian' at `airs', it executes `uux - airs!rmail ian' and writes the mail message to the `uux' process as standard input. The `uux' program, running on `bantam', will read the standard input and store it, as well as the `rmail' request itself, on the work queue for `airs'. `uux' will then start the `uucico' daemon. The `uucico' daemon will call up `airs', just as in the `uucp' example, and transfer the work request and the mail message. The `uucico' daemon on `airs' will put the files on a local work queue. When the communication session is over, the `uucico' daemon on `airs' will start the `uuxqt' daemon. `uuxqt' will see the request on the work queue, and will run `rmail ian' with the mail message as standard input. The `rmail' program, which is not part of the UUCP package, is then responsible for either putting the message in the right mailbox on `airs' or forwarding the message on to another system. Taylor UUCP comes with a few other programs that are useful when installing and configuring UUCP. `uuchk' The `uuchk' program reads the UUCP configuration files and displays a rather lengthy description of what it finds. This is useful when configuring UUCP to make certain that the UUCP package will do what you expect it to do. For more details, see *Note uuchk: Invoking uuchk. `uuconv' The `uuconv' program can be used to convert UUCP configuration files from one format to another. This can be useful for administrators converting from an older UUCP package. Taylor UUCP is able to read and use old configuration file formats, but some new features can not be selected using the old formats. For more details, see *Note uuconv: Invoking uuconv. `uusched' The `uusched' script is provided for compatibility with older UUCP releases. It starts `uucico' to call, one at a time, all the systems for which work has been queued. For more details, see *Note uusched: Invoking uusched. `tstuu' The `tstuu' program is a test harness for the UUCP package; it can help check that the package has been configured and compiled correctly. However, it uses pseudo-terminals, which means that it is less portable than the rest of the package. If it works, it can be useful when initially installing Taylor UUCP. For more details, see *Note tstuu: Testing the Compilation.  File: uucp.info, Node: Invoking the UUCP Programs, Next: Installing Taylor UUCP, Prev: Introduction, Up: Top Invoking the UUCP Programs ************************** This chapter describes how to run the UUCP programs. * Menu: * Standard Options:: Standard Options for the UUCP Programs * Invoking uucp:: Invoking uucp * Invoking uux:: Invoking uux * Invoking uustat:: Invoking uustat * Invoking uuname:: Invoking uuname * Invoking uulog:: Invoking uulog * Invoking uuto:: Invoking uuto * Invoking uupick:: Invoking uupick * Invoking cu:: Invoking cu * Invoking uucico:: Invoking uucico * Invoking uuxqt:: Invoking uuxqt * Invoking uuchk:: Invoking uuchk * Invoking uuconv:: Invoking uuconv * Invoking uusched:: Invoking uusched  File: uucp.info, Node: Standard Options, Next: Invoking uucp, Prev: Invoking the UUCP Programs, Up: Invoking the UUCP Programs Standard Options ================ All of the UUCP programs support a few standard options. `-x type' `--debug type' Turn on particular debugging types. The following types are recognized: `abnormal', `chat', `handshake', `uucp-proto', `proto', `port', `config', `spooldir', `execute', `incoming', `outgoing'. Not all types of debugging are effective for all programs. See the `debug' configuration command for details (*note Debugging Levels::). Multiple types may be given, separated by commas, and the `--debug' option may appear multiple times. A number may also be given, which will turn on that many types from the foregoing list; for example, `--debug 2' is equivalent to `--debug abnormal,chat'. To turn on all types of debugging, use `-x all'. The `uulog' program uses `-X' rather than `-x' to select the debugging type; for `uulog', `-x' has a different meaning, for reasons of historical compatibility. `-I file' `--config file' Set the main configuration file to use. *Note config File::. When this option is used, the programs will revoke any setuid privileges. `-v' `--version' Report version information and exit. `--help' Print a help message and exit.  File: uucp.info, Node: Invoking uucp, Next: Invoking uux, Prev: Standard Options, Up: Invoking the UUCP Programs Invoking uucp ============= * Menu: * uucp Description:: Description of uucp * uucp Options:: Options Supported by uucp  File: uucp.info, Node: uucp Description, Next: uucp Options, Prev: Invoking uucp, Up: Invoking uucp uucp Description ---------------- uucp [options] `source-file' `destination-file' uucp [options] `source-file'... `destination-directory' The `uucp' command copies files between systems. Each `file' argument is either a file name on the local machine or is of the form `system!file'. The latter is interpreted as being on a remote system. When `uucp' is used with two non-option arguments, the contents of the first file are copied to the second. With more than two non-option arguments, each source file is copied into the destination directory. A file may be transferred to or from `system2' via `system1' by using `system1!system2!file'. Any file name that does not begin with `/' or `~' will be prepended with the current directory (unless the `-W' or `--noexpand' options are used). For example, if you are in the directory `/home/ian', then `uucp foo remote!bar' is equivalent to `uucp /home/ian/foo remote!/home/ian/bar'. Note that the resulting file name may not be valid on a remote system. A file name beginning with a simple `~' starts at the UUCP public directory; a file name beginning with `~name' starts at the home directory of the named user. The `~' is interpreted on the appropriate system. Note that some shells will interpret an initial `~' before `uucp' sees it; to avoid this the `~' must be quoted. The shell metacharacters `?' `*' `[' and `]' are interpreted on the appropriate system, assuming they are quoted to prevent the shell from interpreting them first. The file copy does not take place immediately, but is queued up for the `uucico' daemon; the daemon is started immediately unless the `-r' or `--nouucico' option is given. The next time the remote system is called, the file(s) will be copied. *Note Invoking uucico::. The file mode is not preserved, except for the execute bit. The resulting file is owned by the uucp user.  File: uucp.info, Node: uucp Options, Prev: uucp Description, Up: Invoking uucp uucp Options ------------ The following options may be given to `uucp'. `-c' `--nocopy' Do not copy local source files to the spool directory. If they are removed before being processed by the `uucico' daemon, the copy will fail. The files must be readable by the `uucico' daemon, and by the invoking user. `-C' `--copy' Copy local source files to the spool directory. This is the default. `-d' `--directories' Create all necessary directories when doing the copy. This is the default. `-f' `--nodirectories' If any necessary directories do not exist for the destination file name, abort the copy. `-R' `--recursive' If any of the source file names are directories, copy their contents recursively to the destination (which must itself be a directory). `-g grade' `--grade grade' Set the grade of the file transfer command. Jobs of a higher grade are executed first. Grades run `0' to `9', `A' to `Z', `a' to `z', from high to low. *Note When to Call::. `-m' `--mail' Report completion or failure of the file transfer by sending mail. `-n user' `--notify user' Report completion or failure of the file transfer by sending mail to the named user on the destination system. `-r' `--nouucico' Do not start the `uucico' daemon immediately; merely queue up the file transfer for later execution. `-j' `--jobid' Print the jobid on standard output. The job may be later cancelled by passing this jobid to the `-kill' switch of `uustat'. *Note Invoking uustat::. It is possible for some complex operations to produce more than one jobid, in which case each will be printed on a separate line. For example uucp sys1!~user1/file1 sys2!~user2/file2 ~user3 will generate two separate jobs, one for the system `sys1' and one for the system `sys2'. `-W' `--noexpand' Do not prepend remote relative file names with the current directory. `-t' `--uuto' This option is used by the `uuto' shell script; see *Note Invoking uuto::. It causes `uucp' to interpret the final argument as `system!user'. The file(s) are sent to `~/receive/USER/LOCAL' on the remote system, where USER is from the final argument and LOCAL is the local UUCP system name. Also, `uucp' will act as though `--notify user' were specified. `-x type' `--debug type' `-I file' `--config file' `-v' `--version' `--help' *Note Standard Options::.  File: uucp.info, Node: Invoking uux, Next: Invoking uustat, Prev: Invoking uucp, Up: Invoking the UUCP Programs Invoking uux ============ * Menu: * uux Description:: Description of uux * uux Options:: Options Supported by uux * uux Examples:: Examples of uux Usage  File: uucp.info, Node: uux Description, Next: uux Options, Prev: Invoking uux, Up: Invoking uux uux Description --------------- uux [options] command The `uux' command is used to execute a command on a remote system, or to execute a command on the local system using files from remote systems. The command is not executed immediately; the request is queued until the `uucico' daemon calls the system and transfers the necessary files. The daemon is started automatically unless one of the `-r' or `--nouucico' options is given. The actual command execution is done by the `uuxqt' daemon on the appropriate system. File arguments can be gathered from remote systems to the execution system, as can standard input. Standard output may be directed to a file on a remote system. The command name may be preceded by a system name followed by an exclamation point if it is to be executed on a remote system. An empty system name is taken as the local system. Each argument that contains an exclamation point is treated as naming a file. The system which the file is on is before the exclamation point, and the file name on that system follows it. An empty system name is taken as the local system; this form must be used to transfer a file to a command being executed on a remote system. If the file name is not absolute, the current working directory will be prepended to it; the result may not be meaningful on the remote system. A file name may begin with `~/', in which case it is relative to the UUCP public directory on the appropriate system. A file name may begin with `~name/', in which case it is relative to the home directory of the named user on the appropriate system. Standard input and output may be redirected as usual; the file names used may contain exclamation points to indicate that they are on remote systems. Note that the redirection characters must be quoted so that they are passed to `uux' rather than interpreted by the shell. Append redirection (`>>') does not work. All specified files are gathered together into a single directory before execution of the command begins. This means that each file must have a distinct name. For example, uux 'sys1!diff sys2!~user1/foo sys3!~user2/foo >!foo.diff' will fail because both files will be copied to `sys1' and stored under the name `foo'. Arguments may be quoted by parentheses to avoid interpretation of exclamation points. This is useful when executing the `uucp' command on a remote system. Most systems restrict the commands which may be executed using `uux'. Many permit only the execution of `rmail' and `rnews'. A request to execute an empty command (e.g., `uux sys!') will create a poll file for the specified system; see *Note Calling Other Systems:: for an example of why this might be useful. The exit status of `uux' is one of the codes found in the header file `sysexits.h'. In particular, `EX_OK' (`0') indicates success, and `EX_TEMPFAIL' (`75') indicates a temporary failure.  File: uucp.info, Node: uux Options, Next: uux Examples, Prev: uux Description, Up: Invoking uux uux Options ----------- The following options may be given to `uux'. `-' `-p' `--stdin' Read standard input up to end of file, and use it as the standard input for the command to be executed. `-c' `--nocopy' Do not copy local files to the spool directory. This is the default. If they are removed before being processed by the `uucico' daemon, the copy will fail. The files must be readable by the `uucico' daemon, as well as the by the invoker of `uux'. `-C' `--copy' Copy local files to the spool directory. `-l' `--link' Link local files into the spool directory. If a file can not be linked because it is on a different device, it will be copied unless one of the `-c' or `--nocopy' options also appears (in other words, use of `--link' switches the default from `--nocopy' to `--copy'). If the files are changed before being processed by the `uucico' daemon, the changed versions will be used. The files must be readable by the `uucico' daemon, as well as by the invoker of `uux'. `-g grade' `--grade grade' Set the grade of the file transfer command. Jobs of a higher grade are executed first. Grades run `0' to `9', `A' to `Z', `a' to `z', from high to low. *Note When to Call::. `-n' `--notification=no' Do not send mail about the status of the job, even if it fails. `-z' `--notification=error' Send mail about the status of the job if an error occurs. For many `uuxqt' daemons, including the Taylor UUCP `uuxqt', this is the default action; for those, `--notification=error' will have no effect. However, some `uuxqt' daemons will send mail if the job succeeds, unless the `--notification=error' option is used. Some other `uuxqt' daemons will not send mail even if the job fails, unless the `--notification=error' option is used. `-a address' `--requestor address' Report job status, as controlled by the `--notification' option, to the specified mail address. `-r' `--nouucico' Do not start the `uucico' daemon immediately; merely queue up the execution request for later processing. `-j' `--jobid' Print the jobid on standard output. A jobid will be generated for each file copy operation required to execute the command. These file copies may be later cancelled by passing the jobid to the `-kill' switch of `uustat'. *Note Invoking uustat::. Cancelling any file copies will make it impossible to complete execution of the job. `-x type' `--debug type' `-v' `--version' `--help' *Note Standard Options::.  File: uucp.info, Node: uux Examples, Prev: uux Options, Up: Invoking uux uux Examples ------------ Here are some examples of using `uux'. uux -z - sys1!rmail user1 This will execute the command `rmail user1' on the system `sys1', giving it as standard input whatever is given to `uux' as standard input. If a failure occurs, mail will be sent to the user who ran the command. uux 'diff -c sys1!~user1/file1 sys2!~user2/file2 >!file.diff' This will fetch the two named files from system `sys1' and system `sys2' and execute `diff', putting the result in `file.diff' in the current directory on the local system. The current directory must be writable by the `uuxqt' daemon for this to work. uux 'sys1!uucp ~user1/file1 (sys2!~user2/file2)' Execute `uucp' on the system `sys1' copying `file1' (on system `sys1') to `sys2'. This illustrates the use of parentheses for quoting.  File: uucp.info, Node: Invoking uustat, Next: Invoking uuname, Prev: Invoking uux, Up: Invoking the UUCP Programs Invoking uustat =============== * Menu: * uustat Description:: Description of uustat * uustat Options:: Options Supported by uustat * uustat Examples:: Examples of uustat Usage  File: uucp.info, Node: uustat Description, Next: uustat Options, Prev: Invoking uustat, Up: Invoking uustat uustat Description ------------------ uustat -a uustat --all uustat [-eKRiMNQ] [-sS system] [-uU user] [-cC command] [-oy hours] [-B lines] [--executions] [--kill-all] [--rejuvenate-all] [--prompt] [--mail] [--notify] [--no-list] [--system system] [--not-system system] [--user user] [--not-user user] [--command command] [--not-command command] [--older-than hours] [--younger-than hours] [--mail-lines lines] uustat [-kr jobid] [--kill jobid] [--rejuvenate jobid] uustat -q [-sS system] [-oy hours] [--system system] [--not-system system ] [--older-than hours] [--younger-than hours] uustat --list [-sS system] [-oy hours] [--system system ] [--not-system system] [--older-than hours] [--younger-than hours] uustat -m uustat --status uustat -p uustat --ps The `uustat' command can display various types of status information about the UUCP system. It can also be used to cancel or rejuvenate requests made by `uucp' or `uux'. With no options, `uustat' displays all jobs queued up for the invoking user, as if given the `--user' option with the appropriate argument. If any of the `-a', `--all', `-e', `--executions', `-s', `--system', `-S', `--not-system', `-u', `--user', `-U', `--not-user', `-c', `--command', `-C', `--not-command', `-o', `--older-than', `-y', or `--younger-than' options are given, then all jobs which match the combined specifications are displayed. The `-K' or `--kill-all' option may be used to kill off a selected group of jobs, such as all jobs more than 7 days old.  File: uucp.info, Node: uustat Options, Next: uustat Examples, Prev: uustat Description, Up: Invoking uustat uustat Options -------------- The following options may be given to `uustat'. `-a' `--all' List all queued file transfer requests. `-e' `--executions' List queued execution requests rather than queued file transfer requests. Queued execution requests are processed by `uuxqt' rather than `uucico'. Queued execution requests may be waiting for some file to be transferred from a remote system. They are created by an invocation of `uux'. `-s system' `--system system' List all jobs queued up for the named system. These options may be specified multiple times, in which case all jobs for all the named systems will be listed. If used with `--list', only the systems named will be listed. `-S system' `--not-system system' List all jobs queued for systems other than the one named. These options may be specified multiple times, in which case no jobs from any of the specified systems will be listed. If used with `--list', only the systems not named will be listed. These options may not be used with `-s' or `--system'. `-u user' `--user user' List all jobs queued up for the named user. These options may be specified multiple times, in which case all jobs for all the named users will be listed. `-U user' `--not-user user' List all jobs queued up for users other than the one named. These options may be specified multiple times, in which case no jobs from any of the specified users will be listed. These options may not be used with `-u' or `--user'. `-c command' `--command command' List all jobs requesting the execution of the named command. If `command' is `ALL' this will list all jobs requesting the execution of some command (as opposed to simply requesting a file transfer). These options may be specified multiple times, in which case all jobs requesting any of the commands will be listed. `-C command' `--not-command command' List all jobs requesting execution of some command other than the named command, or, if `command' is `ALL', list all jobs that simply request a file transfer (as opposed to requesting the execution of some command). These options may be specified multiple times, in which case no job requesting one of the specified commands will be listed. These options may not be used with `-c' or `--command'. `-o hours' `--older-than hours' List all queued jobs older than the given number of hours. If used with `--list', only systems whose oldest job is older than the given number of hours will be listed. `-y hours' `--younger-than hours' List all queued jobs younger than the given number of hours. If used with `--list', only systems whose oldest job is younger than the given number of hours will be listed. `-k jobid' `--kill jobid' Kill the named job. The job id is shown by the default output format, as well as by the `-j' or `--jobid' options to `uucp' or `uux'. A job may only be killed by the user who created the job, or by the UUCP administrator, or the superuser. The `-k' or `--kill' options may be used multiple times on the command line to kill several jobs. `-r jobid' `--rejuvenate jobid' Rejuvenate the named job. This will mark it as having been invoked at the current time, affecting the output of the `-o', `--older-than', `-y', or `--younger-than' options, possibly preserving it from any automated cleanup daemon. The job id is shown by the default output format, as well as by the `-j' or `--jobid' options to `uucp' or `uux'. A job may only be rejuvenated by the user who created the job, or by the UUCP administrator, or the superuser. The `-r' or `--rejuvenate' options may be used multiple times on the command line to rejuvenate several jobs. `-q' `--list' Display the status of commands, executions and conversations for all remote systems for which commands or executions are queued. The `-s', `--system', `-S', `--not-system', `-o', `--older-than', `-y', and `--younger-than' options may be used to restrict the systems which are listed. Systems for which no commands or executions are queued will never be listed. `-m' `--status' Display the status of conversations for all remote systems. `-p' `--ps' Display the status of all processes holding UUCP locks on systems or ports. `-i' `--prompt' For each listed job, prompt whether to kill the job or not. If the first character of the input line is `y' or `Y', the job will be killed. `-K' `--kill-all' Automatically kill each listed job. This can be useful for automatic cleanup scripts, in conjunction with the `--mail' and `--notify' options. `-R' `--rejuvenate-all' Automatically rejuvenate each listed job. This may not be used with `--kill-all'. `-M' `--mail' For each listed job, send mail to the UUCP administrator. If the job is killed (due to `--kill-all', or `--prompt' with an affirmative response) the mail will indicate that. A comment specified by the `--comment' option may be included. If the job is an execution, the initial portion of its standard input will be included in the mail message; the number of lines to include may be set with the `--mail-lines' option (the default is 100). If the standard input contains null characters, it is assumed to be a binary file and is not included. `-N' `--notify' For each listed job, send mail to the user who requested the job. The mail is identical to that sent by the `-M' or `--mail' options. `-W comment' `--comment comment' Specify a comment to be included in mail sent with the `-M', `--mail', `-N', or `--notify' options. `-B lines' `--mail-lines lines' When the `-M', `--mail', `-N', or `--notify' options are used to send mail about an execution with standard input, this option controls the number of lines of standard input to include in the message. The default is 100. `-Q' `--no-list' Do not actually list the job, but only take any actions indicated by the `-i', `--prompt', `-K', `--kill-all', `-M', `--mail', `-N' or `--notify' options. `-x type' `--debug type' `-I file' `--config file' `-v' `--version' `--help' *Note Standard Options::.  File: uucp.info, Node: uustat Examples, Prev: uustat Options, Up: Invoking uustat uustat Examples --------------- uustat --all Display status of all jobs. A sample output line is as follows: bugsA027h bugs ian 04-01 13:50 Executing rmail ian@airs.com (sending 12 bytes) The format is jobid system user queue-date command (size) The jobid may be passed to the `--kill' or `--rejuvenate' options. The size indicates how much data is to be transferred to the remote system, and is absent for a file receive request. The `--system', `--not-system', `--user', `--not-user', `--command', `--not-command', `--older-than', and `--younger-than' options may be used to control which jobs are listed. uustat --executions Display status of queued up execution requests. A sample output line is as follows: bugs bugs!ian 05-20 12:51 rmail ian The format is system requestor queue-date command The `--system', `--not-system', `--user', `--not-user', `--command', `--not-command', `--older-than', and `--younger-than' options may be used to control which requests are listed. uustat --list Display status for all systems with queued up commands. A sample output line is as follows: bugs 4C (1 hour) 0X (0 secs) 04-01 14:45 Dial failed This indicates the system, the number of queued commands, the age of the oldest queued command, the number of queued local executions, the age of the oldest queued execution, the date of the last conversation, and the status of that conversation. uustat --status Display conversation status for all remote systems. A sample output line is as follows: bugs 04-01 15:51 Conversation complete This indicates the system, the date of the last conversation, and the status of that conversation. If the last conversation failed, `uustat' will indicate how many attempts have been made to call the system. If the retry period is currently preventing calls to that system, `uustat' also displays the time when the next call will be permitted. uustat --ps Display the status of all processes holding UUCP locks. The output format is system dependent, as `uustat' simply invokes `ps' on each process holding a lock. uustat -c rmail -o 168 -K -Q -M -N -W "Queued for over 1 week" This will kill all `rmail' commands that have been queued up waiting for delivery for over 1 week (168 hours). For each such command, mail will be sent both to the UUCP administrator and to the user who requested the rmail execution. The mail message sent will include the string given by the `-W' option. The `-Q' option prevents any of the jobs from being listed on the terminal, so any output from the program will be error messages.  File: uucp.info, Node: Invoking uuname, Next: Invoking uulog, Prev: Invoking uustat, Up: Invoking the UUCP Programs Invoking uuname =============== uuname [-a] [--aliases] uuname -l uuname --local By default, the `uuname' program simply lists the names of all the remote systems mentioned in the UUCP configuration files. The `uuname' program may also be used to print the UUCP name of the local system. The `uuname' program is mainly for use by shell scripts. The following options may be given to `uuname'. `-a' `--aliases' List all aliases for remote systems, as well as their canonical names. Aliases may be specified in the `sys' file (*note Naming the System::). `-l' `--local' Print the UUCP name of the local system, rather than listing the names of all the remote systems. `-x type' `--debug type' `-I file' `--config file' `-v' `--version' `--help' *Note Standard Options::.  File: uucp.info, Node: Invoking uulog, Next: Invoking uuto, Prev: Invoking uuname, Up: Invoking the UUCP Programs Invoking uulog ============== uulog [-#] [-n lines] [-sf system] [-u user] [-DSF] [--lines lines] [--system system] [--user user] [--debuglog] [--statslog] [--follow] [--follow=system] The `uulog' program may be used to display the UUCP log file. Different options may be used to select which parts of the file to display. `-#' `-n lines' `--lines lines' Here `#' is a number; e.g., `-10'. The specified number of lines is displayed from the end of the log file. The default is to display the entire log file, unless the `-f', `-F', or `--follow' options are used, in which case the default is to display 10 lines. `-s system' `--system system' Display only log entries pertaining to the specified system. `-u user' `--user user' Display only log entries pertaining to the specified user. `-D' `--debuglog' Display the debugging log file. `-S' `--statslog' Display the statistics log file. `-F' `--follow' Keep displaying the log file forever, printing new lines as they are appended to the log file. `-f system' `--follow=system' Keep displaying the log file forever, displaying only log entries pertaining to the specified system. `-X type' `--debug type' `-I file' `--config file' `-v' `--version' `--help' *Note Standard Options::. Note that `uulog' specifies the debugging type using `-X' rather than the usual `-x'. The operation of `uulog' depends to some degree upon the type of log files generated by the UUCP programs. This is a compile time option. If the UUCP programs have been compiled to use HDB style log files, `uulog' changes in the following ways: * The new options `-x' and `--uuxqtlog' may be used to list the `uuxqt' log file. * It is no longer possible to omit all arguments: one of `-s', `--system', `-f', `--follow=system', `-D', `--debuglog', `-S', `--statslog', `-x', or `--uuxqtlog' must be used. * The option `--system ANY' may be used to list log file entries which do not pertain to any particular system.  File: uucp.info, Node: Invoking uuto, Next: Invoking uupick, Prev: Invoking uulog, Up: Invoking the UUCP Programs Invoking uuto ============= uuto files... system!user The `uuto' program may be used to conveniently send files to a particular user on a remote system. It will arrange for mail to be sent to the remote user when the files arrive on the remote system, and he or she may easily retrieve the files using the `uupick' program (*note Invoking uupick::). Note that `uuto' does not provide any security--any user on the remote system can examine the files. The last argument specifies the system and user name to which to send the files. The other arguments are the files or directories to be sent. The `uuto' program is actually just a trivial shell script which invokes the `uucp' program with the appropriate arguments. Any option which may be given to `uucp' may also be given to `uuto'. *Note Invoking uucp::.  File: uucp.info, Node: Invoking uupick, Next: Invoking cu, Prev: Invoking uuto, Up: Invoking the UUCP Programs Invoking uupick =============== uupick [-s system] [--system system] The `uupick' program is used to conveniently retrieve files transferred by the `uuto' program. For each file transferred by `uuto', `uupick' will display the source system, the file name, and whether the name refers to a regular file or a directory. It will then wait for the user to specify an action to take. One of the following commands must be entered: `q' Quit out of `uupick'. `RETURN' Skip the file. `m [directory]' Move the file or directory to the specified directory. If no directory is specified, the file is moved to the current directory. `a [directory]' Move all files from this system to the specified directory. If no directory is specified, the files are moved to the current directory. `p' List the file on standard output. `d' Delete the file. `! [command]' Execute `command' as a shell escape. The `-s' or `--system' option may be used to restrict `uupick' to only present files transferred from a particular system. The `uupick' program also supports the standard UUCP program options; see *Note Standard Options::. uucp-1.07/uucp.info-20000664000076400007640000014234407665322530010120 This is uucp.info, produced by makeinfo version 4.1 from uucp.texi. START-INFO-DIR-ENTRY * UUCP: (uucp). Transfer mail and news across phone lines. END-INFO-DIR-ENTRY This file documents Taylor UUCP, version 1.07. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "Copying" are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "Copying" may be included in a translation approved by the author instead of in the original English.  File: uucp.info, Node: Invoking cu, Next: Invoking uucico, Prev: Invoking uupick, Up: Invoking the UUCP Programs Invoking cu =========== * Menu: * cu Description:: Description of cu * cu Commands:: Commands Supported by cu * cu Variables:: Variables Supported by cu * cu Options:: Options Supported by cu  File: uucp.info, Node: cu Description, Next: cu Commands, Prev: Invoking cu, Up: Invoking cu cu Description -------------- cu [options] [system | phone | "dir"] The `cu' program is used to call up another system and act as a dial in terminal. It can also do simple file transfers with no error checking. The `cu' program takes a single non-option argument. If the argument is the string `dir' cu will make a direct connection to the port. This may only be used by users with write access to the port, as it permits reprogramming the modem. Otherwise, if the argument begins with a digit, it is taken to be a phone number to call. Otherwise, it is taken to be the name of a system to call. The `-z' or `--system' options may be used to name a system beginning with a digit, and the `-c' or `--phone' options may be used to name a phone number that does not begin with a digit. The `cu' program locates a port to use in the UUCP configuration files. If a simple system name is given, it will select a port appropriate for that system. The `-p', `--port', `-l', `--line', `-s', and `--speed' options may be used to control the port selection. When a connection is made to the remote system, `cu' forks into two processes. One reads from the port and writes to the terminal, while the other reads from the terminal and writes to the port.  File: uucp.info, Node: cu Commands, Next: cu Variables, Prev: cu Description, Up: Invoking cu cu Commands ----------- The `cu' program provides several commands that may be used during the conversation. The commands all begin with an escape character, which by default is `~' (tilde). The escape character is only recognized at the beginning of a line. To send an escape character to the remote system at the start of a line, it must be entered twice. All commands are either a single character or a word beginning with `%' (percent sign). The `cu' program recognizes the following commands. `~.' Terminate the conversation. `~! command' Run command in a shell. If command is empty, starts up a shell. `~$ command' Run command, sending the standard output to the remote system. `~| command' Run command, taking the standard input from the remote system. `~+ command' Run command, taking the standard input from the remote system and sending the standard output to the remote system. `~#, ~%break' Send a break signal, if possible. `~c directory, ~%cd directory' Change the local directory. `~> file' Send a file to the remote system. This just dumps the file over the communication line. It is assumed that the remote system is expecting it. `~<' Receive a file from the remote system. This prompts for the local file name and for the remote command to execute to begin the file transfer. It continues accepting data until the contents of the `eofread' variable are seen. `~p from to' `~%put from to' Send a file to a remote Unix system. This runs the appropriate commands on the remote system. `~t from to' `~%take from to' Retrieve a file from a remote Unix system. This runs the appropriate commands on the remote system. `~s variable value' Set a `cu' variable to the given value. If value is not given, the variable is set to `true'. `~! variable' Set a `cu' variable to `false'. `~z' Suspend the cu session. This is only supported on some systems. On systems for which `^Z' may be used to suspend a job, `~^Z' will also suspend the session. `~%nostop' Turn off XON/XOFF handling. `~%stop' Turn on XON/XOFF handling. `~v' List all the variables and their values. `~?' List all commands.  File: uucp.info, Node: cu Variables, Next: cu Options, Prev: cu Commands, Up: Invoking cu cu Variables ------------ The `cu' program also supports several variables. They may be listed with the `~v' command, and set with the `~s' or `~!' commands. `escape' The escape character. The default is `~' (tilde). `delay' If this variable is true, `cu' will delay for a second, after recognizing the escape character, before printing the name of the local system. The default is true. `eol' The list of characters which are considered to finish a line. The escape character is only recognized after one of these is seen. The default is `carriage return', `^U', `^C', `^O', `^D', `^S', `^Q', `^R'. `binary' Whether to transfer binary data when sending a file. If this is false, then newlines in the file being sent are converted to carriage returns. The default is false. `binary-prefix' A string used before sending a binary character in a file transfer, if the `binary' variable is true. The default is `^V'. `echo-check' Whether to check file transfers by examining what the remote system echoes back. This probably doesn't work very well. The default is false. `echonl' The character to look for after sending each line in a file. The default is carriage return. `timeout' The timeout to use, in seconds, when looking for a character, either when doing echo checking or when looking for the `echonl' character. The default is 30. `kill' The character to use delete a line if the echo check fails. The default is `^U'. `resend' The number of times to resend a line if the echo check continues to fail. The default is 10. `eofwrite' The string to write after sending a file with the `~>' command. The default is `^D'. `eofread' The string to look for when receiving a file with the ` ~<' command. The default is `$', which is intended to be a typical shell prompt. `verbose' Whether to print accumulated information during a file transfer. The default is true.  File: uucp.info, Node: cu Options, Prev: cu Variables, Up: Invoking cu cu Options ---------- The following options may be given to `cu'. `-e' `--parity=even' Use even parity. `-o' `--parity=odd' Use odd parity. `--parity=none' Use no parity. No parity is also used if both `-e' and `-o' are given. `-h' `--halfduplex' Echo characters locally (half-duplex mode). `--nostop' Turn off XON/XOFF handling (it is on by default). `-E char' `--escape char' Set the escape character. Initially `~' (tilde). To eliminate the escape character, use `-E '''. `-z system' `--system system' The system to call. `-c phone-number' `--phone phone-number' The phone number to call. `-p port' `-a port' `--port port' Name the port to use. `-l line' `--line line' Name the line to use by giving a device name. This may be used to dial out on ports that are not listed in the UUCP configuration files. Write access to the device is required. `-s speed' `-#' `--speed speed' The speed (baud rate) to use. Here, `-#' means an actual number; e.g., `-9600'. `-n' `--prompt' Prompt for the phone number to use. `-d' Enter debugging mode. Equivalent to `--debug all'. `-x type' `--debug type' `-I file' `--config file' `-v' `--version' `--help' *Note Standard Options::.  File: uucp.info, Node: Invoking uucico, Next: Invoking uuxqt, Prev: Invoking cu, Up: Invoking the UUCP Programs Invoking uucico =============== * Menu: * uucico Description:: Description of uucico * uucico Options:: Options Supported by uucico  File: uucp.info, Node: uucico Description, Next: uucico Options, Prev: Invoking uucico, Up: Invoking uucico uucico Description ------------------ uucico [options] The `uucico' daemon processes file transfer requests queued by `uucp' and `uux'. It is started when `uucp' or `uux' is run (unless they are given the `-r' or `--nouucico' options). It is also typically started periodically using entries in the `crontab' table(s). When `uucico' is invoked with `-r1', `--master', `-s', `--system', or `-S', the daemon will place a call to a remote system, running in master mode. Otherwise the daemon will start in slave mode, accepting a call from a remote system. Typically a special login name will be set up for UUCP which automatically invokes `uucico' when a remote system calls in and logs in under that name. When `uucico' terminates, it invokes the `uuxqt' daemon, unless the `-q' or `--nouuxqt' options were given; `uuxqt' executes any work orders created by `uux' on a remote system, and any work orders created locally which have received remote files for which they were waiting. If a call fails, `uucico' will normally refuse to retry the call until a certain (configurable) amount of time has passed. This may be overriden by the `-f', `--force', or `-S' options. The `-l', `--prompt', `-e', or `--loop' options may be used to force `uucico' to produce its own prompts of `login: ' and `Password:'. When another `uucico' daemon calls in, it will see these prompts and log in as usual. The login name and password will normally be checked against a separate list kept specially for `uucico', rather than the `/etc/passwd' file (*note Configuration File Names::). It is possible, on some systems, to configure `uucico' to use `/etc/passwd'. The `-l' or `--prompt' options will prompt once and then exit; in this mode the UUCP administrator, or the superuser, may use the `-u' or `--login' option to force a login name, in which case `uucico' will not prompt for one. The `-e' or `--loop' options will prompt again after the first session is over; in this mode `uucico' will permanently control a port. If `uucico' receives a `SIGQUIT', `SIGTERM' or `SIGPIPE' signal, it will cleanly abort any current conversation with a remote system and exit. If it receives a `SIGHUP' signal it will abort any current conversation, but will continue to place calls to (if invoked with `-r1' or `--master') and accept calls from (if invoked with `-e' or `--loop') other systems. If it receives a `SIGINT' signal it will finish the current conversation, but will not place or accept any more calls.  File: uucp.info, Node: uucico Options, Prev: uucico Description, Up: Invoking uucico uucico Options -------------- The following options may be given to `uucico'. `-r1' `--master' Start in master mode: call out to a remote system. Implied by `-s', `--system', or `-S'. If no system is specified, sequentially call every system for which work is waiting to be done. `-r0' `--slave' Start in slave mode. This is the default. `-s system' `--system system' Call the specified system. `-S system' Call the specified system, ignoring any required wait. This is equivalent to `-s system -f'. `-f' `--force' Ignore any required wait for any systems to be called. `-l' `--prompt' Prompt for login name and password using `login: ' and `Password:'. This allows `uucico' to be easily run from `inetd'. The login name and password are checked against the UUCP password file, which need not be `/etc/passwd'. The `--login' option may be used to force a login name, in which cause `uucico' will only prompt for a password. `-p port' `--port port' Specify a port to call out on or to listen to. `-e' `--loop' Enter an endless loop of login/password prompts and slave mode daemon execution. The program will not stop by itself; you must use `kill' to shut it down. `-w' `--wait' After calling out (to a particular system when `-s', `--system', or `-S' is specifed, or to all systems which have work when just `-r1' or `--master' is specifed), begin an endless loop as with `--loop'. `-q' `--nouuxqt' Do not start the `uuxqt' daemon when finished. `-c' `--quiet' If no calls are permitted at this time, then don't make the call, but also do not put an error message in the log file and do not update the system status (as reported by `uustat'). This can be convenient for automated polling scripts, which may want to simply attempt to call every system rather than worry about which particular systems may be called at the moment. This option also suppresses the log message indicating that there is no work to be done. `-C' `--ifwork' Only call the system named by `-s', `--system', or `-S' if there is work for that system. `-D' `--nodetach' Do not detach from the controlling terminal. Normally `uucico' detaches from the terminal before each call out to another system and before invoking `uuxqt'. This option prevents this. `-u name' `--login name' Set the login name to use instead of that of the invoking user. This option may only be used by the UUCP administrator or the superuser. If used with `--prompt', this will cause `uucico' to prompt only for the password, not the login name. `-z' `--try-next' If a call fails after the remote system is reached, try the next alternate rather than simply exiting. `-i type' `--stdin type' Set the type of port to use when using standard input. The only supported port type is TLI, and this is only available on machines which support the TLI networking interface. Specifying `-i TLI' causes `uucico' to use TLI calls to perform I/O. `-X type' Same as the standard option `-x type'. Provided for historical compatibility. `-x type' `--debug type' `-I file' `--config file' `-v' `--version' `--help' *Note Standard Options::.  File: uucp.info, Node: Invoking uuxqt, Next: Invoking uuchk, Prev: Invoking uucico, Up: Invoking the UUCP Programs Invoking uuxqt ============== uuxqt [-c command] [-s system] [--command command] [--system system] The `uuxqt' daemon executes commands requested by `uux' from either the local system or from remote systems. It is started automatically by the `uucico' daemon (unless `uucico' is given the `-q' or `--nouuxqt' options). There is normally no need to run `uuxqt', since it will be invoked by `uucico'. However, `uuxqt' can be invoked directly to provide greater control over the processing of the work queue. Multiple invocations of `uuxqt' may be run at once, as controlled by the `max-uuxqts' configuration command; see *Note Miscellaneous (config)::. The following options may be given to `uuxqt'. `-c command' `--command command' Only execute requests for the specified command. For example, `uuxqt --command rmail'. `-s system' `--system system' Only execute requests originating from the specified system. `-x type' `--debug type' `-I file' `--config' `-v' `--version' `--help' *Note Standard Options::.  File: uucp.info, Node: Invoking uuchk, Next: Invoking uuconv, Prev: Invoking uuxqt, Up: Invoking the UUCP Programs Invoking uuchk ============== uuchk [-s system] [--system system] The `uuchk' program displays information read from the UUCP configuration files. It should be used to ensure that UUCP has been configured correctly. The `-s' or `--system' options may be used to display the configuration for just the specified system, rather than for all systems. The `uuchk' program also supports the standard UUCP program options; see *Note Standard Options::.  File: uucp.info, Node: Invoking uuconv, Next: Invoking uusched, Prev: Invoking uuchk, Up: Invoking the UUCP Programs Invoking uuconv =============== uuconv -i type -o type [-p program] [--program program] uuconv --input type --output type [-p program] [--program program] The `uuconv' program converts UUCP configuration files from one format to another. The type of configuration file to read is specified using the `-i' or `--input' options. The type of configuration file to write is specified using the `-o' or `--output' options. The supported configuration file types are `taylor', `v2', and `hdb'. For a description of the `taylor' configuration files, see *Note Configuration Files::. The other types of configuration files are used by traditional UUCP packages, and are not described in this manual. An input configuration of type `v2' or `hdb' is read from a compiled in directory (specified by `oldconfigdir' in `Makefile'). An input configuration of type `taylor' is read from a compiled in directory by default, but may be overridden with the standard `-I' or `--config' options (*note Standard Options::). The output configuration is written to files in the directory in which `uuconv' is run. Some information in the input files may not be representable in the desired output format, in which case `uuconv' will silently discard it. The output of `uuconv' should be carefully checked before it is used. The `uuchk' program may be used for this purpose; see *Note Invoking uuchk::. The `-p' or `--program' option may be used to convert specific `cu' configuration information, rather than the default of only converting the `uucp' configuration information; see *Note config File::. The `uuchk' program also supports the standard UUCP program options; see *Note Standard Options::.  File: uucp.info, Node: Invoking uusched, Prev: Invoking uuconv, Up: Invoking the UUCP Programs Invoking uusched ================ The `uusched' program is actually just a shell script which invokes the `uucico' daemon. It is provided for backward compatibility. It causes `uucico' to call all systems for which there is work. Any option which may be given to `uucico' may also be given to `uusched'. *Note Invoking uucico::.  File: uucp.info, Node: Installing Taylor UUCP, Next: Using Taylor UUCP, Prev: Invoking the UUCP Programs, Up: Top Installing Taylor UUCP ********************** These are the installation instructions for the Taylor UUCP package. * Menu: * Compilation:: Compiling Taylor UUCP * Testing the Compilation:: Testing the Compilation * Installing the Binaries:: Installing the Binaries * Configuration:: Configuring Taylor UUCP * Testing the Installation:: Testing the Installation  File: uucp.info, Node: Compilation, Next: Testing the Compilation, Prev: Installing Taylor UUCP, Up: Installing Taylor UUCP Compiling Taylor UUCP ===================== If you have a source code distribution, you must first compile it for your system. Free versions of Unix, such as Linux, NetBSD, or FreeBSD, often come with pre-compiled binary distributions of UUCP. If you are using a binary distribution, you may skip to the configuration section (*note Configuration::). Follow these steps to compile the source code. 1. Take a look at the top of `Makefile.in' and set the appropriate values for your system. These control where the programs are installed and which user on the system owns them (normally they will be owned by a special user `uucp' rather than a real person; they should probably not be owned by `root'). 2. Run the shell script `configure'. This script was generated using the `autoconf' program written by David MacKenzie of the Free Software Foundation. It takes a while to run. It will generate the file `config.h' based on `config.h.in', and, for each source code directory, will generate `Makefile' based on `Makefile.in'. You can pass certain arguments to `configure' in the environment. Because `configure' will compile little test programs to see what is available on your system, you must tell it how to run your compiler. It recognizes the following environment variables: `CC' The C compiler. If this is not set, then if `configure' can find `gcc' it will use it, otherwise it will use `cc'. `CFLAGS' Flags to pass to the C compiler when compiling the actual code. If this is not set, `configure' will use `-g'. `LDFLAGS' Flags to pass to the C compiler when only linking, not compiling. If this is not set, `configure' will use the empty string. `LIBS' Libraries to pass to the C compiler. If this is not set, `configure' will use the empty string. `INSTALL' The program to run to install UUCP in the binary directory. If this is not set, then if `configure' finds the BSD `install' program, it will set this to `install -c'; otherwise, it will use `cp'. Suppose, for example, you want to set the environment variable `CC' to `rcc'. If you are using `sh', `bash', or `ksh', invoke `configure' as `CC=rcc configure'. If you are using `csh', do `setenv CC rcc; sh configure'. On some systems you will want to use `LIBS=-lmalloc'. On Xenix derived versions of Unix do not use `LIBS=-lx' because this will bring in the wrong versions of certain routines; if you want to use `-lx' you must specify `LIBS=-lc -lx'. You can also pass other arguments to `configure' on the command line. Use `configure --help' for a complete list. Of particular interest: `--prefix=DIRNAME' The directory under which all files are installed. Default `/usr/local'. `--with-newconfigdir=DIRNAME' The directory in which to find new style configuration files. Default `PREFIX/conf/uucp'. `--with-oldconfigdir=DIRNAME' The directory in which to find old style configuration files. Default `/usr/lib/uucp'. If `configure' fails for some reason, or if you have a very weird system, you may have to configure the package by hand. To do this, copy the file `config.h.in' to `config.h' and edit it for your system. Then for each source directory (the top directory, and the subdirectories `lib', `unix', and `uuconf') copy `Makefile.in' to `Makefile', find the words within `@' characters, and set them correctly for your system. 3. Igor V. Semenyuk provided this (lightly edited) note about ISC Unix 3.0. The `configure' script will default to passing `-posix' to `gcc'. However, using `-posix' changes the environment to POSIX, and on ISC 3.0, at least, the default for `POSIX_NO_TRUNC' is 1. This can lead to a problem when `uuxqt' executes `rmail'. `IDA sendmail' has dbm configuration files named `mailertable.{dir,pag}'. Notice these names are 15 characters long. When `uuxqt' compiled with the `-posix' executes `rmail', which in turn executes `sendmail', the later is run under the POSIX environment too. This leads to `sendmail' bombing out with `'error opening 'M' database: name too long' (mailertable.dir)'. It's rather obscure behaviour, and it took me a day to find out the cause. I don't use the `-posix' switch; instead, I run `gcc' with `-D_POSIX_SOURCE', and add `-lcposix' to `LIBS'. 4. On some versions of BSDI there is a bug in the shell which causes the default value for `CFLAGS' to be set incorrectly. If `echo ${CFLAGS--g}' echoes `g' rather than `-g', then you must set `CFLAGS' in the environment before running configure. There is a patch available from BSDI for this bug. (Reported by David Vrona). 5. On AIX 3.2.5, and possibly other versions, `cc -E' does not work, reporting `Option NOROCONST is not valid'. Test this before running configure by doing something like `touch /tmp/foo.c; cc -E /tmp/foo.c'. This may give a warning about the file being empty, but it should not give the `Option NOROCONST' warning. The workaround is to remove the `,noroconst' entry from the `options' clause in the `cc' stanza in `/etc/xlc.cfg'. (Reported by Chris Lewis). 6. You should verify that `configure' worked correctly by checking `config.h' and the instances of `Makefile'. 7. Edit `policy.h' for your local system. The comments explain the various choices. The default values are intended to be reasonable, so you may not have to make any changes. You must decide what type of configuration files to use; for more information on the choices, see *Note Configuration::. You must also decide what sort of spool directory you want to use. If this is a new installation, I recommend `SPOOLDIR_TAYLOR'; otherwise, select the spool directory corresponding to your existing UUCP package. 8. Type `make' to compile everything. The `tstuu.c' file is not particularly portable; if you can't figure out how to compile it you can safely ignore it, as it is only used for testing. To use STREAMS pseudo-terminals, tstuu.c must be compiled with `-DHAVE_STREAMS_PTYS'; this is not determined by the configure script. If you have any other problems there is probably a bug in the `configure' script. 9. Please report any problems you have. That is the only way they will get fixed for other people. Supply a patch if you can (*note Patches::), or just ask for help.  File: uucp.info, Node: Testing the Compilation, Next: Installing the Binaries, Prev: Compilation, Up: Installing Taylor UUCP Testing the Compilation ======================= If your system supports pseudo-terminals, and you compiled the code to support the new style of configuration files (`HAVE_TAYLOR_CONFIG' was set to 1 in `policy.h'), you should be able to use the `tstuu' program to test the `uucico' daemon. If your system supports STREAMS based pseudo-terminals, you must compile tstuu.c with `-DHAVE_STREAMS_PTYS'. (The STREAMS based code was contributed by Marc Boucher). To run `tstuu', just type `tstuu' with no arguments. You must run it in the compilation directory, since it runs `./uucp', `./uux' and `./uucico'. The `tstuu' program will run a lengthy series of tests (it takes over ten minutes on a slow VAX). You will need a fair amount of space available in `/usr/tmp'. You will probably want to put it in the background. Do not use `^Z', because the program traps on `SIGCHLD' and winds up dying. The `tstuu' program will create a directory `/usr/tmp/tstuu' and fill it with configuration files, and create spool directories `/usr/tmp/tstuu/spool1' and `/usr/tmp/tstuu/spool2'. If your system does not support the `FIONREAD' call, the `tstuu' program will run very slowly. This may or may not get fixed in a later version. The `tstuu' program will finish with an execute file named `X.SOMETHING' and a data file named `D.SOMETHING' in the directory `/usr/tmp/tstuu/spool1' (or, more likely, in subdirectories, depending on the choice of `SPOOLDIR' in `policy.h'). Two log files will be created in the directory `/usr/tmp/tstuu'. They will be named `Log1' and `Log2', or, if you have selected `HAVE_HDB_LOGGING' in `policy.h', `Log1/uucico/test2' and `Log2/uucico/test1'. There should be no errors in the log files. You can test `uuxqt' with `./uuxqt -I /usr/tmp/tstuu/Config1'. This should leave a command file `C.SOMETHING' and a data file `D.SOMETHING' in `/usr/tmp/tstuu/spool1' or in subdirectories. Again, there should be no errors in the log file. Assuming you compiled the code with debugging enabled, the `-x' switch can be used to set debugging modes; see the `debug' command for details (*note Debugging Levels::). Use `-x all' to turn on all debugging and generate far more output than you will ever want to see. The `uucico' daemons will put debugging output in the files `Debug1' and `Debug2' in the directory `/usr/tmp/tstuu'. After that, you're pretty much on your own. On some systems you can also use `tstuu' to test `uucico' against the system `uucico', by using the `-u' switch. For this to work, change the definitions of `ZUUCICO_CMD' and `UUCICO_EXECL' at the top of `tstuu.c' to something appropriate for your system. The definitions in `tstuu.c' are what I used for Ultrix 4.0, on which `/usr/lib/uucp/uucico' is particularly obstinate about being run as a child; I was only able to run it by creating a login name with no password whose shell was `/usr/lib/uucp/uucico'. Calling login in this way will leave fake entries in `wtmp' and `utmp'; if you compile `tstout.c' (in the `contrib' directory) as a setuid `root' program, `tstuu' will run it to clear those entries out. On most systems, such hackery should not be necessary, although on SCO I had to su to `root' (`uucp' might also have worked) before I could run `/usr/lib/uucp/uucico'. You can test `uucp' and `uux' (give them the `-r' switch to keep them from starting `uucico') to make sure they create the right sorts of files. Unfortunately, if you don't know what the right sorts of files are, I'm not going to tell you here. If you can not run `tstuu', or if it fails inexplicably, don't worry about it too much. On some systems `tstuu' will fail because of problems using pseudo terminals, which will not matter in normal use. The real test of the package is talking to another system.  File: uucp.info, Node: Installing the Binaries, Next: Configuration, Prev: Testing the Compilation, Up: Installing Taylor UUCP Installing the Binaries ======================= You can install the executable files by becoming `root' and typing `make install'. Or you can look at what `make install' does and do it by hand. It tries to preserve your old programs, if any, but it only does this the first time Taylor UUCP is installed (so that if you install several versions of Taylor UUCP, you can still go back to your original UUCP programs). You can retrieve the original programs by typing `make uninstall'. Note that by default the programs are compiled with debugging information, and they are not stripped when they are installed. You may want to strip the installed programs to save disk space. For more information, see your system documentation for the `strip' program. Of course, simply installing the executable files is not enough. You must also arrange for them to be used correctly.  File: uucp.info, Node: Configuration, Next: Testing the Installation, Prev: Installing the Binaries, Up: Installing Taylor UUCP Configuring Taylor UUCP ======================= You will have to decide what types of configuration files you want to use. This package supports a new sort of configuration file; see *Note Configuration Files::. It also supports V2 configuration files (`L.sys', `L-devices', etc.) and HDB configuration files (`Systems', `Devices', etc.). No documentation is provided for V2 or HDB configuration files. All types of configuration files can be used at once, if you are so inclined. Currently using just V2 configuration files is not really possible, because there is no way to specify a dialer (there are no built in dialers, and the program does not know how to read `acucap' or `modemcap'); however, V2 configuration files can be used with a new style dial file (*note dial File::), or with a HDB `Dialers' file. Use of HDB configuration files has two known bugs. A blank line in the middle of an entry in the `Permissions' file will not be ignored as it should be. Dialer programs, as found in some versions of HDB, are not recognized directly. If you must use a dialer program, rather than an entry in `Devices', you must use the `chat-program' command in a new style dial file; see *Note dial File::. You will have to invoke the dialer program via a shell script or another program, since an exit code of 0 is required to recognize success; the `dialHDB' program in the `contrib' directory may be used for this purpose. The `uuconv' (*note Invoking uuconv::) program can be used to convert from V2 or HDB configuration files to the new style (it can also do the reverse translation, if you are so inclined). It will not do all of the work, and the results should be carefully checked, but it can be quite useful. If you are installing a new system, you will, of course, have to write the configuration files; see *Note Configuration Files:: for details on how to do this. After writing the configuration files, use the `uuchk' program to verify that they are what you expect; see *Note Invoking uuchk::.  File: uucp.info, Node: Testing the Installation, Prev: Configuration, Up: Installing Taylor UUCP Testing the Installation ======================== After you have written the configuration files, and verified them with the `uuchk' program (*note Invoking uuchk::), you must check that UUCP can correctly contact another system. Tell `uucico' to dial out to the system by using the `-s' system switch (e.g., `uucico -s uunet'). The log file should tell you what happens. The exact location of the log file depends upon the settings in `policy.h' when you compiled the program, and on the use of the `logfile' command in the `config' file. Typical locations are `/usr/spool/uucp/Log' or a subdirectory under `/usr/spool/uucp/.Log'. If you compiled the code with debugging enabled, you can use debugging mode to get a great deal of information about what sort of data is flowing back and forth; the various possibilities are described with the `debug' command (*note Debugging Levels::). When initially setting up a connection `-x chat' is probably the most useful (e.g., `uucico -s uunet -x chat'); you may also want to use `-x handshake,incoming,outgoing'. You can use `-x' multiple times on one command line, or you can give it comma separated arguments as in the last example. Use `-x all' to turn on all possible debugging information. The debugging information is written to a file, normally `/usr/spool/uucp/Debug', although the default can be changed in `policy.h', and the `config' file can override the default with the `debugfile' command. The debugging file may contain passwords and some file contents as they are transmitted over the line, so the debugging file is only readable by the `uucp' user. You can use the `-f' switch to force `uucico' to call out even if the last call failed recently; using `-S' when naming a system has the same effect. Otherwise the status file (in the `.Status' subdirectory of the main spool directory, normally `/usr/spool/uucp') (*note Status Directory::) will prevent too many attempts from occurring in rapid succession. On older System V based systems which do not have the `setreuid' system call, problems may arise if ordinary users can start an execution of `uuxqt', perhaps indirectly via `uucp' or `uux'. UUCP jobs may wind up executing with a real user ID of the user who invoked `uuxqt', which can cause problems if the UUCP job checks the real user ID for security purposes. On such systems, it is safest to put `run-uuxqt never' (*note Miscellaneous (config)::) in the `config' file, so that `uucico' never starts `uuxqt', and invoke `uuxqt' directly from a `crontab' file. Please let me know about any problems you have and how you got around them. If you do report a problem, please include the version number of the package you are using, the operating system you are running it on, and a sample of the debugging file showing the problem (debugging information is usually what is needed, not just the log file). General questions such as "why doesn't `uucico' dial out" are impossible to answer without much more information.  File: uucp.info, Node: Using Taylor UUCP, Next: Configuration Files, Prev: Installing Taylor UUCP, Up: Top Using Taylor UUCP ***************** * Menu: * Calling Other Systems:: Calling Other Systems * Accepting Calls:: Accepting Calls * Mail and News:: Using UUCP for Mail and News * The Spool Directory Layout:: The Spool Directory Layout * Spool Directory Cleaning:: Cleaning the UUCP Spool Directory  File: uucp.info, Node: Calling Other Systems, Next: Accepting Calls, Prev: Using Taylor UUCP, Up: Using Taylor UUCP Calling Other Systems ===================== By default `uucp' and `uux' will automatically start up `uucico' to call another system whenever work is queued up. However, the call may fail, or you may have put in time restrictions which prevent the call at that time (perhaps because telephone rates are high) (*note When to Call::). Also, a remote system may have work queued up for your system, but may not be calling you for some reason (perhaps you have agreed that your system should always place the call). To make sure that work gets transferred between the systems withing a reasonable time period, you should arrange to periodically invoke `uucico'. These periodic invocations are normally triggered by entries in the `crontab' file. The exact format of `crontab' files, and how new entries are added, varies from system to system; check your local documentation (try `man cron'). To attempt to call all systems with outstanding work, use the command `uucico -r1'. To attempt to call a particular system, use the command `uucico -s SYSTEM'. To attempt to call a particular system, but only if there is work for it, use the command `uucico -C -s SYSTEM'. (*note Invoking uucico::). A common case is to want to try to call a system at a certain time, with periodic retries if the call fails. A simple way to do this is to create an empty UUCP command file, known as a "poll file". If a poll file exists for a system, then `uucico -r1' will place a call to it. If the call succeeds, the poll file will be deleted. A poll file can be easily created using the `uux' command, by requesting the execution of an empty command. To create a poll file for SYSTEM, just do something like this: uux -r SYSTEM! The `-r' tells `uux' to not start up `uucico' immediately. Of course, if you do want `uucico' to start up right away, omit the `-r'; if the call fails, the poll file will be left around to cause a later call. For example, I use the following crontab entries locally: 45 * * * * /bin/echo /usr/lib/uucp/uucico -r1 | /bin/su uucpa 40 4,10,15 * * * /usr/bin/uux -r uunet! Every hour, at 45 minutes past, this will check if there is any work to be done, and, if there is, will call the appropriate system. Also, at 4:40am, 10:40am, and 3:40pm, this will create a poll file file for `uunet', forcing the next run of `uucico' to call `uunet'.  File: uucp.info, Node: Accepting Calls, Next: Mail and News, Prev: Calling Other Systems, Up: Using Taylor UUCP Accepting Calls =============== To accept calls from another system, you must arrange matters such that when that system calls in, it automatically invokes `uucico' on your system. The most common arrangement is to create a special user name and password for incoming UUCP calls. This user name typically uses the same user ID as the regular `uucp' user (Unix permits several user names to share the same user ID). The shell for this user name should be set to `uucico'. Here is a sample `/etc/passwd' line to accept calls from a remote system named airs: Uairs:PASSWORD:4:8:airs UUCP:/usr/spool/uucp:/usr/lib/uucp/uucico The details may vary on your system. You must use reasonable user and group ID's. You must use the correct file name for `uucico'. The PASSWORD must appear in the UUCP configuration files on the remote system, but will otherwise never be seen or typed by a human. Note that `uucico' appears as the login shell, and that it will be run with no arguments. This means that it will start in slave mode and accept an incoming connection. *Note Invoking uucico::. On some systems, creating an empty file named `.hushlogin' in the home directory will skip the printing of various bits of information when the remote `uucico' logs in, speeding up the UUCP connection process. For the greatest security, each system which calls in should use a different user name, each with a different password, and the `called-login' command should be used in the `sys' file to ensure that the correct login name is used. *Note Accepting a Call::, and see *Note Security::. If you never need to dial out from your system, but only accept incoming calls, you can arrange for `uucico' to handle logins itself, completely controlling the port, by using the `--endless' option. *Note Invoking uucico::.  File: uucp.info, Node: Mail and News, Next: The Spool Directory Layout, Prev: Accepting Calls, Up: Using Taylor UUCP Using UUCP for Mail and News. ============================= Taylor UUCP does not include a mail package. All Unix systems come with some sort of mail delivery agent, typically `sendmail' or `MMDF'. Source code is available for some alternative mail delivery agents, such as `IDA sendmail' and `smail'. Taylor UUCP also does not include a news package. The two major Unix news packages are `C-news' and `INN'. Both are available in source code form. Configuring and using mail delivery agents is a notoriously complex topic, and I will not be discussing it here. Configuring news systems is usually simpler, but I will not be discussing that either. I will merely describe the interactions between the mail and news systems and UUCP. A mail or news system interacts with UUCP in two ways: sending and receiving. * Menu: * Sending mail or news:: Sending mail or news via UUCP * Receiving mail or news:: Receiving mail or news via UUCP  File: uucp.info, Node: Sending mail or news, Next: Receiving mail or news, Prev: Mail and News, Up: Mail and News Sending mail or news via UUCP ----------------------------- When mail is to be sent from your machine to another machine via UUCP, the mail delivery agent will invoke `uux'. It will generally run a command such as `uux - SYSTEM!rmail ADDRESS', where SYSTEM is the remote system to which the mail is being sent. It may pass other options to `uux', such as `-r' or `-g' (*note Invoking uux::). The news system also invokes `uux' in order to transfer articles to another system. The only difference is that news will use `uux' to invoke `rnews' on the remote system, rather than `rmail'. You should arrange for your mail and news systems to invoke the Taylor UUCP version of `uux'. If you only have Taylor UUCP, or if you simply replace any existing version of `uux' with the Taylor UUCP version, this will probably happen automatically. However, if you have two UUCP packages installed on your system, you will probably have to modify the mail and news configuration files in some way. Actually, if both the system UUCP and Taylor UUCP are using the same spool directory format, the system `uux' will probably work fine with the Taylor `uucico' (the reverse is not the case: the Taylor `uux' requires the Taylor `uucico'). However, data transfer will be somewhat more efficient if the Taylor `uux' is used.  File: uucp.info, Node: Receiving mail or news, Prev: Sending mail or news, Up: Mail and News Receiving mail or news via UUCP ------------------------------- To receive mail, all that is necessary is for UUCP to invoke `rmail'. Any mail delivery agent will provide an appropriate version of `rmail'; you must simply make sure that it is in the command path used by UUCP (it almost certainly already is). The default command path is set in `policy.h', and it may be overridden for a particular system by the `command-path' command (*note Miscellaneous (sys)::). Similarly, for news UUCP must be able to invoke `rnews'. Any news system will provide a version of `rnews', and you must ensure that is in a directory on the path that UUCP will search.  File: uucp.info, Node: The Spool Directory Layout, Next: Spool Directory Cleaning, Prev: Mail and News, Up: Using Taylor UUCP The Spool Directory Layout ========================== In general, the layout of the spool directory may be safely ignored. However, it is documented here for the curious. This description only covers the `SPOOLDIR_TAYLOR' layout. The ways in which the other spool directory layouts differ are described in the source file `unix/spool.c'. Directories and files are only created when they are needed, so a typical system will not have all of the entries described here. * Menu: * System Spool Directories:: System Spool Directories * Status Directory:: Status Spool Directory * Execution Subdirectories:: Execution Spool Subdirectories * Other Spool Subdirectories:: Other Spool Subdirectories * Spool Lock Files:: Spool Directory Lock Files  File: uucp.info, Node: System Spool Directories, Next: Status Directory, Prev: The Spool Directory Layout, Up: The Spool Directory Layout System Spool Directories ------------------------ `SYSTEM' There is a subdirectory of the main spool directory for each remote system. `SYSTEM/C.' This directory stores files describing file transfer commands to be sent to the SYSTEM. Each file name starts with `C.G', where G is the job grade. Each file contains one or more commands. For details of the commands, see *Note UUCP Protocol Commands::. `SYSTEM/D.' This directory stores data files. Files with names like `D.GSSSS', where G is the grade and SSSS is a sequence number, are waiting to be transferred to the SYSTEM, as directed by the files in the `SYSTEM/C.' directory. Files with other names, typically `D.SYSTEMGSSSS', have been received from SYSTEM and are waiting to be processed by an execution file in the `SYSTEM/X.' directory. `SYSTEM/D.X' This directory stores data files which will become execution files on the remote system. In current practice, this directory rarely exists, because most simple executions, including typical uses of `rmail' and `rnews', send an `E' command rather than an execution file (*note The E Command::). `SYSTEM/X.' This directory stores execution files which have been received from SYSTEM. This directory normally exists, even though the corresponding `D.X' directory does not, because `uucico' will create an execution file on the fly when it receives an `E' command. `SYSTEM/SEQF' This file holds the sequence number of the last job sent to SYSTEM. The sequence number is used to ensure that file names are unique in the remote system spool directory. The file is four bytes long. Sequence numbers are composed of digits and the upper case letters.  File: uucp.info, Node: Status Directory, Next: Execution Subdirectories, Prev: System Spool Directories, Up: The Spool Directory Layout Status Directory ---------------- `.Status' This directory holds status files for each remote system. The name of the status file is the name of the system which it describes. Each status file describes the last conversation with the system. Running `uustat --status' basically just formats and prints the contents of the status files (*note uustat Examples::). Each status file has a single text line with six fields. code A code indicating the status of the last conversation. The following values are defined, though not all are actually used. `0' Conversation completed normally. `1' `uucico' was unable to open the port. `2' The last call to the system failed while dailing. `3' The last call to the system failed while logging in. `4' The last call to the system failed during the initial UUCP protocol handshake (*note The Initial Handshake::). `5' The last call to the system failed after the initial handshake. `6' `uucico' is currently talking to the system. `7' The last call to the system failed because it was the wrong time to call (this is not used if calling the system is never permitted). retries The number of retries since the last successful call. time of last call The time of the last call, in seconds since the epoch (as returned by the `time' system call). wait If the last call failed, this is the number of seconds since the last call before `uucico' may attempt another call. This is set based on the retry time; see *Note When to Call::. The `-f' or `-S' options to `uucico' direct it to ignore this wait time; see *Note Invoking uucico::. description A text description of the status, corresponding to the code in the first field. This may contain spaces. system name The name of the remote system. uucp-1.07/uucp.info-30000664000076400007640000014162207665322530010117 This is uucp.info, produced by makeinfo version 4.1 from uucp.texi. START-INFO-DIR-ENTRY * UUCP: (uucp). Transfer mail and news across phone lines. END-INFO-DIR-ENTRY This file documents Taylor UUCP, version 1.07. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "Copying" are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "Copying" may be included in a translation approved by the author instead of in the original English.  File: uucp.info, Node: Execution Subdirectories, Next: Other Spool Subdirectories, Prev: Status Directory, Up: The Spool Directory Layout Execution Subdirectories ------------------------ `.Xqtdir' When `uuxqt' executes a job requested by `uux', it first changes the working directory to the `.Xqtdir' subdirectory. This permits the job to create any sort of temporary file without worrying about overwriting other files in the spool directory. Any files left in the `.Xqtdir' subdirectory are removed after each execution is complete. `.XqtdirNNNN' When several instances of `uuxqt' are executing simultaneously, each one executes jobs in a separate directory. The first uses `.Xqtdir', the second uses `.Xqtdir0001', the third uses `.Xqtdir0002', and so forth. `.Corrupt' If `uuxqt' encounters an execution file which it is unable to parse, it saves it in the `.Corrupt' directory, and sends mail about it to the UUCP administrator. `.Failed' If `uuxqt' executes a job, and the job fails, and there is enough disk space to hold the command file and all the data files, then `uuxqt' saves the files in the `.Failed' directory, and sends mail about it to the UUCP administrator.  File: uucp.info, Node: Other Spool Subdirectories, Next: Spool Lock Files, Prev: Execution Subdirectories, Up: The Spool Directory Layout Other Spool Subdirectories -------------------------- `.Sequence' This directory holds conversation sequence number files. These are used if the `sequence' command is used for a system (*note Miscellaneous (sys)::). The sequence number for the system SYSTEM is stored in the file `.Sequence/SYSTEM'. It is simply stored as a printable number. `.Temp' This directory holds data files as they are being received from a remote system, before they are moved to their final destination. For file send requests which use a valid temporary file name in the TEMP field of the `S' or `E' command (*note The S Command::), `uucico' receives the file into `.Temp/SYSTEM/TEMP', where SYSTEM is the name of the remote system, and TEMP is the temporary file name. If a conversation fails during a file transfer, these files are used to automatically restart the file transfer from the point of failure. If the `S' or `E' command does not include a temporary file name, automatic restart is not possible. In this case, the files are received into a randomly named file in the `.Temp' directory itself. `.Preserve' This directory holds data files which could not be transferred to a remote system for some reason (for example, the data file might be large, and exceed size restrictions imposed by the remote system). When a locally requested file transfer fails, `uucico' will store the data file in the `.Preserve' directory, and send mail to the requestor describing the failure and naming the saved file. `.Received' This directory records which files have been received. If a conversation fails just after `uucico' acknowledges receipt of a file, it is possible for the acknowledgement to be lost. If this happens, the remote system will resend the file. If the file were an execution request, and `uucico' did not keep track of which files it had already received, this could lead to the execution being performed twice. To avoid this problem, when a conversation fails, `uucico' records each file that has been received, but for which the remote system may not have received the acknowledgement. It records this information by creating an empty file with the name `.Received/SYSTEM/TEMP', where SYSTEM is the name of the remote system, and TEMP is the TEMP field of the `S' or `E' command from the remote system (*note The S Command::). Then, if the remote system offers the file again in the next conversation, `uucico' refuses the send request and deletes the record in the `.Received' directory. This approach only works for file sends which use a temporary file name, but this is true of all execution requests.  File: uucp.info, Node: Spool Lock Files, Prev: Other Spool Subdirectories, Up: The Spool Directory Layout Lock Files in the Spool Directory --------------------------------- Lock files for devices and systems are stored in the lock directory, which may or may not be the same as the spool directory. The lock directory is set at compilation time by `LOCKDIR' in `policy.h', which may be overridden by the `lockdir' command in the `config' file (*note Miscellaneous (config)::). For a description of the names used for device lock files, and the format of the contents of a lock file, see *Note UUCP Lock Files::. `LCK..SYS' A lock file for a system, where SYS is the system name. As noted above, these lock files are kept in the lock directory, which may not be the spool directory. These lock files are created by `uucico' while talking to a remote system, and are used to prevent multiple simultaneous conversations with a system. On systems which limit file names to 14 characters, only the first eight characters of the system name are used in the lock file name. This requires that the names of each directly connected remote system be unique in the first eight characters. `LCK.XQT.NN' When `uuxqt' starts up, it uses lock files to determine how many other `uuxqt' daemons are currently running. It first tries to lock `LCK.XQT.0', then `LCK.XQT.1', and so forth. This is used to implement the `max-uuxqts' command (*note Miscellaneous (config)::). It is also used to parcel out the `.Xqtdir' subdirectories (*note Execution Subdirectories::). `LXQ.CMD' When `uuxqt' is invoked with the `-c' or `--command' option (*note Invoking uuxqt::), it creates a lock file named after the command it is executing. For example, `uuxqt -c rmail' will create the lock file `LXQ.rmail'. This prevents other `uuxqt' daemons from executing jobs of the specified type. `SYSTEM/X./L.XXX' While `uuxqt' is executing a particular job, it creates a lock file with the same name as the `X.' file describing the job, but replacing the initial `X' with `L'. This ensures that if multiple `uuxqt' daemons are running, they do not simultaneously execute the same job. `LCK..SEQ' This lock file is used to control access to the sequence files for each system (*note System Spool Directories::). It is only used on systems which do not support POSIX file locking using the `fcntl' system call.  File: uucp.info, Node: Spool Directory Cleaning, Prev: The Spool Directory Layout, Up: Using Taylor UUCP Cleaning the Spool Directory ============================ The spool directory may need to be cleaned up periodically. Under some circumstances, files may accumulate in various subdirectories, such as `.Preserve' (*note Other Spool Subdirectories::) or `.Corrupt' (*note Execution Subdirectories::). Also, if a remote system stops calling in, you may want to arrange for any queued up mail to be returned to the sender. This can be done using the `uustat' command (*note Invoking uustat::). The `contrib' directory includes a simple `uuclean' script which may be used as an example of a clean up script. It can be run daily out of `crontab'. You should periodically trim the UUCP log files, as they will otherwise grow without limit. The names of the log files are set in `policy.h', and may be overridden in the configuration file (*note config File::). By default they are are `/usr/spool/uucp/Log' and `/usr/spool/uucp/Stats'. You may find the `savelog' program in the `contrib' directory to be of use. There is a manual page for it in `contrib' as well.  File: uucp.info, Node: Configuration Files, Next: Protocols, Prev: Using Taylor UUCP, Up: Top Taylor UUCP Configuration Files ******************************* This chapter describes the configuration files accepted by the Taylor UUCP package if compiled with `HAVE_TAYLOR_CONFIG' set to 1 in `policy.h'. The configuration files are normally found in the directory NEWCONFIGDIR, which is defined by the `configure' option `--with-newconfigdir'; by default NEWCONFIGDIR is `/usr/local/conf/uucp'. However, the main configuration file, `config', is the only one which must be in that directory, since it may specify a different location for any or all of the other files. You may run any of the UUCP programs with a different main configuration file by using the `-I' or `--config' option; this can be useful when testing a new configuration. When you use the `-I' option the programs will revoke any setuid privileges. * Menu: * Configuration Overview:: Configuration File Overview * Configuration File Format:: Configuration File Format * Configuration Examples:: Examples of Configuration Files * Time Strings:: How to Write Time Strings * Chat Scripts:: How to Write Chat Scripts * config File:: The Main Configuration File * sys File:: The System Configuration File * port File:: The Port Configuration Files * dial File:: The Dialer Configuration Files * UUCP Over TCP:: UUCP Over TCP * Security:: Security Issues  File: uucp.info, Node: Configuration Overview, Next: Configuration File Format, Prev: Configuration Files, Up: Configuration Files Configuration File Overview =========================== UUCP uses several different types of configuration files, each describing a different kind of information. The commands permitted in each file are described in detail below. This section is a brief description of some of the different types of files. The `config' file is the main configuration file. It describes general information not associated with a particular remote system, such as the location of various log files. There are reasonable defaults for everything that may be specified in the `config' file, so you may not actually need one on your system. There may be only one `config' file, but there may be one or more of each other type of file. The default is one file for each type, but more may be listed in the `config' file. The `sys' files are used to describe remote systems. Each remote system to which you connect must be listed in a `sys' file. A `sys' file will include information for a system, such as the speed (baud rate) to use, or when to place calls. For each system you wish to call, you must describe one or more ports; these ports may be defined directly in the `sys' file, or they may be defined in a `port' file. The `port' files are used to describe ports. A port is a particular hardware connection on your computer. You would normally define as many ports as there are modems attached to your computer. A TCP connection is also described using a port. The `dial' files are used to describe dialers. Dialer is essentially another word for modem. The `dial' file describes the commands UUCP should use to dial out on a particular type of modem. You would normally define as many dialers as there are types of modems attached to your computer. For example, if you have three Telebit modems used for UUCP, you would probably define three ports and one dialer. There are other types of configuration files, but these are the important ones. The other types are described below.  File: uucp.info, Node: Configuration File Format, Next: Configuration Examples, Prev: Configuration Overview, Up: Configuration Files Configuration File Format ========================= All the configuration files follow a simple line-oriented `KEYWORD VALUE' format. Empty lines are ignored, as are leading spaces; unlike HDB, lines with leading spaces are read. The first word on each line is a keyword. The rest of the line is interpreted according to the keyword. Most keywords are followed by numbers, boolean values or simple strings with no embedded spaces. The `#' character is used for comments. Everything from a `#' to the end of the line is ignored unless the `#' is preceded by a `\' (backslash); if the `#' is preceeded by a `\', the `\' is removed but the `#' remains in the line. This can be useful for a phone number containing a `#'. To enter the sequence `\#', use `\\#'. The backslash character may be used to continue lines. If the last character in a line is a backslash, the backslash is removed and the line is continued by the next line. The second line is attached to the first with no intervening characters; if you want any whitespace between the end of the first line and the start of the second line, you must insert it yourself. However, the backslash is not a general quoting character. For example, you cannot use it to get an embedded space in a string argument. Everything after the keyword must be on the same line. A BOOLEAN may be specified as `y', `Y', `t', or `T' for true and `n', `N', `f', or `F' for false; any trailing characters are ignored, so `true', `false', etc., are also acceptable.  File: uucp.info, Node: Configuration Examples, Next: Time Strings, Prev: Configuration File Format, Up: Configuration Files Examples of Configuration Files =============================== This section provides few typical examples of configuration files. There are also sample configuration files in the `sample' subdirectory of the distribution. * Menu: * config File Examples:: Examples of the Main Configuration File * Leaf Example:: Call a Single Remote Site * Gateway Example:: The Gateway for Several Local Systems  File: uucp.info, Node: config File Examples, Next: Leaf Example, Prev: Configuration Examples, Up: Configuration Examples config File Examples -------------------- To start with, here are some examples of uses of the main configuration file, `config'. For a complete description of the commands that are permitted in `config', see *Note config File::. In many cases you will not need to create a `config' file at all. The most common reason to create one is to give your machine a special UUCP name. Other reasons might be to change the UUCP spool directory, or to permit any remote system to call in. If you have an internal network of machines, then it is likely that the internal name of your UUCP machine is not the name you want to use when calling other systems. For example, here at `airs.com' our mail/news gateway machine is named `elmer.airs.com' (it is one of several machines all named `LOCALNAME.airs.com'). If we did not provide a `config' file, then our UUCP name would be `elmer'; however, we actually want it to be `airs'. Therefore, we use the following line in `config': nodename airs The UUCP spool directory name is set in `policy.h' when the code is compiled. You might at some point decide that it is appropriate to move the spool directory, perhaps to put it on a different disk partition. You would use the following commands in `config' to change to directories on the partition `/uucp': spool /uucp/spool pubdir /uucp/uucppublic logfile /uucp/spool/Log debugfile /uucp/spool/Debug You would then move the contents of the current spool directory to `/uucp/spool'. If you do this, make sure that no UUCP processes are running while you change `config' and move the spool directory. Suppose you wanted to permit any system to call in to your system and request files. This is generally known as "anonymous UUCP", since the systems which call in are effectively anonymous. By default, unknown systems are not permitted to call in. To permit this you must use the `unknown' command in `config'. The `unknown' command is followed by any command that may appear in the system file; for full details, see *Note sys File::. I will show two possible anonymous UUCP configurations. The first will let any system call in and download files, but will not permit them to upload files to your system. # No files may be transferred to this system unknown receive-request no # The public directory is /usr/spool/anonymous unknown pubdir /usr/spool/anonymous # Only files in the public directory may be sent (the default anyhow) unknown remote-send ~ Setting the public directory is convenient for the systems which call in. It permits to request a file by prefixing it with `~/'. For example, assuming your system is known as `server', then to retrieve the file `/usr/spool/anonymous/INDEX' a user on a remote site could just enter `uucp server!~/INDEX ~'; this would transfer `INDEX' from `server''s public directory to the user's local public directory. Note that when using `csh' or `bash' the `!' and the second `~' must be quoted. The next example will permit remote systems to upload files to a special directory named `/usr/spool/anonymous/upload'. Permitting a remote system to upload files permits it to send work requests as well; this example is careful to prohibit commands from unknown systems. # No commands may be executed (the list of permitted commands is empty) unknown commands # The public directory is /usr/spool/anonymous unknown pubdir /usr/spool/anonymous # Only files in the public directory may be sent; users may not download # files from the upload directory unknown remote-send ~ !~/upload # May only upload files into /usr/spool/anonymous/upload unknown remote-receive ~/upload  File: uucp.info, Node: Leaf Example, Next: Gateway Example, Prev: config File Examples, Up: Configuration Examples Leaf Example ------------ A relatively common simple case is a "leaf site", a system which only calls or is called by a single remote site. Here is a typical `sys' file that might be used in such a case. For full details on what commands can appear in the `sys' file, see *Note sys File::. This is the `sys' file that is used at `airs.com'. We use a single modem to dial out to `uunet'. This example shows how you can specify the port and dialer information directly in the `sys' file for simple cases. It also shows the use of the following: `call-login' Using `call-login' and `call-password' allows the default login chat script to be used. In this case, the login name is specified in the call-out login file (*note Configuration File Names::). `call-timegrade' `uunet' is requested to not send us news during the daytime. `chat-fail' If the modem returns `BUSY' or `NO CARRIER' the call is immediately aborted. `protocol-parameter' Since `uunet' tends to be slow, the default timeout has been increased. This `sys' file relies on certain defaults. It will allow `uunet' to queue up `rmail' and `rnews' commands. It will allow users to request files from `uunet' into the UUCP public directory. It will also allow `uunet' to request files from the UUCP public directory; in fact `uunet' never requests files, but for additional security we could add the line `request false'. # The following information is for uunet system uunet # The login name and password are kept in the callout password file call-login * call-password * # We can send anything at any time. time any # During the day we only accept grade `Z' or above; at other times # (not mentioned here) we accept all grades. uunet queues up news # at grade `d', which is lower than `Z'. call-timegrade Z Wk0755-2305,Su1655-2305 # The phone number. phone 7389449 # uunet tends to be slow, so we increase the timeout chat-timeout 120 # We are using a preconfigured Telebit 2500. port type modem port device /dev/ttyd0 port speed 19200 port carrier true port dialer chat "" ATZ\r\d\c OK ATDT\D CONNECT port dialer chat-fail BUSY port dialer chat-fail NO\sCARRIER port dialer complete \d\d+++\d\dATH\r\c port dialer abort \d\d+++\d\dATH\r\c # Increase the timeout and the number of retries. protocol-parameter g timeout 20 protocol-parameter g retries 10  File: uucp.info, Node: Gateway Example, Prev: Leaf Example, Up: Configuration Examples Gateway Example --------------- Many organizations have several local machines which are connected by UUCP, and a single machine which connects to the outside world. This single machine is often referred to as a "gateway" machine. For this example I will assume a fairly simple case. It should still provide a good general example. There are three machines, `elmer', `comton' and `bugs'. `elmer' is the gateway machine for which I will show the configuration file. `elmer' calls out to `uupsi'. As an additional complication, `uupsi' knows `elmer' as `airs'; this will show how a machine can have one name on an internal network but a different name to the external world. `elmer' has two modems. It also has an TCP connection to `uupsi', but since that is supposed to be reserved for interactive work (it is, perhaps, only a 9600 baud SLIP line) it will only use it if the modems are not available. A network this small would normally use a single `sys' file. However, for pedagogical purposes I will show two separate `sys' files, one for the local systems and one for `uupsi'. This is done with the `sysfile' command in the `config' file. Here is the `config' file. # This is config # The local sys file sysfile /usr/local/lib/uucp/sys.local # The remote sys file sysfile /usr/local/lib/uucp/sys.remote Using the defaults feature of the `sys' file can greatly simplify the listing of local systems. Here is `sys.local'. Note that this assumes that the local systems are trusted; they are permited to request any world readable file and to write files into any world writable directory. # This is sys.local # Get the login name and password to use from the call-out file call-login * call-password * # The systems must use a particular login called-login Ulocal # Permit sending any world readable file local-send / remote-send / # Permit receiving into any world writable directory local-receive / remote-receive / # Call at any time time any # Use port1, then port2 port port1 alternate port port2 # Now define the systems themselves. Because of all the defaults we # used, there is very little to specify for the systems themselves. system comton phone 5551212 system bugs phone 5552424 The `sys.remote' file describes the `uupsi' connection. The `myname' command is used to change the UUCP name to `airs' when talking to `uupsi'. # This is sys.remote # Define uupsi system uupsi # The login name and password are in the call-out file call-login * call-password * # We can call out at any time time any # uupsi uses a special login name called-login Uuupsi # uuspi thinks of us as `airs' myname airs # The phone number phone 5554848 # We use port2 first, then port1, then TCP port port2 alternate port port1 alternate # We don't bother to make a special entry in the port file for TCP, we # just describe the entire port right here. We use a special chat # script over TCP because the usual one confuses some TCP servers. port type TCP address uu.psi.com chat ogin: \L word: \P The ports are defined in the file `port' (*note port File::). For this example they are both connected to the same type of 2400 baud Hayes-compatible modem. # This is port port port1 type modem device /dev/ttyd0 dialer hayes speed 2400 port port2 type modem device /dev/ttyd1 dialer hayes speed 2400 Dialers are described in the `dial' file (*note dial File::). # This is dial dialer hayes # The chat script used to dial the phone. \D is the phone number. chat "" ATZ\r\d\c OK ATDT\D CONNECT # If we get BUSY or NO CARRIER we abort the dial immediately chat-fail BUSY chat-fail NO\sCARRIER # When the call is over we make sure we hangup the modem. complete \d\d+++\d\dATH\r\c abort \d\d+++\d\dATH\r\c  File: uucp.info, Node: Time Strings, Next: Chat Scripts, Prev: Configuration Examples, Up: Configuration Files Time Strings ============ Several commands use time strings to specify a range of times. This section describes how to write time strings. A time string may be a list of simple time strings separated with a vertical bar `|' or a comma `,'. Each simple time string must begin with `Su', `Mo', `Tu', `We', `Th', `Fr', or `Sa', or `Wk' for any weekday, or `Any' for any day. Following the day may be a range of hours separated with a hyphen using 24 hour time. The range of hours may cross 0; for example `2300-0700' means any time except 7 AM to 11 PM. If no time is given, calls may be made at any time on the specified day(s). The time string may also be the single word `Never', which does not match any time. The time string may also be a single word with a name defined in a previous `timetable' command (*note Miscellaneous (config)::). Here are a few sample time strings with an explanation of what they mean. `Wk2305-0855,Sa,Su2305-1655' This means weekdays before 8:55 AM or after 11:05 PM, any time Saturday, or Sunday before 4:55 PM or after 11:05 PM. These are approximately the times during which night rates apply to phone calls in the U.S.A. Note that this time string uses, for example, `2305' rather than `2300'; this will ensure a cheap rate phone call even if the computer clock is running up to five minutes ahead of the real time. `Wk0905-2255,Su1705-2255' This means weekdays from 9:05 AM to 10:55 PM, or Sunday from 5:05 PM to 10:55 PM. This is approximately the opposite of the previous example. `Any' This means any day. Since no time is specified, it means any time on any day.  File: uucp.info, Node: Chat Scripts, Next: config File, Prev: Time Strings, Up: Configuration Files Chat Scripts ============ Chat scripts are used in several different places, such as dialing out on modems or logging in to remote systems. Chat scripts are made up of pairs of strings. The program waits until it sees the first string, known as the "expect" string, and then sends out the second string, the "send" string. Each chat script is defined using a set of commands. These commands always end in a string beginning with `chat', but may start with different strings. For example, in the `sys' file there is one set of commands beginning with `chat' and another set beginning with `called-chat'. The prefixes are only used to disambiguate different types of chat scripts, and this section ignores the prefixes when describing the commands. `chat STRINGS' Specify a chat script. The arguments to the `chat' command are pairs of strings separated by whitespace. The first string of each pair is an expect string, the second is a send string. The program will wait for the expect string to appear; when it does, the program will send the send string. If the expect string does not appear within a certain number of seconds (as set by the `chat-timeout' command), the chat script fails and, typically, the call is aborted. If the final expect string is seen (and the optional final send string has been sent), the chat script is successful. An expect string may contain additional subsend and subexpect strings, separated by hyphens. If the expect string is not seen, the subsend string is sent and the chat script continues by waiting for the subexpect string. This means that a hyphen may not appear in an expect string; on an ASCII system, use `\055' instead. An expect string may simply be `""', meaning to skip the expect phase. Otherwise, the following escape characters may appear in expect strings: `\b' a backspace character `\n' a newline or line feed character `\N' a null character (for HDB compatibility) `\r' a carriage return character `\s' a space character `\t' a tab character `\\' a backslash character `\DDD' character DDD, where DDD are up to three octal digits `\xDDD' character DDD, where DDD are hexadecimal digits. As in C, there may be up to three octal digits following a backslash, but the hexadecimal escape sequence continues as far as possible. To follow a hexadecimal escape sequence with a hex digit, interpose a send string of `""'. A chat script expect string may also specify a timeout. This is done by using the escape sequence `\WSECONDS'. This escape sequence may only appear at the very end of the expect string. It temporarily overrides the timeout set by `chat-timeout' (described below) only for the expect string to which it is attached. A send string may simply be `""' to skip the send phase. Otherwise, all of the escape characters legal for expect strings may be used, and the following escape characters are also permitted: `EOT' send an end of transmission character (`^D') `BREAK' send a break character (may not work on all systems) `\c' suppress trailing carriage return at end of send string `\d' delay sending for 1 or 2 seconds `\e' disable echo checking `\E' enable echo checking `\K' same as `BREAK' `\p' pause sending for a fraction of a second `\M' do not require carrier signal `\m' require carrier signal (fail if not present) Some specific types of chat scripts also define additional escape sequences that may appear in the send string. For example, the login chat script defines `\L' and `\P' to send the login name and password, respectively. A carriage return will be sent at the end of each send string, unless the `\c' escape sequence appears in the string. Note that some UUCP packages use `\b' for break, but here it means backspace. Echo checking means that after writing each character the program will wait until the character is echoed. Echo checking must be turned on separately for each send string for which it is desired; it will be turned on for characters following `\E' and turned off for characters following `\e'. When used with a port which does not support the carrier signal, as set by the `carrier' command in the port file, `\M' and `\m' are ignored. Similarly, when used in a dialer chat script with a dialer which does not support the carrier signal, as set by the `carrier' command in the dial file, `\M' and `\m' are ignored. `chat-timeout NUMBER' The number of seconds to wait for an expect string in the chat script, before timing out and sending the next subsend, or failing the chat script entirely. The default value is 10 for a login chat or 60 for any other type of chat. `chat-fail STRING' If the STRING is seen at any time during a chat script, the chat script is aborted. The string may not contain any whitespace characters: escape sequences must be used for them. Multiple `chat-fail' commands may appear in a single chat script. The default is to have none. This permits a chat script to be quickly aborted if an error string is seen. For example, a script used to dial out on a modem might use the command `chat-fail BUSY' to stop the chat script immediately if the string `BUSY' was seen. The `chat-fail' strings are considered in the order they are listed, so if one string is a suffix of another the longer one should be listed first. This affects the error message which will be logged. Of course, if one string is contained within another, but is not a suffix, the smaller string will always be found before the larger string could match. `chat-seven-bit BOOLEAN' If the argument is true, all incoming characters are stripped to seven bits when being compared to the expect string. Otherwise all eight bits are used in the comparison. The default is true, because some Unix systems generate parity bits during the login prompt which must be ignored while running a chat script. This has no effect on any `chat-program', which must ignore parity by itself if necessary. `chat-program STRINGS' Specify a program to run before executing the chat script. This program could run its own version of a chat script, or it could do whatever it wants. If both `chat-program' and `chat' are specified, the program is executed first followed by the chat script. The first argument to the `chat-program' command is the program name to run. The remaining arguments are passed to the program. The following escape sequences are recognized in the arguments: `\Y' port device name `\S' port speed `\\' backslash Some specific uses of `chat-program' define additional escape sequences. Arguments other than escape sequences are passed exactly as they appear in the configuration file, except that sequences of whitespace are compressed to a single space character (this exception may be removed in the future). If the `chat-program' command is not used, no program is run. On Unix, the standard input and standard output of the program will be attached to the port in use. Anything the program writes to standard error will be written to the UUCP log file. No other file descriptors will be open. If the program does not exit with a status of 0, it will be assumed to have failed. This means that the dialing programs used by some versions of HDB may not be used directly, but you may be able to run them via the `dialHDB' program in the `contrib' directory. The program will be run as the `uucp' user, and the environment will be that of the process that started `uucico', so care must be taken to maintain security. No search path is used to find the program; a full file name must be given. If the program is an executable shell script, it will be passed to `/bin/sh' even on systems which are unable to execute shell scripts. Here is a simple example of a chat script that might be used to reset a Hayes compatible modem. chat "" ATZ OK-ATZ-OK The first expect string is `""', so it is ignored. The chat script then sends `ATZ'. If the modem responds with `OK', the chat script finishes. If 60 seconds (the default timeout) pass before seeing `OK', the chat script sends another `ATZ'. If it then sees `OK', the chat script succeeds. Otherwise, the chat script fails. For a more complex chat script example, see *Note Logging In::.  File: uucp.info, Node: config File, Next: sys File, Prev: Chat Scripts, Up: Configuration Files The Main Configuration File =========================== The main configuration file is named `config'. Since all the values that may be specified in the main configuration file also have defaults, there need not be a main configuration file at all. Each command in `config' may have a program prefix, which is a separate word appearing at the beginning of the line. The currently supported prefixes are `uucp' and `cu'. Any command prefixed by `uucp' will not be read by the `cu' program. Any command prefixed by `cu' will only be read by the `cu' program. For example, to use a list of systems known only to `cu', list them in a separate file `FILE' and put `cu sysfile `FILE'' in `config'. * Menu: * Miscellaneous (config):: Miscellaneous config File Commands * Configuration File Names:: Using Different Configuration Files * Log File Names:: Using Different Log Files * Debugging Levels:: Debugging Levels  File: uucp.info, Node: Miscellaneous (config), Next: Configuration File Names, Prev: config File, Up: config File Miscellaneous config File Commands ---------------------------------- `nodename STRING' `hostname STRING' `uuname STRING' These keywords are equivalent. They specify the UUCP name of the local host. If there is no configuration file, an appropriate system function will be used to get the host name, if possible. `spool STRING' Specify the spool directory. The default is from `policy.h'. This is where UUCP files are queued. Status files and various sorts of temporary files are also stored in this directory and subdirectories of it. `pubdir STRING' Specify the public directory. The default is from `policy.h'. When a file is named using a leading `~/', it is taken from or to the public directory. Each system may use a separate public directory by using the `pubdir' command in the system configuration file; see *Note Miscellaneous (sys)::. `lockdir STRING' Specify the directory to place lock files in. The default is from `policy.h'; see the information in that file. Normally the lock directory should be set correctly in `policy.h', and not changed here. However, changing the lock directory is sometimes useful for testing purposes. This only affects lock files for devices and systems; it does not affect certain internal lock files which are stored in the spool directory (*note Spool Lock Files::). `unknown STRING ...' The STRING and subsequent arguments are treated as though they appeared in the system file (*note sys File::). They are used to apply to any unknown systems that may call in, probably to set file transfer permissions and the like. If the `unknown' command is not used, unknown systems are not permitted to call in. `strip-login BOOLEAN' If the argument is true, then, when `uucico' is doing its own login prompting with the `-e', `-l', or `-w' switches, it will strip the parity bit when it reads the login name and password. Otherwise all eight bits will be used when checking the strings against the UUCP password file. The default is true, since some other UUCP packages send parity bits with the login name and password, and few systems use eight bit characters in the password file. `strip-proto BOOLEAN' If the argument is true, then `uucico' will strip the parity bit from incoming UUCP protocol commands. Otherwise all eight bits will be used. This only applies to commands which are not encapsulated in a link layer protocol. The default is true, which should always be correct unless your UUCP system names use eight bit characters. `max-uuxqts NUMBER' Specify the maximum number of `uuxqt' processes which may run at the same time. Having several `uuxqt' processes running at once can significantly slow down a system, but, since `uuxqt' is automatically started by `uucico', it can happen quite easily. The default for `max-uuxqts' is 0, which means that there is no limit. If HDB configuration files are being read and the code was compiled without `HAVE_TAYLOR_CONFIG', then, if the file `Maxuuxqts' in the configuration directory contains a readable number, it will be used as the value for `max-uuxqts'. `run-uuxqt STRING or NUMBER' Specify when `uuxqt' should be run by `uucico'. This may be a positive number, in which case `uucico' will start a `uuxqt' process whenever it receives the given number of execution files from the remote system, and, if necessary, at the end of the call. The argument may also be one of the strings `once', `percall', or `never'. The string `once' means that `uucico' will start `uuxqt' once at the end of execution. The string `percall' means that `uucico' will start `uuxqt' once per call that it makes (this is only different from `once' when `uucico' is invoked in a way that causes it to make multiple calls, such as when the `-r1' option is used without the `-s' option). The string `never' means that `uucico' will never start `uuxqt', in which case `uuxqt' should be periodically run via some other mechanism. The default depends upon which type of configuration files are being used; if `HAVE_TAYLOR_CONFIG' is used the default is `once', otherwise if `HAVE_HDB_CONFIG' is used the default is `percall', and otherwise, for `HAVE_V2_CONFIG', the default is `10'. `timetable STRING STRING' The `timetable' defines a timetable that may be used in subsequently appearing time strings; see *Note Time Strings::. The first string names the timetable entry; the second is a time string. The following `timetable' commands are predefined. The NonPeak timetable is included for compatibility. It originally described the offpeak hours of Tymnet and Telenet, but both have since changed their schedules. timetable Evening Wk1705-0755,Sa,Su timetable Night Wk2305-0755,Sa,Su2305-1655 timetable NonPeak Wk1805-0655,Sa,Su If this command does not appear, then, obviously, no additional timetables will be defined. `v2-files BOOLEAN' If the code was compiled to be able to read V2 configuration files, a false argument to this command will prevent them from being read. This can be useful while testing. The default is true. `hdb-files BOOLEAN' If the code was compiled to be able to read HDB configuration files, a false argument to this command will prevent them from being read. This can be useful while testing. The default is true.  File: uucp.info, Node: Configuration File Names, Next: Log File Names, Prev: Miscellaneous (config), Up: config File Configuration File Names ------------------------ `sysfile STRINGS' Specify the system file(s). The default is the file `sys' in the directory NEWCONFIGDIR. These files hold information about other systems with which this system communicates; see *Note sys File::. Multiple system files may be given on the line, and the `sysfile' command may be repeated; each system file has its own set of defaults. `portfile STRINGS' Specify the port file(s). The default is the file `port' in the directory NEWCONFIGDIR. These files describe ports which are used to call other systems and accept calls from other systems; see *Note port File::. No port files need be named at all. Multiple port files may be given on the line, and the `portfile' command may be repeated. `dialfile STRINGS' Specify the dial file(s). The default is the file `dial' in the directory NEWCONFIGDIR. These files describe dialing devices (modems); see *Note dial File::. No dial files need be named at all. Multiple dial files may be given on the line, and the `dialfile' command may be repeated. `dialcodefile STRINGS' Specify the dialcode file(s). The default is the file `dialcode' in the directory NEWCONFIGDIR. These files specify dialcodes that may be used when sending phone numbers to a modem. This permits using the same set of phone numbers in different area-codes or with different phone systems, by using dialcodes to specify the calling sequence. When a phone number goes through dialcode translation, the leading alphabetic characters are stripped off. The dialcode files are read line by line, just like any other configuration file, and when a line is found whose first word is the same as the leading characters from the phone number, the second word on the line (which would normally consist of numbers) replaces the dialcode in the phone number. No dialcode file need be used. Multiple dialcode files may be specified on the line, and the `dialcodefile' command may be repeated; all the dialcode files will be read in turn until a dialcode is located. `callfile STRINGS' Specify the call out login name and password file(s). The default is the file `call' in the directory NEWCONFIGDIR. If the call out login name or password for a system are given as `*' (*note Logging In::), these files are read to get the real login name or password. Each line in the file(s) has three words: the system name, the login name, and the password. The login name and password may contain escape sequences like those in a chat script expect string (*note Chat Scripts::). This file is only used when placing calls to remote systems; the password file described under `passwdfile' below is used for incoming calls. The intention of the call out file is to permit the system file to be publically readable; the call out files must obviously be kept secure. These files need not be used. Multiple call out files may be specified on the line, and the `callfile' command may be repeated; all the files will be read in turn until the system is found. `passwdfile STRINGS' Specify the password file(s) to use for login names when `uucico' is doing its own login prompting, which it does when given the `-e', `-l' or `-w' switches. The default is the file `passwd' in the directory NEWCONFIGDIR. Each line in the file(s) has two words: the login name and the password (e.g., `Ufoo foopas'). They may contain escape sequences like those in a chat script expect string (*note Chat Scripts::). The login name is accepted before the system name is known, so these are independent of which system is calling in; a particular login may be required for a system by using the `called-login' command in the system file (*note Accepting a Call::). These password files are optional, although one must exist if `uucico' is to present its own login prompts. As a special exception, a colon may be used to separate the login name from the password, and a colon may be used to terminate the password. This means that the login name and password may not contain a colon. This feature, in conjunction with the `HAVE_ENCRYPTED_PASSWORDS' macro in `policy.h', permits using a standard Unix `/etc/passwd' as a UUCP password file, providing the same set of login names and passwords for both `getty' and `uucico'. Multiple password files may be specified on the line, and the `passwdfile' command may be repeated; all the files will be read in turn until the login name is found.  File: uucp.info, Node: Log File Names, Next: Debugging Levels, Prev: Configuration File Names, Up: config File Log File Names -------------- `logfile STRING' Name the log file. The default is from `policy.h'. Logging information is written to this file. If `HAVE_HDB_LOGGING' is defined in `policy.h', then by default a separate log file is used for each system; using this command to name a log file will cause all the systems to use it. `statfile STRING' Name the statistics file. The default is from `policy.h'. Statistical information about file transfers is written to this file. `debugfile STRING' Name the file to which all debugging information is written. The default is from `policy.h'. This command is only effective if the code has been compiled to include debugging (this is controlled by the `DEBUG' macro in `policy.h'). If debugging is on, messages written to the log file are also written to the debugging file to make it easier to keep the order of actions straight. The debugging file is different from the log file because information such as passwords can appear in it, so it must be not be publically readable. uucp-1.07/uucp.info-40000664000076400007640000014050107665322530010113 This is uucp.info, produced by makeinfo version 4.1 from uucp.texi. START-INFO-DIR-ENTRY * UUCP: (uucp). Transfer mail and news across phone lines. END-INFO-DIR-ENTRY This file documents Taylor UUCP, version 1.07. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "Copying" are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "Copying" may be included in a translation approved by the author instead of in the original English.  File: uucp.info, Node: Debugging Levels, Prev: Log File Names, Up: config File Debugging Levels ---------------- `debug STRING ...' Set the debugging level. This command is only effective if the code has been compiled to include debugging. The default is to have no debugging. The arguments are strings which name the types of debugging to be turned on. The following types of debugging are defined: `abnormal' Output debugging messages for abnormal situations, such as recoverable errors. `chat' Output debugging messages for chat scripts. `handshake' Output debugging messages for the initial handshake. `uucp-proto' Output debugging messages for the UUCP session protocol. `proto' Output debugging messages for the individual link protocols. `port' Output debugging messages for actions on the communication port. `config' Output debugging messages while reading the configuration files. `spooldir' Output debugging messages for actions in the spool directory. `execute' Output debugging messages whenever another program is executed. `incoming' List all incoming data in the debugging file. `outgoing' List all outgoing data in the debugging file. `all' All of the above. The debugging level may also be specified as a number. A 1 will set `chat' debugging, a 2 will set both `chat' and `handshake' debugging, and so on down the possibilities. Currently an 11 will turn on all possible debugging, since there are 11 types of debugging messages listed above; more debugging types may be added in the future. The `debug' command may be used several times in the configuration file; every debugging type named will be turned on. When running any of the programs, the `-x' switch (actually, for `uulog' it's the `-X' switch) may be used to turn on debugging. The argument to the `-x' switch is one of the strings listed above, or a number as described above, or a comma separated list of strings (e.g., `-x chat,handshake'). The `-x' switch may also appear several times on the command line, in which case all named debugging types will be turned on. The `-x' debugging is in addition to any debugging specified by the `debug' command; there is no way to cancel debugging information. The debugging level may also be set specifically for calls to or from a specific system with the `debug' command in the system file (*note Miscellaneous (sys)::). The debugging messages are somewhat idiosyncratic; it may be necessary to refer to the source code for additional information in some cases.  File: uucp.info, Node: sys File, Next: port File, Prev: config File, Up: Configuration Files The System Configuration File ============================= By default there is a single system configuration, named `sys' in the directory NEWCONFIGDIR. This may be overridden by the `sysfile' command in the main configuration file; see *Note Configuration File Names::. These files describe all remote systems known to the UUCP package. * Menu: * Defaults and Alternates:: Using Defaults and Alternates * Naming the System:: Naming the System * Calling Out:: Calling Out * Accepting a Call:: Accepting a Call * Protocol Selection:: Protocol Selection * File Transfer Control:: File Transfer Control * Miscellaneous (sys):: Miscellaneous sys File Commands * Default sys File Values:: Default Values  File: uucp.info, Node: Defaults and Alternates, Next: Naming the System, Prev: sys File, Up: sys File Defaults and Alternates ----------------------- The first set of commands in the file, up to the first `system' command, specify defaults to be used for all systems in that file. Each `sys' file uses a different set of defaults. Subsequently, each set of commands from `system' up to the next `system' command describe a particular system. Default values may be overridden for specific systems. Each system may then have a series of alternate choices to use when calling out or calling in. The first set of commands for a particular system, up to the first `alternate' command, provide the first choice. Subsequently, each set of commands from `alternate' up to the next `alternate' command describe an alternate choice for calling out or calling in. When a system is called, the commands before the first `alternate' are used to select a phone number, port, and so forth; if the call fails for some reason, the commands between the first `alternate' and the second are used, and so forth. Well, not quite. Actually, each succeeding alternate will only be used if it is different in some relevant way (different phone number, different chat script, etc.). If you want to force the same alternate to be used again (to retry a phone call more than once, for example), enter the phone number (or any other relevant field) again to make it appear different. The alternates can also be used to give different permissions to an incoming call based on the login name. This will only be done if the first set of commands, before the first `alternate' command, uses the `called-login' command. The list of alternates will be searched, and the first alternate with a matching `called-login' command will be used. If no alternates match, the call will be rejected. The `alternate' command may also be used in the file-wide defaults (the set of commands before the first `system' command). This might be used to specify a list of ports which are available for all systems (for an example of this, see *Note Gateway Example::) or to specify permissions based on the login name used by the remote system when it calls in. The first alternate for each system will default to the first alternate for the file-wide defaults (as modified by the commands used before the first `alternate' command for this system), the second alternate for each system to the second alternate for the file-wide defaults (as modified the same way), and so forth. If a system specifies more alternates than the file-wide defaults, the trailing ones will default to the last file-wide default alternate. If a system specifies fewer alternates than the file-wide defaults, the trailing file-wide default alternates will be used unmodified. The `default-alternates' command may be used to modify this behaviour. This can all get rather confusing, although it's easier to use than to describe concisely; the `uuchk' program may be used to ensure that you are getting what you want.  File: uucp.info, Node: Naming the System, Next: Calling Out, Prev: Defaults and Alternates, Up: sys File Naming the System ----------------- `system STRING' Specify the remote system name. Subsequent commands up to the next `system' command refer to this system. `alternate [STRING]' Start an alternate set of commands (*note Defaults and Alternates::). An optional argument may be used to name the alternate. This name will be recorded in the log file if the alternate is used to call the system. There is no way to name the first alternate (the commands before the first `alternate' command). `default-alternates BOOLEAN' If the argument is false, any remaining default alternates (from the defaults specified at the top of the current system file) will not be used. The default is true. `alias STRING' Specify an alias for the current system. The alias may be used by local `uucp' and `uux' commands, as well as by the remote system (which can be convenient if a remote system changes its name). The default is to have no aliases. `myname STRING' Specifies a different system name to use when calling the remote system. Also, if `called-login' is used and is not `ANY', then, when a system logs in with that login name, STRING is used as the local system name. Because the local system name must be determined before the remote system has identified itself, using `myname' and `called-login' together for any system will set the local name for that login; this means that each locally used system name must have a unique login name associated with it. This allows a system to have different names for an external and an internal network. The default is to not use a special local name.  File: uucp.info, Node: Calling Out, Next: Accepting a Call, Prev: Naming the System, Up: sys File Calling Out ----------- This section describes commands used when placing a call to another system. * Menu: * When to Call:: When to Call * Placing the Call:: Placing the Call * Logging In:: Logging In  File: uucp.info, Node: When to Call, Next: Placing the Call, Prev: Calling Out, Up: Calling Out When to Call ............ `time STRING [NUMBER]' Specify when the system may be called. The first argument is a time string; see *Note Time Strings::. The optional second argument specifies a retry time in minutes. If a call made during a time that matches the time string fails, no more calls are permitted until the retry time has passed. By default an exponentially increasing retry time is used: after each failure the next retry period is longer. A retry time specified in the `time' command is always a fixed amount of time. The `time' command may appear multiple times in a single alternate, in which case if any time string matches the system may be called. When the `time' command is used for a particular system, any `time' or `timegrade' commands that appeared in the system defaults are ignored. The default time string is `Never'. `timegrade CHARACTER STRING [NUMBER]' The CHARACTER specifies a grade. It must be a single letter or digit. The STRING is a time string (*note Time Strings::). All jobs of grade CHARACTER or higher (where `0' > `9' > `A' > `Z' > `a' > `z') may be run at the specified time. An ordinary `time' command is equivalent to using `timegrade' with a grade of `z', permitting all jobs. If there are no jobs of a sufficiently high grade according to the time string, the system will not be called. Giving the `-s' switch to `uucico' to force it to call a system causes it to assume there is a job of grade `0' waiting to be run. The optional third argument specifies a retry time in minutes. See the `time' command, above, for more details. Note that the `timegrade' command serves two purposes: 1) if there is no job of sufficiently high grade the system will not be called, and 2) if the system is called anyway (because the `-s' switch was given to `uucico') only jobs of sufficiently high grade will be transferred. However, if the other system calls in, the `timegrade' commands are ignored, and jobs of any grade may be transferred (but see `call-timegrade' and `called-timegrade', below). Also, the `timegrade' command will not prevent the other system from transferring any job it chooses, regardless of who placed the call. The `timegrade' command may appear multiple times without using `alternate'. When the `timegrade' command is used for a particular system, any `time' or `timegrade' commands that appeared in the system defaults are ignored. If this command does not appear, there are no restrictions on what grade of work may be done at what time. `max-retries NUMBER' Gives the maximum number of times this system may be retried. If this many calls to the system fail, it will be called at most once a day whatever the retry time is. The default is 26. `success-wait NUMBER' A retry time, in seconds, which applies after a successful call. This can be used to put a limit on how frequently the system is called. For example, an argument of 1800 means that the system will not be called more than once every half hour. The default is 0, which means that there is no limit. `call-timegrade CHARACTER STRING' The CHARACTER is a single character `A' to `Z', `a' to `z', or `0' to `9' and specifies a grade. The STRING is a time string (*note Time Strings::). If a call is placed to the other system during a time which matches the time string, the remote system will be requested to only run jobs of grade CHARACTER or higher. Unfortunately, there is no way to guarantee that the other system will obey the request (this UUCP package will, but there are others which will not); moreover, job grades are historically somewhat arbitrary, so specifying a grade will only be meaningful if the other system cooperates in assigning grades. This grade restriction only applies when the other system is called, not when the other system calls in. The `call-timegrade' command may appear multiple times without using `alternate'. If this command does not appear, or if none of the time strings match, the remote system will be allowed to send whatever grades of work it chooses. `called-timegrade CHARACTER STRING' The CHARACTER is a single character `A' to `Z', `a' to `z', or `0' to `9' and specifies a grade. The STRING is a time string (*note Time Strings::). If a call is received from the other system during a time which matches the time string, only jobs of grade CHARACTER or higher will be sent to the remote system. This allows the job grade to be set for incoming calls, overriding any request made by the remote uucico. As noted above, job grades are historically somewhat arbitrary, so specifying a grade will only be meaningful if the other system cooperates in assigning grades. This grade restriction only applies to jobs on the local system; it does not affect the jobs transferred by the remote system. This grade restriction only applies when the other system calls in, not when the other system is called. The `called-timegrade' command may appear multiple times. If this command does not appear, or if none of the time strings match, any grade may be sent to the remote system upon receiving a call.  File: uucp.info, Node: Placing the Call, Next: Logging In, Prev: When to Call, Up: Calling Out Placing the Call ................ `speed NUMBER' `baud NUMBER' Specify the speed (the term "baud" is technically incorrect, but widely understood) at which to call the system. This will try all available ports with that speed until an unlocked port is found. The ports are defined in the port file. If both `speed' and `port' commands appear, both are used when selecting a port. To allow calls at more than one speed, the `alternate' command must be used (*note Defaults and Alternates::). If this command does not appear, there is no default; the speed may be specified in the port file, but if it is not then the natural speed of the port will be used (whatever that means on the system). Specifying an explicit speed of 0 will request the natural speed of the port (whatever the system sets it to), overriding any default speed from the defaults at the top of the file. `port STRING' Name a particular port or type of port to use when calling the system. The information for this port is obtained from the port file. If this command does not appear, there is no default; a port must somehow be specified in order to call out (it may be specified implicitly using the `speed' command or explicitly using the next version of `port'). There may be many ports with the same name; each will be tried in turn until an unlocked one is found which matches the desired speed. `port STRING ...' If more than one string follows the `port' command, the strings are treated as a command that might appear in the port file (*note port File::). If a port is named (by using a single string following `port') these commands are ignored; their purpose is to permit defining the port completely in the system file rather than always requiring entries in two different files. In order to call out, a port must be specified using some version of the `port' command, or by using the `speed' command to select ports from the port file. `phone STRING' `address STRING' Give a phone number to call (when using a modem port) or a remote host to contact (when using a TCP or TLI port). The commands `phone' and `address' are equivalent; the duplication is intended to provide a mnemonic choice depending on the type of port in use. When used with a modem port, an `=' character in the phone number means to wait for a secondary dial tone (although only some modems support this); a `-' character means to pause while dialing for 1 second (again, only some modems support this). If the system has more than one phone number, each one must appear in a different alternate. The `phone' command must appear in order to call out on a modem; there is no default. When used with a TCP port, the string names the host to contact. It may be a domain name or a numeric Internet address. If no address is specified, the system name is used. When used with a TLI port, the string is treated as though it were an expect string in a chat script, allowing the use of escape characters (*note Chat Scripts::). The `dialer-sequence' command in the port file may override this address (*note port File::). When used with a port that not a modem or TCP or TLI, this command is ignored.  File: uucp.info, Node: Logging In, Prev: Placing the Call, Up: Calling Out Logging In .......... `chat STRINGS' `chat-timeout NUMBER' `chat-fail STRING' `chat-seven-bit BOOLEAN' `chat-program STRINGS' These commands describe a chat script to use when logging on to a remote system. This login chat script is run after any chat script defined in the `dial' file (*note dial File::). Chat scripts are explained in *Note Chat Scripts::. Two additional escape sequences may be used in send strings. `\L' Send the login name, as set by the `call-login' command. `\P' Send the password, as set by the `call-password' command. Three additional escape sequences may be used with the `chat-program' command. These are `\L' and `\P', which become the login name and password, respectively, and `\Z', which becomes the name of the system of being called. The default chat script is: chat "" \r\c ogin:-BREAK-ogin:-BREAK-ogin: \L word: \P This will send a carriage return (the `\c' suppresses the additional trailing carriage return that would otherwise be sent) and waits for the string `ogin:' (which would be the last part of the `login:' prompt supplied by a Unix system). If it doesn't see `ogin:', it sends a break and waits for `ogin:' again. If it still doesn't see `ogin:', it sends another break and waits for `ogin:' again. If it still doesn't see `ogin:', the chat script aborts and hangs up the phone. If it does see `ogin:' at some point, it sends the login name (as specified by the `call-login' command) followed by a carriage return (since all send strings are followed by a carriage return unless `\c' is used) and waits for the string `word:' (which would be the last part of the `Password:' prompt supplied by a Unix system). If it sees `word:', it sends the password and a carriage return, completing the chat script. The program will then enter the handshake phase of the UUCP protocol. This chat script will work for most systems, so you will only be required to use the `call-login' and `call-password' commands. In fact, in the file-wide defaults you could set defaults of `call-login *' and `call-password *'; you would then just have to make an entry for each system in the call-out login file. Some systems seem to flush input after the `login:' prompt, so they may need a version of this chat script with a `\d' before the `\L'. When using UUCP over TCP, some servers will not be handle the initial carriage return sent by this chat script; in this case you may have to specify the simple chat script `ogin: \L word: \P'. `call-login STRING' Specify the login name to send with `\L' in the chat script. If the string is `*' (e.g., `call-login *') the login name will be fetched from the call out login name and password file (*note Configuration File Names::). The string may contain escape sequences as though it were an expect string in a chat script (*note Chat Scripts::). There is no default. `call-password STRING' Specify the password to send with `\P' in the chat script. If the string is `*' (e.g., `call-password *') the password will be fetched from the call-out login name and password file (*note Configuration File Names::). The string may contain escape sequences as though it were an expect string in a chat script (*note Chat Scripts::). There is no default.  File: uucp.info, Node: Accepting a Call, Next: Protocol Selection, Prev: Calling Out, Up: sys File Accepting a Call ---------------- `called-login STRINGS' The first STRING specifies the login name that the system must use when calling in. If it is `ANY' (e.g., `called-login ANY') any login name may be used; this is useful to override a file-wide default and to indicate that future alternates may have different login names. Case is significant. The default value is `ANY'. Different alternates (*note Defaults and Alternates::) may use different `called-login' commands, in which case the login name will be used to select which alternate is in effect; this will only work if the first alternate (before the first `alternate' command) uses the `called-login' command. Additional strings may be specified after the login name; they are a list of which systems are permitted to use this login name. If this feature is used, then normally the login name will only be given in a single `called-login' command. Only systems which appear on the list, or which use an explicit `called-login' command, will be permitted to use that login name. If the same login name is used more than once with a list of systems, all the lists are concatenated together. This feature permits you to restrict a login name to a particular set of systems without requiring you to use the `called-login' command for every single system; you can achieve a similar effect by using a different system file for each permitted login name with an appropriate `called-login' command in the file-wide defaults. `callback BOOLEAN' If BOOLEAN is true, then when the remote system calls `uucico' will hang up the connection and prepare to call it back. The default is false. `called-chat STRINGS' `called-chat-timeout NUMBER' `called-chat-fail STRING' `called-chat-seven-bit BOOLEAN' `called-chat-program STRINGS' These commands may be used to define a chat script (*note Chat Scripts::) that is run whenever the local system is called by the system being defined. The chat script defined by the `chat' command (*note Logging In::), on the other hand, is used when the remote system is called. This called chat script might be used to set special modem parameters that are appropriate to a particular system. It is run after protocol negotiation is complete, but before the protocol has been started. For additional escape sequence which may be used besides those defined for all chat scripts, see *Note Logging In::. There is no default called chat script. If the called chat script fails, the incoming call will be aborted.  File: uucp.info, Node: Protocol Selection, Next: File Transfer Control, Prev: Accepting a Call, Up: sys File Protocol Selection ------------------ `protocol STRING' Specifies which protocols to use for the other system, and in which order to use them. This would not normally be used. For example, `protocol tfg'. The default depends on the characteristics of the port and the dialer, as specified by the `seven-bit' and `reliable' commands. If neither the port nor the dialer use either of these commands, the default is to assume an eight-bit reliable connection. The commands `seven-bit true' or `reliable false' might be used in either the port or the dialer to change this. Each protocol has particular requirements that must be met before it will be considered during negotiation with the remote side. The `t' and `e' protocols are intended for use over TCP or some other communication path with end to end reliability, as they do no checking of the data at all. They will only be considered on a TCP port which is both reliable and eight bit. For technical details, see *Note t Protocol::, and *Note e Protocol::. The `i' protocol is a bidirectional protocol. It requires an eight-bit connection. It will run over a half-duplex link, such as Telebit modems in PEP mode, but for efficient use of such a connection you must use the `half-duplex' command (*note port File::). *Note i Protocol::. The `g' protocol is robust, but requires an eight-bit connection. *Note g Protocol::. The `G' protocol is the System V Release 4 version of the `g' protocol. *Note Big G Protocol::. The `a' protocol is a Zmodem like protocol, contributed by Doug Evans. It requires an eight-bit connection, but unlike the `g' or `i' protocol it will work if certain control characters may not be transmitted. The `j' protocol is a variant of the `i' protocol which can avoid certain control characters. The set of characters it avoids can be set by a parameter. While it technically does not require an eight bit connection (it could be configured to avoid all characters with the high bit set) it would be very inefficient to use it over one. It is useful over a eight-bit connection that will not transmit certain control characters. *Note j Protocol::. The `f' protocol is intended for use with X.25 connections; it checksums each file as a whole, so any error causes the entire file to be retransmitted. It requires a reliable connection, but only uses seven-bit transmissions. It is a streaming protocol, so, while it can be used on a serial port, the port must be completely reliable and flow controlled; many aren't. *Note f Protocol::. The `v' protocol is the `g' protocol as used by the DOS program UUPC/Extended. It is provided only so that UUPC/Extended users can use it; there is no particular reason to select it. *Note v Protocol::. The `y' protocol is an efficient streaming protocol. It does error checking, but when it detects an error it immediately aborts the connection. This requires a reliable, flow controlled, eight-bit connection. In practice, it is only useful on a connection that is nearly always error-free. Unlike the `t' and `e' protocols, the connection need not be entirely error-free, so the `y' protocol can be used on a serial port. *Note y Protocol::. The protocols will be considered in the order shown above. This means that if neither the `seven-bit' nor the `reliable' command are used, the `t' protocol will be used over a TCP connection and the `i' protocol will be used over any other type of connection (subject, of course, to what is supported by the remote system; it may be assumed that all systems support the `g' protocol). Note that currently specifying both `seven-bit true' and `reliable false' will not match any protocol. If this occurs through a combination of port and dialer specifications, you will have to use the `protocol' command for the system or no protocol will be selected at all (the only reasonable choice would be `protocol f'). A protocol list may also be specified for a port (*note port File::), but, if there is a list for the system, the list for the port is ignored. `protocol-parameter CHARACTER STRING ...' CHARACTER is a single character specifying a protocol. The remaining strings are a command specific to that protocol which will be executed if that protocol is used. A typical command is something like `window 7'. The particular commands are protocol specific. The `i' protocol supports the following commands, all of which take numeric arguments: `window' The window size to request the remote system to use. This must be between 1 and 16 inclusive. The default is 16. `packet-size' The packet size to request the remote system to use. This must be between 1 and 4095 inclusive. The default is 1024. `remote-packet-size' If this is between 1 and 4095 inclusive, the packet size requested by the remote system is ignored, and this is used instead. The default is 0, which means that the remote system's request is honored. `sync-timeout' The length of time, in seconds, to wait for a SYNC packet from the remote system. SYNC packets are exchanged when the protocol is started. The default is 10. `sync-retries' The number of times to retry sending a SYNC packet before giving up. The default is 6. `timeout' The length of time, in seconds, to wait for an incoming packet before sending a negative acknowledgement. The default is 10. `retries' The number of times to retry sending a packet or a negative acknowledgement before giving up and closing the connection. The default is 6. `errors' The maximum number of errors to permit before closing the connection. The default is 100. `error-decay' The rate at which to ignore errors. Each time this many packets are received, the error count is decreased by one, so that a long connection with an occasional error will not exceed the limit set by `errors'. The default is 10. `ack-frequency' The number of packets to receive before sending an acknowledgement. The default is half the requested window size, which should provide good performance in most cases. The `g', `G' and `v' protocols support the following commands, all of which take numeric arguments, except `short-packets' which takes a boolean argument: `window' The window size to request the remote system to use. This must be between 1 and 7 inclusive. The default is 7. `packet-size' The packet size to request the remote system to use. This must be a power of 2 between 32 and 4096 inclusive. The default is 64 for the `g' and `G' protocols and 1024 for the `v' protocol. Many older UUCP packages do not support packet sizes larger than 64, and many others do not support packet sizes larger than 128. Some UUCP packages will even dump core if a larger packet size is requested. The packet size is not a negotiation, and it may be different in each direction. If you request a packet size larger than the remote system supports, you will not be able to send any files. `startup-retries' The number of times to retry the initialization sequence. The default is 8. `init-retries' The number of times to retry one phase of the initialization sequence (there are three phases). The default is 4. `init-timeout' The timeout in seconds for one phase of the initialization sequence. The default is 10. `retries' The number of times to retry sending either a data packet or a request for the next packet. The default is 6. `timeout' The timeout in seconds when waiting for either a data packet or an acknowledgement. The default is 10. `garbage' The number of unrecognized bytes to permit before dropping the connection. This must be larger than the packet size. The default is 10000. `errors' The number of errors (malformed packets, out of order packets, bad checksums, or packets rejected by the remote system) to permit before dropping the connection. The default is 100. `error-decay' The rate at which to ignore errors. Each time this many packets are received, the error count is decreased by one, so that a long connection with an occasional error will not exceed the limit set by `errors'. The default is 10. `remote-window' If this is between 1 and 7 inclusive, the window size requested by the remote system is ignored and this is used instead. This can be useful when dealing with some poor UUCP packages. The default is 0, which means that the remote system's request is honored. `remote-packet-size' If this is between 32 and 4096 inclusive the packet size requested by the remote system is ignored and this is used instead. There is probably no good reason to use this. The default is 0, which means that the remote system's request is honored. `short-packets' If this is true, then the code will optimize by sending shorter packets when there is less data to send. This confuses some UUCP packages, such as System V Release 4 (when using the `G' protocol) and Waffle; when connecting to such a package, this parameter must be set to false. The default is true for the `g' and `v' protocols and false for the `G' protocol. The `a' protocol is a Zmodem like protocol contributed by Doug Evans. It supports the following commands, all of which take numeric arguments except for `escape-control', which takes a boolean argument: `timeout' Number of seconds to wait for a packet to arrive. The default is 10. `retries' The number of times to retry sending a packet. The default is 10. `startup-retries' The number of times to retry sending the initialization packet. The default is 4. `garbage' The number of garbage characters to accept before closing the connection. The default is 2400. `send-window' The number of characters that may be sent before waiting for an acknowledgement. The default is 1024. `escape-control' Whether to escape control characters. If this is true, the protocol may be used over a connection which does not transmit certain control characters, such as `XON' or `XOFF'. The connection must still transmit eight bit characters other than control characters. The default is false. The `j' protocol can be used over an eight bit connection that will not transmit certain control characters. It accepts the same protocol parameters that the `i' protocol accepts, as well as one more: `avoid' A list of characters to avoid. This is a string which is interpreted as an escape sequence (*note Chat Scripts::). The protocol does not have a way to avoid printable ASCII characters (byte values from 32 to 126, inclusive); only ASCII control characters and eight-bit characters may be avoided. The default value is `\021\023'; these are the characters `XON' and `XOFF', which many connections use for flow control. If the package is configured to use `HAVE_BSD_TTY', then on some versions of Unix you may have to avoid `\377' as well, due to the way some implementations of the BSD terminal driver handle signals. The `f' protocol is intended for use with error-correcting modems only; it checksums each file as a whole, so any error causes the entire file to be retransmitted. It supports the following commands, both of which take numeric arguments: `timeout' The timeout in seconds before giving up. The default is 120. `retries' How many times to retry sending a file. The default is 2. The `t' and `e' protocols are intended for use over TCP or some other communication path with end to end reliability, as they do no checking of the data at all. They both support a single command, which takes a numeric argument: `timeout' The timeout in seconds before giving up. The default is 120. The `y' protocol is a streaming protocol contributed by Jorge Cwik. It supports the following commands, both of which take numeric arguments: `timeout' The timeout in seconds when waiting for a packet. The default is 60. `packet-size' The packet size to use. The default is 1024. The protocol parameters are reset to their default values after each call.  File: uucp.info, Node: File Transfer Control, Next: Miscellaneous (sys), Prev: Protocol Selection, Up: sys File File Transfer Control --------------------- `send-request BOOLEAN' The BOOLEAN determines whether the remote system is permitted to request files from the local system. The default is yes. `receive-request BOOLEAN' The BOOLEAN determines whether the remote system is permitted to send files to the local system. The default is yes. `request BOOLEAN' A shorthand command, equivalent to specifying both `send-request BOOLEAN' and `receive-request BOOLEAN'. `call-transfer BOOLEAN' The BOOLEAN is checked when the local system places the call. It determines whether the local system may do file transfers queued up for the remote system. The default is yes. `called-transfer BOOLEAN' The BOOLEAN is checked when the remote system calls in. It determines whether the local system may do file transfers queued up for the remote system. The default is yes. `transfer BOOLEAN' A shorthand command, equivalent to specifying both `call-transfer BOOLEAN' and `called-transfer BOOLEAN'. `call-local-size NUMBER STRING' The STRING is a time string (*note Time Strings::). The NUMBER is the size in bytes of the largest file that should be transferred at a time matching the time string, if the local system placed the call and the request was made by the local system. This command may appear multiple times in a single alternate. If this command does not appear, or if none of the time strings match, there are no size restrictions. With all the size control commands, the size of a file from the remote system (as opposed to a file from the local system) will only be checked if the other system is running this package: other UUCP packages will not understand a maximum size request, nor will they provide the size of remote files. `call-remote-size NUMBER STRING' Specify the size in bytes of the largest file that should be transferred at a given time by remote request, when the local system placed the call. This command may appear multiple times in a single alternate. If this command does not appear, there are no size restrictions. `called-local-size NUMBER STRING' Specify the size in bytes of the largest file that should be transferred at a given time by local request, when the remote system placed the call. This command may appear multiple times in a single alternate. If this command does not appear, there are no size restrictions. `called-remote-size NUMBER STRING' Specify the size in bytes of the largest file that should be transferred at a given time by remote request, when the remote system placed the call. This command may appear multiple times in a single alternate. If this command does not appear, there are no size restrictions. `local-send STRINGS' Specifies that files in the directories named by the STRINGS may be sent to the remote system when requested locally (using `uucp' or `uux'). The directories in the list should be separated by whitespace. A `~' may be used for the public directory. On a Unix system, this is typically `/usr/spool/uucppublic'; the public directory may be set with the `pubdir' command. Here is an example of `local-send': local-send ~ /usr/spool/ftp/pub Listing a directory allows all files within the directory and all subdirectories to be sent. Directories may be excluded by preceding them with an exclamation point. For example: local-send /usr/ftp !/usr/ftp/private ~ means that all files in `/usr/ftp' or the public directory may be sent, except those files in `/usr/ftp/private'. The list of directories is read from left to right, and the last directory to apply takes effect; this means that directories should be listed from top down. The default is the root directory (i.e., any file at all may be sent by local request). `remote-send STRINGS' Specifies that files in the named directories may be sent to the remote system when requested by the remote system. The default is `~'. `local-receive STRINGS' Specifies that files may be received into the named directories when requested by a local user. The default is `~'. `remote-receive STRINGS' Specifies that files may be received into the named directories when requested by the remote system. The default is `~'. On Unix, the remote system may only request that files be received into directories that are writeable by the world, regardless of how this is set. `forward-to STRINGS' Specifies a list of systems to which files may be forwarded. The remote system may forward files through the local system on to any of the systems in this list. The string `ANY' may be used to permit forwarding to any system. The default is to not permit forwarding to other systems. Note that if the remote system is permitted to execute the `uucp' command, it effectively has the ability to forward to any system. `forward-from STRINGS' Specifies a list of systems from which files may be forwarded. The remote system may request files via the local system from any of the systems in this list. The string `ANY' may be used to permit forwarding to any system. The default is to not permit forwarding from other systems. Note that if a remote system is permitted to execute the `uucp' command, it effectively has the ability to request files from any system. `forward STRINGS' Equivalent to specifying both `forward-to STRINGS' and `forward-from STRINGS'. This would normally be used rather than either of the more specific commands. `max-file-time NUMBER' The maximum amount of time which will be sent sending any one file if there are other files to send. This will only be effective when using a protocol which permits interrupting one file send to send another file. This is true of the `i' and `j' protocols. The default is to have no maximum.  File: uucp.info, Node: Miscellaneous (sys), Next: Default sys File Values, Prev: File Transfer Control, Up: sys File Miscellaneous sys File Commands ------------------------------- `sequence BOOLEAN' If BOOLEAN is true, then conversation sequencing is automatically used for the remote system, so that if somebody manages to spoof as the remote system, it will be detected the next time the remote system actually calls. This is false by default. `command-path STRINGS' Specifies the path (a list of whitespace separated directories) to be searched to locate commands to execute. This is only used for commands requested by `uux', not for chat programs. The default is from `policy.h'. `commands STRINGS' The list of commands which the remote system is permitted to execute locally. For example: `commands rnews rmail'. If the value is `ALL' (case significant), all commands may be executed. The default is `rnews rmail'. `free-space NUMBER' Specify the minimum amount of file system space (in bytes) to leave free after receiving a file. If the incoming file will not fit, it will be rejected. This initial rejection will only work when talking to another instance of this package, since older UUCP packages do not provide the file size of incoming files. Also, while a file is being received, `uucico' will periodically check the amount of free space. If it drops below the amount given by the `free-space' command, the file transfer will be aborted. The default amount of space to leave free is from `policy.h'. This file space checking may not work on all systems. `pubdir STRING' Specifies the public directory that is used when `~' is specifed in a file transfer or a list of directories. This essentially overrides the public directory specified in the main configuration file for this system only. The default is the public directory specified in the main configuration file (which defaults to a value from `policy.h'). `debug STRING ...' Set additional debugging for calls to or from the system. This may be used to debug a connection with a specific system. It is particularly useful when debugging incoming calls, since debugging information will be generated whenever the call comes in. See the `debug' command in the main configuration file (*note Debugging Levels::) for more details. The debugging information specified here is in addition to that specified in the main configuration file or on the command line. `max-remote-debug STRING ...' When the system calls in, it may request that the debugging level be set to a certain value. The `max-remote-debug' command may be used to put a limit on the debugging level which the system may request, to avoid filling up the disk with debugging information. Only the debugging types named in the `max-remote-debug' command may be turned on by the remote system. To prohibit any debugging, use `max-remote-debug none'.  File: uucp.info, Node: Default sys File Values, Prev: Miscellaneous (sys), Up: sys File Default sys File Values ----------------------- The following are used as default values for all systems; they can be considered as appearing before the start of the file. time Never chat "" \r\c ogin:-BREAK-ogin:-BREAK-ogin: \L word: \P chat-timeout 10 callback n sequence n request y transfer y local-send / remote-send ~ local-receive ~ remove-receive ~ command-path [ from `policy.h' ] commands rnews rmail max-remote-debug abnormal,chat,handshake uucp-1.07/uucp.info-50000664000076400007640000013002307665322530010112 This is uucp.info, produced by makeinfo version 4.1 from uucp.texi. START-INFO-DIR-ENTRY * UUCP: (uucp). Transfer mail and news across phone lines. END-INFO-DIR-ENTRY This file documents Taylor UUCP, version 1.07. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "Copying" are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "Copying" may be included in a translation approved by the author instead of in the original English.  File: uucp.info, Node: port File, Next: dial File, Prev: sys File, Up: Configuration Files The Port Configuration File =========================== The port files may be used to name and describe ports. By default there is a single port file, named `port' in the directory NEWCONFIGDIR. This may be overridden by the `portfile' command in the main configuration file; see *Note Configuration File Names::. Any commands in a port file before the first `port' command specify defaults for all ports in the file; however, since the `type' command must appear before all other commands for a port, the defaults are only useful if all ports in the file are of the same type (this restriction may be lifted in a later version). All commands after a `port' command up to the next `port' command then describe that port. There are different types of ports; each type supports its own set of commands. Each command indicates which types of ports support it. There may be many ports with the same name; if a system requests a port by name then each port with that name will be tried until an unlocked one is found. `port STRING' Introduces and names a port. `type STRING' Define the type of port. The default is `modem'. If this command appears, it must immediately follow the `port' command. The type defines what commands are subsequently allowed. Currently the types are: `modem' For a modem hookup. `stdin' For a connection through standard input and standard output, as when `uucico' is run as a login shell. `direct' For a direct connection to another system. `tcp' For a connection using TCP. `tli' For a connection using TLI. `pipe' For a connection through a pipe running another program. `protocol STRING' Specify a list of protocols to use for this port. This is just like the corresponding command for a system (*note Protocol Selection::). A protocol list for a system takes precedence over a list for a port. `protocol-parameter CHARACTER STRINGS [ any type ]' The same command as the `protocol-parameter' command used for systems (*note Protocol Selection::). This one takes precedence. `seven-bit BOOLEAN [ any type ]' This is only used during protocol negotiation; if the argument is true, it forces the selection of a protocol which works across a seven-bit link. It does not prevent eight bit characters from being transmitted. The default is false. `reliable BOOLEAN [ any type ]' This is only used during protocol negotiation; if the argument is false, it forces the selection of a protocol which works across an unreliable communication link. The default is true. It would be more common to specify this for a dialer rather than a port. `half-duplex BOOLEAN [ any type ]' If the argument is true, it means that the port only supports half-duplex connections. This only affects bidirectional protocols, and causes them to not do bidirectional transfers. `device STRING [ modem, direct and tli only ]' Names the device associated with this port. If the device is not named, the port name is taken as the device. Device names are system dependent. On Unix, a modem or direct connection might be something like `/dev/ttyd0'; a TLI port might be `/dev/inet/tcp'. `speed NUMBER [modem and direct only ]' `baud NUMBER [ modem and direct only ]' The speed this port runs at. If a system specifies a speed but no port name, then all ports which match the speed will be tried in order. If the speed is not specified here and is not specified by the system, the natural speed of the port will be used by default. `speed-range NUMBER NUMBER [ modem only ]' `baud-range NUMBER NUMBER [ modem only ]' Specify a range of speeds this port can run at. The first number is the minimum speed, the second number is the maximum speed. These numbers will be used when matching a system which specifies a desired speed. The simple `speed' (or `baud') command is still used to determine the speed to run at if the system does not specify a speed. For example, the command `speed-range 300 19200' means that the port will match any system which uses a speed from 300 to 19200 baud (and will use the speed specified by the system); this could be combined with `speed 2400', which means that when this port is used with a system that does not specify a speed, the port will be used at 2400 baud. `carrier BOOLEAN [ modem and direct only ]' The argument indicates whether the port supports carrier. If a modem port does not support carrier, the carrier detect signal will never be required on this port, regardless of what the modem chat script indicates. The default for a modem port is true. If a direct port supports carrier, the port will be set to expect carrier whenever it is used. The default for a direct port is false. `hardflow BOOLEAN [ modem and direct only ]' The argument indicates whether the port supports hardware flow control. If it does not, hardware flow control will not be turned on for this port. The default is true. Hardware flow control is only supported on some systems. `dial-device STRING [ modem only ]' Dialing instructions should be output to the named device, rather than to the normal port device. The default is to output to the normal port device. `dialer STRING [ modem only ]' Name a dialer to use. The information is looked up in the dial file. There is no default. Some sort of dialer information must be specified to call out on a modem. `dialer STRING ... [ modem only ]' If more than one string follows the `dialer' command, the strings are treated as a command that might appear in the dial file (*note dial File::). If a dialer is named (by using the first form of this command, described just above), these commands are ignored. They may be used to specify dialer information directly in simple situations without needing to go to a separate file. There is no default. Some sort of dialer information must be specified to call out on a modem. `dialer-sequence STRINGS [ modem or tcp or tli only ]' Name a sequence of dialers and tokens (phone numbers) to use. The first argument names a dialer, and the second argument names a token. The third argument names another dialer, and so on. If there are an odd number of arguments, the phone number specified with a `phone' command in the system file is used as the final token. The token is what is used for `\D' or `\T' in the dialer chat script. If the token in this string is `\D', the system phone number will be used; if it is `\T', the system phone number will be used after undergoing dialcodes translation. A missing final token is taken as `\D'. This command currently does not work if `dial-device' is specified; to handle this correctly will require a more systematic notion of chat scripts. Moreover, the `complete' and `abort' chat scripts, the protocol parameters, and the `carrier' and `dtr-toggle' commands are ignored for all but the first dialer. This command basically lets you specify a sequence of chat scripts to use. For example, the first dialer might get you to a local network and the second dialer might describe how to select a machine from the local network. This lets you break your dialing sequence into simple modules, and may make it easier to share dialer entries between machines. This command is to only way to use a chat script with a TCP port. This can be useful when using a modem which is accessed via TCP. When this command is used with a TLI port, then if the first dialer is `TLI' or `TLIS' the first token is used as the address to connect to. If the first dialer is something else, or if there is no token, the address given by the `address' command is used (*note Placing the Call::). Escape sequences in the address are expanded as they are for chat script expect strings (*note Chat Scripts::). The different between `TLI' and `TLIS' is that the latter implies the command `stream true'. These contortions are all for HDB compatibility. Any subsequent dialers are treated as they are for a TCP port. `lockname STRING [ modem and direct only ]' Give the name to use when locking this port. On Unix, this is the name of the file that will be created in the lock directory. It is used as is, so on Unix it should generally start with `LCK..'. For example, if a single port were named both `/dev/ttycu0' and `/dev/tty0' (perhaps with different characteristics keyed on the minor device number), then the command `lockname LCK..ttycu0' could be used to force the latter to use the same lock file name as the former. `service STRING [ tcp only ]' Name the TCP port number to use. This may be a number. If not, it will be looked up in `/etc/services'. If this is not specified, the string `uucp' is looked up in `/etc/services'. If it is not found, port number 540 (the standard UUCP-over-TCP port number) will be used. `version STRING [ tcp only ]' Specify the IP version number to use. The default is `0', which permits any version. The other possible choices are `4', which requires `IPv4', or `6', which requires `IPv6'. Normally it is not necessary to use this command, but in some cases, as `IPv6' is rolled out across the Internet, it may be necessary to require UUCP to use a particular type of connection. `push STRINGS [ tli only ]' Give a list of modules to push on to the TLI stream. `stream BOOLEAN [ tli only ]' If this is true, and the `push' command was not used, the `tirdwr' module is pushed on to the TLI stream. `server-address STRING [ tli only ]' Give the address to use when running as a TLI server. Escape sequences in the address are expanded as they are for chat script expect strings (*note Chat Scripts::). The string is passed directly to the TLI `t_bind' function. The value needed may depend upon your particular TLI implementation. Check the manual pages, and, if necessary, try writing some sample programs. For AT&T 3B2 System V Release 3 using the Wollongong TCP/IP stack, which is probably typical, the format of TLI string is `SSPPIIII', where `SS' is the service number (for TCP, this is 2), `PP' is the TCP port number, and `IIII' is the Internet address. For example, to accept a connection from on port 540 from any interface, use `server-address \x00\x02\x02\x1c\x00\x00\x00\x00'. To only accept connections from a particular interface, replace the last four digits with the network address of the interface. (Thanks to Paul Pryor for the information in this paragraph). `command STRINGS [ pipe only ]' Give the command, with arguments, to run when using a pipe port type. When a port of this type is used, the command is executed and `uucico' communicates with it over a pipe. This permits `uucico' or `cu' to communicate with another system which can only be reached through some unusual means. A sample use might be `command /bin/rlogin -E -8 -l LOGIN SYSTEM'. The command is run with the full privileges of UUCP; it is responsible for maintaining security.  File: uucp.info, Node: dial File, Next: UUCP Over TCP, Prev: port File, Up: Configuration Files The Dialer Configuration File ============================= The dialer configuration files define dialers. By default there is a single dialer file, named `dial' in the directory NEWCONFIGDIR. This may be overridden by the `dialfile' command in the main configuration file; see *Note Configuration File Names::. Any commands in the file before the first `dialer' command specify defaults for all the dialers in the file. All commands after a `dialer' command up to the next `dialer' command are associated with the named dialer. `dialer STRING' Introduces and names a dialer. `chat STRINGS' `chat-timeout NUMBER' `chat-fail STRING' `chat-seven-bit BOOLEAN' `chat-program STRINGS' Specify a chat script to be used to dial the phone. This chat script is used before the login chat script in the `sys' file, if any (*note Logging In::). For full details on chat scripts, see *Note Chat Scripts::. The `uucico' daemon will sleep for one second between attempts to dial out on a modem. If your modem requires a longer wait period, you must start your chat script with delays (`\d' in a send string). The chat script will be read from and sent to the port specified by the `dial-device' command for the port, if there is one. The following escape addition escape sequences may appear in send strings: `\D' send phone number without dialcode translation `\T' send phone number with dialcode translation See the description of the dialcodes file (*note Configuration File Names::) for a description of dialcode translation. If both the port and the dialer support carrier, as set by the `carrier' command in the port file and the `carrier' command in the dialer file, then every chat script implicitly begins with `\M' and ends with `\m'. There is no default chat script for dialers. The following additional escape sequences may be used in `chat-program': `\D' phone number without dialcode translation `\T' phone number with dialcode translation If the program changes the port in any way (e.g., sets parity) the changes will be preserved during protocol negotiation, but once the protocol is selected it will change the port settings. `dialtone STRING' A string to output when dialing the phone number which causes the modem to wait for a secondary dial tone. This is used to translate the `=' character in a phone number. The default is a comma. `pause STRING' A string to output when dialing the phone number which causes the modem to wait for 1 second. This is used to translate the `-' character in a phone number. The default is a comma. `carrier BOOLEAN' An argument of true means that the dialer supports the modem carrier signal. After the phone number is dialed, `uucico' will require that carrier be on. One some systems, it will be able to wait for it. If the argument is false, carrier will not be required. The default is true. `carrier-wait NUMBER' If the port is supposed to wait for carrier, this may be used to indicate how many seconds to wait. The default is 60 seconds. Only some systems support waiting for carrier. `dtr-toggle BOOLEAN BOOLEAN' If the first argument is true, then DTR is toggled before using the modem. This is only supported on some systems and some ports. The second BOOLEAN need not be present; if it is, and it is true, the program will sleep for 1 second after toggling DTR. The default is to not toggle DTR. `complete-chat STRINGS' `complete-chat-timeout NUMBER' `complete-chat-fail STRING' `complete-chat-seven-bit BOOLEAN' `complete-chat-program STRINGS' These commands define a chat script (*note Chat Scripts::) which is run when a call is finished normally. This allows the modem to be reset. There is no default. No additional escape sequences may be used. `complete STRING' This is a simple use of `complete-chat'. It is equivalent to `complete-chat "" STRING'; this has the effect of sending STRING to the modem when a call finishes normally. `abort-chat STRINGS' `abort-chat-timeout NUMBER' `abort-chat-fail STRING' `abort-chat-seven-bit BOOLEAN' `abort-chat-program STRINGS' These commands define a chat script (*note Chat Scripts::) to be run when a call is aborted. They may be used to interrupt and reset the modem. There is no default. No additional escape sequences may be used. `abort STRING' This is a simple use of `abort-chat'. It is equivalent to `abort-chat "" STRING'; this has the effect of sending STRING to the modem when a call is aborted. `protocol-parameter CHARACTER STRINGS' Set protocol parameters, just like the `protocol-parameter' command in the system configuration file or the port configuration file; see *Note Protocol Selection::. These parameters take precedence, then those for the port, then those for the system. `seven-bit BOOLEAN' This is only used during protocol negotiation; if it is true, it forces selection of a protocol which works across a seven-bit link. It does not prevent eight bit characters from being transmitted. The default is false. It would be more common to specify this for a port than for a dialer. `reliable BOOLEAN' This is only used during protocol negotiation; if it is false, it forces selection of a protocol which works across an unreliable communication link. The default is true. `half-duplex BOOLEAN [ any type ]' If the argument is true, it means that the dialer only supports half-duplex connections. This only affects bidirectional protocols, and causes them to not do bidirectional transfers.  File: uucp.info, Node: UUCP Over TCP, Next: Security, Prev: dial File, Up: Configuration Files UUCP Over TCP ============= If your system has a Berkeley style socket library, or a System V style TLI interface library, you can compile the code to permit making connections over TCP. Specifying that a system should be reached via TCP is easy, but nonobvious. * Menu: * TCP Client:: Connecting to Another System Over TCP * TCP Server:: Running a TCP Server  File: uucp.info, Node: TCP Client, Next: TCP Server, Prev: UUCP Over TCP, Up: UUCP Over TCP Connecting to Another System Over TCP ------------------------------------- If you are using the new style configuration files (*note Configuration Files::), add the line `port type tcp' to the entry in the `sys' file. By default UUCP will get the port number by looking up `uucp' in `/etc/services'; if the `uucp' service is not defined, port 540 will be used. You can set the port number to use with the command `port service XXX', where XXX can be either a number or a name to look up in `/etc/services'. You can specify the address of the remote host with `address A.B.C'; if you don't give an address, the remote system name will be used. You should give an explicit chat script for the system when you use TCP; the default chat script begins with a carriage return, which will not work with some UUCP TCP servers. If you are using V2 configuration files, add a line like this to `L.sys': SYS Any TCP uucp HOST.DOMAIN chat-script This will make an entry for system SYS, to be called at any time, over TCP, using port number `uucp' (as found in `/etc/services'; this may be specified as a number), using remote host `HOST.DOMAIN', with some chat script. If you are using HDB configuration files, add a line like this to Systems: SYS Any TCP - HOST.DOMAIN chat-script and a line like this to `Devices': TCP uucp - - You only need one line in `Devices' regardless of how many systems you contact over TCP. This will make an entry for system SYS, to be called at any time, over TCP, using port number `uucp' (as found in `/etc/services'; this may be specified as a number), using remote host `HOST.DOMAIN', with some chat script.  File: uucp.info, Node: TCP Server, Prev: TCP Client, Up: UUCP Over TCP Running a TCP Server -------------------- The `uucico' daemon may be run as a TCP server. To use the default port number, which is a reserved port, `uucico' must be invoked by the superuser (or it must be set user ID to the superuser, but I don't recommend doing that). You must define a port, either using the port file (*note port File::), if you are using the new configuration method, or with an entry in `Devices' if you are using HDB; there is no way to define a port using V2. If you are using HDB the port must be named `TCP'; a line as shown above will suffice. You can then start `uucico' as `uucico -p TCP' (after the `-p', name the port; in HDB it must be `TCP'). This will wait for incoming connections, and fork off a child for each one. Each connection will be prompted with `login:' and `Password:'; the results will be checked against the UUCP (not the system) password file (*note Configuration File Names::). Another way to run a UUCP TCP server is to use the BSD `uucpd' program. Yet another way to run a UUCP TCP server is to use `inetd'. Arrange for `inetd' to start up `uucico' with the `-l' switch. This will cause `uucico' to prompt with `login:' and `Password:' and check the results against the UUCP (not the system) password file (you may want to also use the `-D' switch to avoid a fork, which in this case is unnecessary).  File: uucp.info, Node: Security, Prev: UUCP Over TCP, Up: Configuration Files Security ======== This discussion of UUCP security applies only to Unix. It is a bit cursory; suggestions for improvement are solicited. UUCP is traditionally not very secure. Taylor UUCP addresses some security issues, but is still far from being a secure system. If security is very important to you, then you should not permit any external access to your computer, including UUCP. Any opening to the outside world is a potential security risk. When local users use UUCP to transfer files, Taylor UUCP can do little to secure them from each other. You can allow somewhat increased security by putting the owner of the UUCP programs (normally `uucp') into a separate group; the use of this is explained in the following paragraphs, which refer to this separate group as `uucp-group'. When the `uucp' program is invoked to copy a file to a remote system, it will, by default, copy the file into the UUCP spool directory. When the `uux' program is used, the `-C' switch must be used to copy the file into the UUCP spool directory. In any case, once the file has been copied into the spool directory, other local users will not be able to access it. When a file is requested from a remote system, UUCP will only permit it to be placed in a directory which is writable by the requesting user. The directory must also be writable by UUCP. A local user can create a directory with a group of `uucp-group' and set the mode to permit group write access. This will allow the file be requested without permitting it to be viewed by any other user. There is no provision for security for `uucp' requests (as opposed to `uux' requests) made by a user on a remote system. A file sent over by a remote request may only be placed in a directory which is world writable, and the file will be world readable and writable. This will permit any local user to destroy or replace the contents of the file. A file requested by a remote system must be world readable, and the directory it is in must be world readable. Any local user will be able to examine, although not necessarily modify, the file before it is sent. There are some security holes and race conditions that apply to the above discussion which I will not elaborate on. They are not hidden from anybody who reads the source code, but they are somewhat technical and difficult (though scarcely impossible) to exploit. Suffice it to say that even under the best of conditions UUCP is not completely secure. For many sites, security from remote sites is a more important consideration. Fortunately, Taylor UUCP does provide some support in this area. The greatest security is provided by always dialing out to the other site. This prevents anybody from pretending to be the other site. Of course, only one side of the connection can do this. If remote dialins must be permitted, then it is best if the dialin line is used only for UUCP. If this is the case, then you should create a call-in password file (*note Configuration File Names::) and let `uucico' do its own login prompting. For example, to let remote sites log in on a port named `entry' in the port file (*note port File::), you might invoke `uucico -e -p entry'. This would cause `uucico' to enter an endless loop of login prompts and daemon executions. The advantage of this approach is that even if remote users break into the system by guessing or learning the password, they will only be able to do whatever `uucico' permits them to do. They will not be able to start a shell on your system. If remote users can dial in and log on to your system, then you have a security hazard more serious than that posed by UUCP. But then, you probably knew that already. Once your system has connected with the remote UUCP, there is a fair amount of control you can exercise. You can use the `remote-send' and `remote-receive' commands to control the directories the remote UUCP can access. You can use the `request' command to prevent the remote UUCP from making any requests of your system at all; however, if you do this it will not even be able to send you mail or news. If you do permit remote requests, you should be careful to restrict what commands may be executed at the remote system's request. The default is `rmail' and `rnews', which will suffice for most systems. If different remote systems call in and they must be granted different privileges (perhaps some systems are within the same organization and some are not) then the `called-login' command should be used for each system to require that they use different login names. Otherwise, it would be simple for a remote system to use the `myname' command and pretend to be a different system. The `sequence' command can be used to detect when one system pretended to be another, but, since the sequence numbers must be reset manually after a failed handshake, this can sometimes be more trouble than it's worth.  File: uucp.info, Node: Protocols, Next: Hacking, Prev: Configuration Files, Up: Top UUCP Protocol Internals *********************** This chapter describes how the various UUCP protocols work, and discusses some other internal UUCP issues. This chapter is quite technical. You do not need to understand it, or even read it, in order to use Taylor UUCP. It is intended for people who are interested in how the UUCP code works. The information in this chapter is posted monthly to the Usenet newsgroups `comp.mail.uucp', `news.answers', and `comp.answers'. The posting is available from any `news.answers' archive site, such as `rtfm.mit.edu'. If you plan to use this information to write a UUCP program, please make sure you get the most recent version of the posting, in case there have been any corrections. * Menu: * UUCP Protocol Sources:: Sources for UUCP Protocol Information * UUCP Grades:: UUCP Grades * UUCP Lock Files:: UUCP Lock Files * Execution File Format:: Execution File Format * UUCP Protocol:: UUCP Protocol * g Protocol:: g protocol * f Protocol:: f protocol * t Protocol:: t protocol * e Protocol:: e protocol * Big G Protocol:: G protocol * i Protocol:: i protocol * j Protocol:: j protocol * x Protocol:: x protocol * y Protocol:: y protocol * d Protocol:: d protocol * h Protocol:: h protocol * v Protocol:: v protocol  File: uucp.info, Node: UUCP Protocol Sources, Next: UUCP Grades, Prev: Protocols, Up: Protocols UUCP Protocol Sources ===================== "Unix-to-Unix Copy Program," said PDP-1. "You will never find a more wretched hive of bugs and flamers. We must be cautious." --DECWars I took a lot of the information from Jamie E. Hanrahan's paper in the Fall 1990 DECUS Symposium, and from `Managing UUCP and Usenet' by Tim O'Reilly and Grace Todino (with contributions by several other people). The latter includes most of the former, and is published by O'Reilly & Associates, Inc. 103 Morris Street, Suite A Sebastopol, CA 95472 It is currently in its tenth edition. The ISBN number is `0-937175-93-5'. Some information is originally due to a Usenet article by Chuck Wegrzyn. The information on execution files comes partially from Peter Honeyman. The information on the `g' protocol comes partially from a paper by G.L. Chesson of Bell Laboratories, partially from Jamie E. Hanrahan's paper, and partially from source code by John Gilmore. The information on the `f' protocol comes from the source code by Piet Berteema. The information on the `t' protocol comes from the source code by Rick Adams. The information on the `e' protocol comes from a Usenet article by Matthias Urlichs. The information on the `d' protocol comes from Jonathan Clark, who also supplied information about QFT. The UUPlus information comes straight from Christopher J. Ambler, of UUPlus Development; it applies to version 1.52 and up of the shareware version of UUPlus Utilities, called FSUUCP 1.52, but referred to in this article as UUPlus. Although there are few books about UUCP, there are many about networks and protocols in general. I recommend two non-technical books which describe the sorts of things that are available on the network: `The Whole Internet', by Ed Krol, and `Zen and the Art of the Internet', by Brendan P. Kehoe. Good technical discussions of networking issues can be found in `Internetworking with TCP/IP', by Douglas E. Comer and David L. Stevens and in `Design and Validation of Computer Protocols' by Gerard J. Holzmann.  File: uucp.info, Node: UUCP Grades, Next: UUCP Lock Files, Prev: UUCP Protocol Sources, Up: Protocols UUCP Grades =========== Modern UUCP packages support a priority grade for each command. The grades generally range from `A' (the highest) to `Z' followed by `a' to `z'. Some UUCP packages (including Taylor UUCP) also support `0' to `9' before `A'. Some UUCP packages may permit any ASCII character as a grade. On Unix, these grades are encoded in the name of the command file created by `uucp' or `uux'. A command file name generally has the form `C.nnnngssss' where `nnnn' is the remote system name for which the command is queued, `g' is a single character grade, and `ssss' is a four character sequence number. For example, a command file created for the system `airs' at grade `Z' might be named `C.airsZ2551'. The remote system name will be truncated to seven characters, to ensure that the command file name will fit in the 14 character file name limit of the traditional Unix file system. UUCP packages which have no other means of distinguishing which command files are intended for which systems thus require all systems they connect to to have names that are unique in the first seven characters. Some UUCP packages use a variant of this format which truncates the system name to six characters. HDB and Taylor UUCP use a different spool directory format, which allows up to fourteen characters to be used for each system name. The sequence number in the command file name may be a decimal integer, or it may be a hexadecimal integer, or it may contain any alphanumeric character. Different UUCP packages are different. Taylor UUCP uses any alphanumeric character. UUPlus Utilities (as FSUUCP, a shareware DOS based UUCP and news package) uses up to 8 characters for file names in the spool (this is a DOS file system limitation; actually, with the extension, 11 characters are available, but FSUUCP reserves that for future use). FSUUCP defaults mail to grade `D', and news to grade `N', except that when the grade of incoming mail can be determined, that grade is preserved if the mail is forwarded to another system. The default grades may be changed by editing the `LIB/MAILRC' file for mail, or the `UUPLUS.CFG' file for news. UUPC/extended for DOS, OS/2 and Windows NT handles mail at grade `C', news at grade `d', and file transfers at grade `n'. The UUPC/extended `UUCP' and `RMAIL' commands accept grades to override the default, the others do not. I do not know how command grades are handled in other non-Unix UUCP packages. Modern UUCP packages allow you to restrict file transfer by grade depending on the time of day. Typically this is done with a line in the `Systems' (or `L.sys') file like this: airs Any/Z,Any2305-0855 ... This allows grades `Z' and above to be transferred at any time. Lower grades may only be transferred at night. I believe that this grade restriction applies to local commands as well as to remote commands, but I am not sure. It may only apply if the UUCP package places the call, not if it is called by the remote system. Taylor UUCP can use the `timegrade' and `call-timegrade' commands to achieve the same effect. *Note When to Call::. It supports the above format when reading `Systems' or `L.sys'. UUPC/extended provides the `symmetricgrades' option to announce the current grade in effect when calling the remote system. UUPlus allows specification of the highest grade accepted on a per-call basis with the `-g' option in `UUCICO'. This sort of grade restriction is most useful if you know what grades are being used at the remote site. The default grades used depend on the UUCP package. Generally `uucp' and `uux' have different defaults. A particular grade can be specified with the `-g' option to `uucp' or `uux'. For example, to request execution of `rnews' on `airs' with grade `d', you might use something like uux -gd - airs!rnews < article Uunet queues up mail at grade `C', but increases the grade based on the size. News is queued at grade `d', and file transfers at grade `n'. The example above would allow mail (below some large size) to be received at any time, but would only permit news to be transferred at night.  File: uucp.info, Node: UUCP Lock Files, Next: Execution File Format, Prev: UUCP Grades, Up: Protocols UUCP Lock Files =============== This discussion applies only to Unix. I have no idea how UUCP locks ports on other systems. UUCP creates files to lock serial ports and systems. On most, if not all, systems, these same lock files are also used by `cu' to coordinate access to serial ports. On some systems `getty' also uses these lock files, often under the name `uugetty'. The lock file normally contains the process ID of the locking process. This makes it easy to determine whether a lock is still valid. The algorithm is to create a temporary file and then link it to the name that must be locked. If the link fails because a file with that name already exists, the existing file is read to get the process ID. If the process still exists, the lock attempt fails. Otherwise the lock file is deleted and the locking algorithm is retried. Older UUCP packages put the lock files in the main UUCP spool directory, `/usr/spool/uucp'. HDB UUCP generally puts the lock files in a directory of their own, usually `/usr/spool/locks' or `/etc/locks'. The original UUCP lock file format encodes the process ID as a four byte binary number. The order of the bytes is host-dependent. HDB UUCP stores the process ID as a ten byte ASCII decimal number, with a trailing newline. For example, if process 1570 holds a lock file, it would contain the eleven characters space, space, space, space, space, space, one, five, seven, zero, newline. Some versions of UUCP add a second line indicating which program created the lock (`uucp', `cu', or `getty/uugetty'). I have also seen a third type of UUCP lock file which does not contain the process ID at all. The name of the lock file is traditionally `LCK..' followed by the base name of the device. For example, to lock `/dev/ttyd0' the file `LCK..ttyd0' would be created. On SCO Unix, the last letter of the lock file name is always forced to lower case even if the device name ends with an upper case letter. System V Release 4 UUCP names the lock file using the major and minor device numbers rather than the device name. The file is named `LK.XXX.YYY.ZZZ', where XXX, YYY and ZZZ are all three digit decimal numbers. XXX is the major device number of the device holding the directory holding the device file (e.g., `/dev'). YYY is the major device number of the device file itself. ZZZ is the minor device number of the device file itself. If `s' holds the result of passing the device to the stat system call (e.g., `stat ("/dev/ttyd0", &s)'), the following line of C code will print out the corresponding lock file name: printf ("LK.%03d.%03d.%03d", major (s.st_dev), major (s.st_rdev), minor (s.st_rdev)); The advantage of this system is that even if there are several links to the same device, they will all use the same lock file name. When two or more instances of `uuxqt' are executing, some sort of locking is needed to ensure that a single execution job is only started once. I don't know how most UUCP packages deal with this. Taylor UUCP uses a lock file for each execution job. The name of the lock file is the same as the name of the `X.*' file, except that the initial `X' is changed to an `L'. The lock file holds the process ID as described above.  File: uucp.info, Node: Execution File Format, Next: UUCP Protocol, Prev: UUCP Lock Files, Up: Protocols Execution File Format ===================== UUCP `X.*' files control program execution. They are created by `uux'. They are transferred between systems just like any other file. The `uuxqt' daemon reads them to figure out how to execute the job requested by `uux'. An `X.*' file is simply a text file. The first character of each line is a command, and the remainder of the line supplies arguments. The following commands are defined: `C command' This gives the command to execute, including the program and all arguments. For example, `rmail ian@airs.com'. `U user system' This names the user who requested the command, and the system from which the request came. `I standard-input' This names the file from which standard input is taken. If no standard input file is given, the standard input will probably be attached to `/dev/null'. If the standard input file is not from the system on which the execution is to occur, it will also appear in an `F' command. `O standard-output [system]' This names the standard output file. The optional second argument names the system to which the file should be sent. If there is no second argument, the file should be created on the executing system. `F required-file [filename-to-use]' The `F' command can appear multiple times. Each `F' command names a file which must exist before the execution can proceed. This will usually be a file which is transferred from the system on which `uux' was executed, but it can also be a file from the local system or some other system. If the file is not from the local system, then the command will usually name a file in the spool directory. If the optional second argument appears, then the file should be copied to the execution directory under that name. This is necessary for any file other than the standard input file. If the standard input file is not from the local system, it will appear in both an `F' command and an `I' command. `R requestor-address' This is the address to which mail about the job should be sent. It is relative to the system named in the `U' command. If the `R' command does not appear, then mail is sent to the user named in the `U' command. `Z' This command takes no arguments. It means that a mail message should be sent if the command failed. This is the default behaviour for most modern UUCP packages, and for them the `Z' command does not actually do anything. `N' This command takes no arguments. It means that no mail message should be sent, even if the command failed. `n' This command takes no arguments. It means that a mail message should be sent if the command succeeded. Normally a message is sent only if the command failed. `B' This command takes no arguments. It means that the standard input should be returned with any error message. This can be useful in cases where the input would otherwise be lost. `e' This command takes no arguments. It means that the command should be processed with `/bin/sh'. For some packages this is the default anyhow. Most packages will refuse to execute complex commands or commands containing wildcards, because of the security holes this opens. `E' This command takes no arguments. It means that the command should be processed with the `execve' system call. For some packages this is the default anyhow. `M status-file' This command means that instead of mailing a message, the message should be copied to the named file on the system named by the `U' command. `Q' This command takes no arguments. It means that the string arguments to all the other commands are backslash quoted. Any backslash in one of the strings should be followed by either a backslash or three octal digits. The backslash quoting is interpreted as in a C string. If the `Q' command does not appear, backslashes in the strings are not treated specially. The `Q' command was introduced in Taylor UUCP version 1.07. `# comment' This command is ignored, as is any other unrecognized command. Here is an example. Given the following command executed on system test1 uux - test2!cat - test2!~ian/bar !qux '>~/gorp' (this is only an example, as most UUCP systems will not permit the cat command to be executed) Taylor UUCP will produce something like the following `X.' file: U ian test1 F D.test1N003r qux O /usr/spool/uucppublic/gorp test1 F D.test1N003s I D.test1N003s C cat - ~ian/bar qux The standard input will be read into a file and then transferred to the file `D.test1N003s' on system `test2'. The file `qux' will be transferred to `D.test1N003r' on system `test2'. When the command is executed, the latter file will be copied to the execution directory under the name `qux'. Note that since the file `~ian/bar' is already on the execution system, no action need be taken for it. The standard output will be collected in a file, then copied to the file `/usr/spool/uucppublic/gorp' on the system `test1'.  File: uucp.info, Node: UUCP Protocol, Next: g Protocol, Prev: Execution File Format, Up: Protocols UUCP Protocol ============= The UUCP protocol is a conversation between two UUCP packages. A UUCP conversation consists of three parts: an initial handshake, a series of file transfer requests, and a final handshake. * Menu: * The Initial Handshake:: The Initial Handshake * UUCP Protocol Commands:: UUCP Protocol Commands * The Final Handshake:: The Final Handshake uucp-1.07/uucp.info-60000664000076400007640000013047507665322530010126 This is uucp.info, produced by makeinfo version 4.1 from uucp.texi. START-INFO-DIR-ENTRY * UUCP: (uucp). Transfer mail and news across phone lines. END-INFO-DIR-ENTRY This file documents Taylor UUCP, version 1.07. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "Copying" are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "Copying" may be included in a translation approved by the author instead of in the original English.  File: uucp.info, Node: The Initial Handshake, Next: UUCP Protocol Commands, Prev: UUCP Protocol, Up: UUCP Protocol The Initial Handshake --------------------- Before the initial handshake, the caller will usually have logged in the called machine and somehow started the UUCP package there. On Unix this is normally done by setting the shell of the login name used to `/usr/lib/uucp/uucico'. All messages in the initial handshake begin with a `^P' (a byte with the octal value `\020') and end with a null byte (`\000'). A few systems end these messages with a line feed character (`\012') instead of a null byte; the examples below assume a null byte is being used. Some options below are supported by QFT, which stands for Queued File Transfer, and is (or was) an internal Bell Labs version of UUCP. Taylor UUCP size negotiation was introduced by Taylor UUCP, and is also supported by DOS based UUPlus and Amiga based wUUCP and UUCP-1.17. The initial handshake goes as follows. It is begun by the called machine. called: `\020Shere=hostname\000' The hostname is the UUCP name of the called machine. Older UUCP packages do not output it, and simply send `\020Shere\000'. caller: `\020Shostname options\000' The hostname is the UUCP name of the calling machine. The following options may appear (or there may be none): `-QSEQ' Report sequence number for this conversation. The sequence number is stored at both sites, and incremented after each call. If there is a sequence number mismatch, something has gone wrong (somebody may have broken security by pretending to be one of the machines) and the call is denied. If the sequence number changes on one of the machines, perhaps because of an attempted breakin or because a disk backup was restored, the sequence numbers on the two machines must be reconciled manually. `-xLEVEL' Requests the called system to set its debugging level to the specified value. This is not supported by all systems. `-pGRADE' `-vgrade=GRADE' Requests the called system to only transfer files of the specified grade or higher. This is not supported by all systems. Some systems support `-p', some support `-vgrade='. UUPlus allows either `-p' or `-v' to be specified on a per-system basis in the `SYSTEMS' file (`gradechar' option). `-R' Indicates that the calling UUCP understands how to restart failed file transmissions. Supported only by System V Release 4 UUCP, QFT, and Taylor UUCP. `-ULIMIT' Reports the ulimit value of the calling UUCP. The limit is specified as a base 16 number in C notation (e.g., `-U0x1000000'). This number is the number of 512 byte blocks in the largest file which the calling UUCP can create. The called UUCP may not transfer a file larger than this. Supported only by System V Release 4 UUCP, QFT and UUPlus. UUPlus reports the lesser of the available disk space on the spool directory drive and the ulimit variable in `UUPLUS.CFG'. Taylor UUCP understands this option, but does not generate it. `-N[NUMBER]' Indicates that the calling UUCP understands the Taylor UUCP size negotiation extension. Not supported by traditional UUCP packages. Supported by UUPlus. The optional number is a bitmask of features supported by the calling UUCP, and is described below. called: `\020ROK\000' There are actually several possible responses. `ROK' The calling UUCP is acceptable, and the handshake proceeds to the protocol negotiation. Some options may also appear; see below. `ROKN[NUMBER]' The calling UUCP is acceptable, it specified `-N', and the called UUCP also understands the Taylor UUCP size limiting extensions. The optional number is a bitmask of features supported by the called UUCP, and is described below. `RLCK' The called UUCP already has a lock for the calling UUCP, which normally indicates the two machines are already communicating. `RCB' The called UUCP will call back. This may be used to avoid impostors (but only one machine out of each pair should call back, or no conversation will ever begin). `RBADSEQ' The call sequence number is wrong (see the `-Q' discussion above). `RLOGIN' The calling UUCP is using the wrong login name. `RYou are unknown to me' The calling UUCP is not known to the called UUCP, and the called UUCP does not permit connections from unknown systems. Some versions of UUCP just drop the line rather than sending this message. If the response is `ROK', the following options are supported by System V Release 4 UUCP and QFT. `-R' The called UUCP knows how to restart failed file transmissions. `-ULIMIT' Reports the ulimit value of the called UUCP. The limit is specified as a base 16 number in C notation. This number is the number of 512 byte blocks in the largest file which the called UUCP can create. The calling UUCP may not send a file larger than this. Also supported by UUPlus. Taylor UUCP understands this option, but does not generate it. `-xLEVEL' I'm not sure just what this means. It may request the calling UUCP to set its debugging level to the specified value. If the response is not `ROK' (or `ROKN') both sides hang up the phone, abandoning the call. called: `\020Pprotocols\000' Note that the called UUCP outputs two strings in a row. The protocols string is a list of UUCP protocols supported by the caller. Each UUCP protocol has a single character name. These protocols are discussed in more detail later in this document. For example, the called UUCP might send `\020Pgf\000'. caller: `\020Uprotocol\000' The calling UUCP selects which protocol to use out of the protocols offered by the called UUCP. If there are no mutually supported protocols, the calling UUCP sends `\020UN\000' and both sides hang up the phone. Otherwise the calling UUCP sends something like `\020Ug\000'. Most UUCP packages will consider each locally supported protocol in turn and select the first one supported by the called UUCP. With some versions of HDB UUCP, this can be modified by giving a list of protocols after the device name in the `Devices' file or the `Systems' file. For example, to select the `e' protocol in `Systems', airs Any ACU,e ... or in Devices, ACU,e ttyXX ... Taylor UUCP provides the `protocol' command which may be used either for a system (*note Protocol Selection::) or a port (*note port File::). UUPlus allows specification of the protocol string on a per-system basis in the `SYSTEMS' file. The optional number following a `-N' sent by the calling system, or an `ROKN' sent by the called system, is a bitmask of features supported by the UUCP package. The optional number was introduced in Taylor UUCP version 1.04. The number is sent as an octal number with a leading zero. The following bits are currently defined. A missing number should be taken as `011'. `01' UUCP supports size negotiation. `02' UUCP supports file restart. `04' UUCP supports the `E' command. `010' UUCP requires the file size in the `S' and `R' commands to be in base 10. This bit is used by default if no number appears, but should not be explicitly sent. `020' UUCP expects a dummy string between the notify field and the size field in an `S' command. This is true of SVR4 UUCP. This bit should not be used. `040' UUCP supports the `q' option in the `S', `R', `X', and `E' commands. After the protocol has been selected and the initial handshake has been completed, both sides turn on the selected protocol. For some protocols (notably `g') a further handshake is done at this point.  File: uucp.info, Node: UUCP Protocol Commands, Next: The Final Handshake, Prev: The Initial Handshake, Up: UUCP Protocol UUCP Protocol Commands ---------------------- Each protocol supports a method for sending a command to the remote system. This method is used to transmit a series of commands between the two UUCP packages. At all times, one package is the master and the other is the slave. Initially, the calling UUCP is the master. If a protocol error occurs during the exchange of commands, both sides move immediately to the final handshake. The master will send one of five commands: `S', `R', `X', `E', or `H'. Any file name referred to below is either an absolute file name beginning with `/', a public directory file name beginning with `~/', a file name relative to a user's home directory beginning with `~USER/', or a spool directory file name. File names in the spool directory are not absolute, but instead are converted to file names within the spool directory by UUCP. They always begin with `C.' (for a command file created by `uucp' or `uux'), `D.' (for a data file created by `uucp', `uux' or by an execution, or received from another system for an execution), or `X.' (for an execution file created by `uux' or received from another system). All the commands other than the `H' command support options. The `q' option indicates that the command argument strings are backslash quoted. If the `q' option appears, then any backslash in one of the arguments should be followed by either a backslash or three octal digits. The backslash quoting is interpreted as in a C string. If the `q' option does not appear, backslashes in the strings are not treated specially. The `q' option was introduced in Taylor UUCP version 1.07. * Menu: * The S Command:: The S Command * The R Command:: The R Command * The X Command:: The X Command * The E Command:: The E Command * The H Command:: The H Command  File: uucp.info, Node: The S Command, Next: The R Command, Prev: UUCP Protocol Commands, Up: UUCP Protocol Commands The S Command ............. master: `S FROM TO USER -OPTIONS TEMP MODE NOTIFY SIZE' The `S' and the `-' are literal characters. This is a request by the master to send a file to the slave. FROM The name of the file to send. If the `C' option does not appear in OPTIONS, the master will actually open and send this file. Otherwise the file has been copied to the spool directory, where it is named TEMP. The slave ignores this field unless TO is a directory, in which case the basename of FROM will be used as the file name. If FROM is a spool directory filename, it must be a data file created for or by an execution, and must begin with `D.'. TO The name to give the file on the slave. If this field names a directory the file is placed within that directory with the basename of FROM. A name ending in `/' is taken to be a directory even if one does not already exist with that name. If TO begins with `X.', an execution file will be created on the slave. Otherwise, if TO begins with `D.' it names a data file to be used by some execution file. Otherwise, TO should not be in the spool directory. USER The name of the user who requested the transfer. OPTIONS A list of options to control the transfer. The following options are defined (all options are single characters): `C' The file has been copied to the spool directory (the master should use TEMP rather than FROM). `c' The file has not been copied to the spool directory (this is the default). `d' The slave should create directories as necessary (this is the default). `f' The slave should not create directories if necessary, but should fail the transfer instead. `m' The master should send mail to USER when the transfer is complete. `n' The slave should send mail to NOTIFY when the transfer is complete. `q' Backslash quoting is applied to the FROM, TO, USER, and NOTIFY arguments. *Note UUCP Protocol Commands::. This option was introduced in Taylor UUCP version 1.07. TEMP If the `C' option appears in OPTIONS, this names the file to be sent. Otherwise if FROM is in the spool directory, TEMP is the same as FROM. Otherwise TEMP may be a dummy string, such as `D.0'. After the transfer has been succesfully completed, the master will delete the file TEMP. MODE This is an octal number giving the mode of the file on the master. If the file is not in the spool directory, the slave will always create it with mode 0666, except that if (MODE & 0111) is not zero (the file is executable), the slave will create the file with mode 0777. If the file is in the spool directory, some UUCP packages will use the algorithm above and some will always create the file with mode 0600. This field is ignored by UUPlus, since it is meaningless on DOS; UUPlus uses 0666 for outgoing files. NOTIFY This field may not be present, and in any case is only meaningful if the `n' option appears in OPTIONS. If the `n' option appears, then, when the transfer is successfully completed, the slave will send mail to NOTIFY, which must be a legal mailing address on the slave. If a SIZE field will appear but the `n' option does not appear, NOTIFY will always be present, typically as the string `dummy' or simply a pair of double quotes. SIZE This field is only present when doing Taylor UUCP or SVR4 UUCP size negotiation. It is the size of the file in bytes. Taylor UUCP version 1.03 sends the size as a decimal integer, while versions 1.04 and up, and all other UUCP packages that support size negotiation, send the size in base 16 with a leading 0x. The slave then responds with an `S' command response. `SY START' The slave is willing to accept the file, and file transfer begins. The START field will only be present when using file restart. It specifies the byte offset into the file at which to start sending. If this is a new file, START will be 0x0. `SN2' The slave denies permission to transfer the file. This can mean that the destination directory may not be accessed, or that no requests are permitted. It implies that the file transfer will never succeed. `SN4' The slave is unable to create the necessary temporary file. This implies that the file transfer might succeed later. `SN6' This is only used by Taylor UUCP size negotiation. It means that the slave considers the file too large to transfer at the moment, but it may be possible to transfer it at some other time. `SN7' This is only used by Taylor UUCP size negotiation. It means that the slave considers the file too large to ever transfer. `SN8' This is only used by Taylor UUCP. It means that the file was already received in a previous conversation. This can happen if the receive acknowledgement was lost after it was sent by the receiver but before it was received by the sender. `SN9' This is only used by Taylor UUCP (versions 1.05 and up) and UUPlus (versions 2.0 and up). It means that the remote system was unable to open another channel (see the discussion of the `i' protocol for more information about channels). This implies that the file transfer might succeed later. `SN10' This is reportedly used by SVR4 UUCP to mean that the file size is too large. If the slave responds with `SY', a file transfer begins. When the file transfer is complete, the slave sends a `C' command response. `CY' The file transfer was successful. `CYM' The file transfer was successful, and the slave wishes to become the master; the master should send an `H' command, described below. `CN5' The temporary file could not be moved into the final location. This implies that the file transfer will never succeed. After the `C' command response has been received (in the `SY' case) or immediately (in an `SN' case) the master will send another command.  File: uucp.info, Node: The R Command, Next: The X Command, Prev: The S Command, Up: UUCP Protocol Commands The R Command ............. master: `R FROM TO USER -OPTIONS SIZE' The `R' and the `-' are literal characters. This is a request by the master to receive a file from the slave. I do not know how SVR4 UUCP or QFT implement file transfer restart in this case. FROM This is the name of the file on the slave which the master wishes to receive. It must not be in the spool directory, and it may not contain any wildcards. TO This is the name of the file to create on the master. I do not believe that it can be a directory. It may only be in the spool directory if this file is being requested to support an execution either on the master or on some system other than the slave. USER The name of the user who requested the transfer. OPTIONS A list of options to control the transfer. The following options are defined (all options are single characters): `d' The master should create directories as necessary (this is the default). `f' The master should not create directories if necessary, but should fail the transfer instead. `m' The master should send mail to USER when the transfer is complete. `q' Backslash quoting is applied to the FROM, TO, and USER arguments. *Note UUCP Protocol Commands::. This option was introduced in Taylor UUCP version 1.07. SIZE This only appears if Taylor UUCP size negotiation is being used. It specifies the largest file which the master is prepared to accept (when using SVR4 UUCP or QFT, this was specified in the `-U' option during the initial handshake). The slave then responds with an `R' command response. UUPlus does not support `R' requests, and always responds with `RN2'. `RY MODE [SIZE]' The slave is willing to send the file, and file transfer begins. The MODE argument is the octal mode of the file on the slave. The master treats this just as the slave does the MODE argument in the send command, q.v. I am told that SVR4 UUCP sends a trailing SIZE argument. For some versions of BSD UUCP, the MODE argument may have a trailing `M' character (e.g., `RY 0666M'). This means that the slave wishes to become the master. `RN2' The slave is not willing to send the file, either because it is not permitted or because the file does not exist. This implies that the file request will never succeed. `RN6' This is only used by Taylor UUCP size negotiation. It means that the file is too large to send, either because of the size limit specifies by the master or because the slave considers it too large. The file transfer might succeed later, or it might not (this may be cleared up in a later release of Taylor UUCP). `RN9' This is only used by Taylor UUCP (versions 1.05 and up) and FSUUCP (versions 1.5 and up). It means that the remote system was unable to open another channel (see the discussion of the `i' protocol for more information about channels). This implies that the file transfer might succeed later. If the slave responds with `RY', a file transfer begins. When the file transfer is complete, the master sends a `C' command. The slave pretty much ignores this, although it may log it. `CY' The file transfer was successful. `CN5' The temporary file could not be moved into the final location. After the `C' command response has been sent (in the `RY' case) or immediately (in an `RN' case) the master will send another command.  File: uucp.info, Node: The X Command, Next: The E Command, Prev: The R Command, Up: UUCP Protocol Commands The X Command ............. master: `X FROM TO USER -OPTIONS' The `X' and the `-' are literal characters. This is a request by the master to, in essence, execute uucp on the slave. The slave should execute `uucp FROM TO'. FROM This is the name of the file or files on the slave which the master wishes to transfer. Any wildcards are expanded on the slave. If the master is requesting that the files be transferred to itself, the request would normally contain wildcard characters, since otherwise an `R' command would suffice. The master can also use this command to request that the slave transfer files to a third system. TO This is the name of the file or directory to which the files should be transferred. This will normally use a UUCP name. For example, if the master wishes to receive the files itself, it would use `master!path'. USER The name of the user who requested the transfer. OPTIONS A list of options to control the transfer. As far as I know, only one option is defined: `q' Backslash quoting is applied to the FROM, TO, and USER arguments. *Note UUCP Protocol Commands::. This option was introduced in Taylor UUCP version 1.07. The slave then responds with an `X' command response. FSUUCP does not support `X' requests, and always responds with `XN'. `XY' The request was accepted, and the appropriate file transfer commands have been queued up for later processing. `XN' The request was denied. No particular reason is given. In either case, the master will then send another command.  File: uucp.info, Node: The E Command, Next: The H Command, Prev: The X Command, Up: UUCP Protocol Commands The E Command ............. master: `E FROM TO USER -OPTIONS TEMP MODE NOTIFY SIZE COMMAND' The `E' command is only supported by Taylor UUCP 1.04 and up. It is used to make an execution request without requiring a separate `X.*' file. *Note Execution File Format::. It is only used when the command to be executed requires a single input file which is passed to it as standard input. All the fields have the same meaning as they do for an `S' command, except for OPTIONS and COMMAND. OPTIONS A list of options to control the transfer. The following options are defined (all options are single characters): `C' The file has been copied to the spool directory (the master should use TEMP rather than FROM). `c' The file has not been copied to the spool directory (this is the default). `N' No mail message should be sent, even if the command fails. This is the equivalent of the `N' command in an `X.*' file. `Z' A mail message should be sent if the command fails (this is generally the default in any case). This is the equivalent of the `Z' command in an `X.*' file. `R' Mail messages about the execution should be sent to the address in the NOTIFY field. This is the equivalent of the `R' command in an `X.*' file. `e' The execution should be done with `/bin/sh'. This is the equivalent of the `e' command in an `X.*' file. `q' Backslash quoting is applied to the FROM, TO, USER, and NOTIFY arguments. *Note UUCP Protocol Commands::. This option was introduced in Taylor UUCP version 1.07. Note that the COMMAND argument is not backslash quoted--that argument is defined as the remainder of the line, and so is already permitted to contain any character. COMMAND The command which should be executed. This is the equivalent of the `C' command in an `X.*' file. The slave then responds with an `E' command response. These are the same as the `S' command responses, but the initial character is `E' rather than `S'. If the slave responds with `EY', the file transfer begins. When the file transfer is complete, the slave sends a `C' command response, just as for the `S' command. After a successful file transfer, the slave is responsible for arranging for the command to be executed. The transferred file is passed as standard input, as though it were named in the `I' and `F' commands of an `X.*' file. After the `C' command response has been received (in the `EY' case) or immediately (in an `EN' case) the master will send another command.  File: uucp.info, Node: The H Command, Prev: The E Command, Up: UUCP Protocol Commands The H Command ............. master: `H' This is used by the master to hang up the connection. The slave will respond with an `H' command response. `HY' The slave agrees to hang up the connection. In this case the master sends another `HY' command. In some UUCP packages the slave will then send a third `HY' command. At this point the protocol is shut down, and the final handshake is begun. `HN' The slave does not agree to hang up. In this case the master and the slave exchange roles. The next command will be sent by the former slave, which is the new master. The roles may be reversed several times during a single connection.  File: uucp.info, Node: The Final Handshake, Prev: UUCP Protocol Commands, Up: UUCP Protocol The Final Handshake ------------------- After the protocol has been shut down, the final handshake is performed. This handshake has no real purpose, and some UUCP packages simply drop the connection rather than do it (in fact, some will drop the connection immediately after both sides agree to hangup, without even closing down the protocol). caller: `\020OOOOOO\000' called: `\020OOOOOOO\000' That is, the calling UUCP sends six `O' characters and the called UUCP replies with seven `O' characters. Some UUCP packages always send six `O' characters.  File: uucp.info, Node: g Protocol, Next: f Protocol, Prev: UUCP Protocol, Up: Protocols UUCP `g' Protocol ================= The `g' protocol is a packet based flow controlled error correcting protocol that requires an eight bit clear connection. It is the original UUCP protocol, and is supported by all UUCP implementations. Many implementations of it are only able to support small window and packet sizes, specifically a window size of 3 and a packet size of 64 bytes, but the protocol itself can support up to a window size of 7 and a packet size of 4096 bytes. Complaints about the inefficiency of the `g' protocol generally refer to specific implementations, rather than to the correctly implemented protocol. The `g' protocol was originally designed for general packet drivers, and thus contains some features that are not used by UUCP, including an alternate data channel and the ability to renegotiate packet and window sizes during the communication session. The `g' protocol is spoofed by many Telebit modems. When spoofing is in effect, each Telebit modem uses the `g' protocol to communicate with the attached computer, but the data between the modems is sent using a Telebit proprietary error correcting protocol. This allows for very high throughput over the Telebit connection, which, because it is half-duplex, would not normally be able to handle the `g' protocol very well at all. When a Telebit is spoofing the `g' protocol, it forces the packet size to be 64 bytes and the window size to be 3. This discussion of the `g' protocol explains how it works, but does not discuss useful error handling techniques. Some discussion of this can be found in Jamie E. Hanrahan's paper, cited above (*note UUCP Protocol Sources::). All `g' protocol communication is done with packets. Each packet begins with a six byte header. Control packets consist only of the header. Data packets contain additional data. The header is as follows: `\020' Every packet begins with a `^P'. K (1 <= K <= 9) The K value is always 9 for a control packet. For a data packet, the K value indicates how much data follows the six byte header. The amount of data is 2 ** (K + 4), where ** indicates exponentiation. Thus a K value of 1 means 32 data bytes and a K value of 8 means 4096 data bytes. The K value for a data packet must be between 1 and 8 inclusive. checksum low byte checksum high byte The checksum value is described below. control byte The control byte indicates the type of packet, and is described below. xor byte This byte is the xor of K, the checksum low byte, the checksum high byte and the control byte (i.e., the second, third, fourth and fifth header bytes). It is used to ensure that the header data is valid. The control byte in the header is composed of three bit fields, referred to here as TT (two bits), XXX (three bits) and YYY (three bits). The control is TTXXXYYY, or `(TT << 6) + (XXX << 3) + YYY'. The TT field takes on the following values: `0' This is a control packet. In this case the K byte in the header must be 9. The XXX field indicates the type of control packet; these types are described below. `1' This is an alternate data channel packet. This is not used by UUCP. `2' This is a data packet, and the entire contents of the attached data field (whose length is given by the K byte in the header) are valid. The XXX and YYY fields are described below. `3' This is a short data packet. Let the length of the data field (as given by the K byte in the header) be L. Let the first byte in the data field be B1. If B1 is less than 128 (if the most significant bit of B1 is 0), then there are `L - B1' valid bytes of data in the data field, beginning with the second byte. If `B1 >= 128', let B2 be the second byte in the data field. Then there are `L - ((B1 & 0x7f) + (B2 << 7))' valid bytes of data in the data field, beginning with the third byte. In all cases L bytes of data are sent (and all data bytes participate in the checksum calculation) but some of the trailing bytes may be dropped by the receiver. The XXX and YYY fields are described below. In a data packet (short or not) the XXX field gives the sequence number of the packet. Thus sequence numbers can range from 0 to 7, inclusive. The YYY field gives the sequence number of the last correctly received packet. Each communication direction uses a window which indicates how many unacknowledged packets may be transmitted before waiting for an acknowledgement. The window may range from 1 to 7, and may be different in each direction. For example, if the window is 3 and the last packet acknowledged was packet number 6, packet numbers 7, 0 and 1 may be sent but the sender must wait for an acknowledgement before sending packet number 2. This acknowledgement could come as the YYY field of a data packet, or as the YYY field of a `RJ' or `RR' control packet (described below). Each packet must be transmitted in order (the sender may not skip sequence numbers). Each packet must be acknowledged, and each packet must be acknowledged in order. In a control packet, the XXX field takes on the following values: 1 `CLOSE' The connection should be closed immediately. This is typically sent when one side has seen too many errors and wants to give up. It is also sent when shutting down the protocol. If an unexpected `CLOSE' packet is received, a `CLOSE' packet should be sent in reply and the `g' protocol should halt, causing UUCP to enter the final handshake. 2 `RJ' or `NAK' The last packet was not received correctly. The YYY field contains the sequence number of the last correctly received packet. 3 `SRJ' Selective reject. The YYY field contains the sequence number of a packet that was not received correctly, and should be retransmitted. This is not used by UUCP, and most implementations will not recognize it. 4 `RR' or `ACK' Packet acknowledgement. The YYY field contains the sequence number of the last correctly received packet. 5 `INITC' Third initialization packet. The YYY field contains the maximum window size to use. 6 `INITB' Second initialization packet. The YYY field contains the packet size to use. It requests a size of 2 ** (YYY + 5). Note that this is not the same coding used for the K byte in the packet header (it is 1 less). Most UUCP implementations that request a packet size larger than 64 bytes can handle any packet size up to that specified. 7 `INITA' First initialization packet. The YYY field contains the maximum window size to use. To compute the checksum, call the control byte (the fifth byte in the header) C. The checksum of a control packet is simply `0xaaaa - C'. The checksum of a data packet is `0xaaaa - (CHECK ^ C)', where `^' denotes exclusive or, and CHECK is the result of the following routine as run on the contents of the data field (every byte in the data field participates in the checksum, even for a short data packet). Below is the routine used by an early version of Taylor UUCP; it is a slightly modified version of a routine which John Gilmore patched from G.L. Chesson's original paper. The `z' argument points to the data and the `c' argument indicates how much data there is. int igchecksum (z, c) register const char *z; register int c; { register unsigned int ichk1, ichk2; ichk1 = 0xffff; ichk2 = 0; do { register unsigned int b; /* Rotate ichk1 left. */ if ((ichk1 & 0x8000) == 0) ichk1 <<= 1; else { ichk1 <<= 1; ++ichk1; } /* Add the next character to ichk1. */ b = *z++ & 0xff; ichk1 += b; /* Add ichk1 xor the character position in the buffer counting from the back to ichk2. */ ichk2 += ichk1 ^ c; /* If the character was zero, or adding it to ichk1 caused an overflow, xor ichk2 to ichk1. */ if (b == 0 || (ichk1 & 0xffff) < b) ichk1 ^= ichk2; } while (--c > 0); return ichk1 & 0xffff; } When the `g' protocol is started, the calling UUCP sends an `INITA' control packet with the window size it wishes the called UUCP to use. The called UUCP responds with an `INITA' packet with the window size it wishes the calling UUCP to use. Pairs of `INITB' and `INITC' packets are then similarly exchanged. When these exchanges are completed, the protocol is considered to have been started. Note that the window and packet sizes are not a negotiation. Each system announces the window and packet size which the other system should use. It is possible that different window and packet sizes will be used in each direction. The protocol works this way on the theory that each system knows how much data it can accept without getting overrun. Therefore, each system tells the other how much data to send before waiting for an acknowledgement. When a UUCP package transmits a command, it sends one or more data packets. All the data packets will normally be complete, although some UUCP packages may send the last one as a short packet. The command string is sent with a trailing null byte, to let the receiving package know when the command is finished. Some UUCP packages require the last byte of the last packet sent to be null, even if the command ends earlier in the packet. Some packages may require all the trailing bytes in the last packet to be null, but I have not confirmed this. When a UUCP package sends a file, it will send a sequence of data packets. The end of the file is signalled by a short data packet containing zero valid bytes (it will normally be preceeded by a short data packet containing the last few bytes in the file). Note that the sequence numbers cover the entire communication session, including both command and file data. When the protocol is shut down, each UUCP package sends a `CLOSE' control packet.  File: uucp.info, Node: f Protocol, Next: t Protocol, Prev: g Protocol, Up: Protocols UUCP `f' Protocol ================= The `f' protocol is a seven bit protocol which checksums an entire file at a time. It only uses the characters between `\040' and `\176' (ASCII `space' and `~') inclusive, as well as the carriage return character. It can be very efficient for transferring text only data, but it is very inefficient at transferring eight bit data (such as compressed news). It is not flow controlled, and the checksum is fairly insecure over large files, so using it over a serial connection requires handshaking (XON/XOFF can be used) and error correcting modems. Some people think it should not be used even under those circumstances. I believe that the `f' protocol originated in BSD versions of UUCP. It was originally intended for transmission over X.25 PAD links. The `f' protocol has no startup or finish protocol. However, both sides typically sleep for a couple of seconds before starting up, because they switch the terminal into XON/XOFF mode and want to allow the changes to settle before beginning transmission. When a UUCP package transmits a command, it simply sends a string terminated by a carriage return. When a UUCP package transmits a file, each byte B of the file is translated according to the following table: 0 <= B <= 037: 0172, B + 0100 (0100 to 0137) 040 <= B <= 0171: B ( 040 to 0171) 0172 <= B <= 0177: 0173, B - 0100 ( 072 to 077) 0200 <= B <= 0237: 0174, B - 0100 (0100 to 0137) 0240 <= B <= 0371: 0175, B - 0200 ( 040 to 0171) 0372 <= B <= 0377: 0176, B - 0300 ( 072 to 077) That is, a byte between `\040' and `\171' inclusive is transmitted as is, and all other bytes are prefixed and modified as shown. When all the file data is sent, a seven byte sequence is sent: two bytes of `\176' followed by four ASCII bytes of the checksum as printed in base 16 followed by a carriage return. For example, if the checksum was 0x1234, this would be sent: `\176\1761234\r'. The checksum is initialized to 0xffff. For each byte that is sent it is modified as follows (where B is the byte before it has been transformed as described above): /* Rotate the checksum left. */ if ((ichk & 0x8000) == 0) ichk <<= 1; else { ichk <<= 1; ++ichk; } /* Add the next byte into the checksum. */ ichk += B; When the receiving UUCP sees the checksum, it compares it against its own calculated checksum and replies with a single character followed by a carriage return. `G' The file was received correctly. `R' The checksum did not match, and the file should be resent from the beginning. `Q' The checksum did not match, but too many retries have occurred and the communication session should be abandoned. The sending UUCP checks the returned character and acts accordingly.  File: uucp.info, Node: t Protocol, Next: e Protocol, Prev: f Protocol, Up: Protocols UUCP `t' Protocol ================= The `t' protocol is intended for use on links which provide reliable end-to-end connections, such as TCP. It does no error checking or flow control, and requires an eight bit clear channel. I believe the `t' protocol originated in BSD versions of UUCP. When a UUCP package transmits a command, it first gets the length of the command string, C. It then sends `((C / 512) + 1) * 512' bytes (the smallest multiple of 512 which can hold C bytes plus a null byte) consisting of the command string itself followed by trailing null bytes. When a UUCP package sends a file, it sends it in blocks. Each block contains at most 1024 bytes of data. Each block consists of four bytes containing the amount of data in binary (most significant byte first, the same format as used by the Unix function `htonl') followed by that amount of data. The end of the file is signalled by a block containing zero bytes of data.  File: uucp.info, Node: e Protocol, Next: Big G Protocol, Prev: t Protocol, Up: Protocols UUCP `e' Protocol ================= The `e' protocol is similar to the `t' protocol. It does no flow control or error checking and is intended for use over networks providing reliable end-to-end connections, such as TCP. The `e' protocol originated in versions of HDB UUCP. When a UUCP package transmits a command, it simply sends the command as an ASCII string terminated by a null byte. When a UUCP package transmits a file, it sends the complete size of the file as an ASCII decimal number. The ASCII string is padded out to 20 bytes with null bytes (i.e. if the file is 1000 bytes long, it sends `1000\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'). It then sends the entire file.  File: uucp.info, Node: Big G Protocol, Next: i Protocol, Prev: e Protocol, Up: Protocols UUCP `G' Protocol ================= The `G' protocol is used by SVR4 UUCP. It is identical to the `g' protocol, except that it is possible to modify the window and packet sizes. The SVR4 implementation of the `g' protocol reportedly is fixed at a packet size of 64 and a window size of 7. Supposedly SVR4 chose to implement a new protocol using a new letter to avoid any potential incompatibilities when using different packet or window sizes. Most implementations of the `g' protocol that accept packets larger than 64 bytes will also accept packets smaller than whatever they requested in the `INITB' packet. The SVR4 `G' implementation is an exception; it will only accept packets of precisely the size it requests in the INITB packet. uucp-1.07/uucp.info-70000664000076400007640000013732507665322530010130 This is uucp.info, produced by makeinfo version 4.1 from uucp.texi. START-INFO-DIR-ENTRY * UUCP: (uucp). Transfer mail and news across phone lines. END-INFO-DIR-ENTRY This file documents Taylor UUCP, version 1.07. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "Copying" are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "Copying" may be included in a translation approved by the author instead of in the original English.  File: uucp.info, Node: i Protocol, Next: j Protocol, Prev: Big G Protocol, Up: Protocols UUCP `i' Protocol ================= The `i' protocol was written by Ian Lance Taylor (who also wrote this manual). It was first used by Taylor UUCP version 1.04. It is a sliding window packet protocol, like the `g' protocol, but it supports bidirectional transfers (i.e., file transfers in both directions simultaneously). It requires an eight bit clear connection. Several ideas for the protocol were taken from the paper `A High-Throughput Message Transport System' by P. Lauder. I don't know where the paper was published, but the author's e-mail address is . The `i' protocol does not adopt his main idea, which is to dispense with windows entirely. This is because some links still do require flow control and, more importantly, because using windows sets a limit to the amount of data which the protocol must be able to resend upon request. To reduce the costs of window acknowledgements, the protocol uses a large window and only requires an ack at the halfway point. Each packet starts with a six byte header, optionally followed by data bytes with a four byte checksum. There are currently five defined packet types (`DATA', `SYNC', `ACK', `NAK', `SPOS', `CLOSE') which are described below. Although any packet type may include data, any data provided with an `ACK', `NAK' or `CLOSE' packet is ignored. Every `DATA', `SPOS' and `CLOSE' packet has a sequence number. The sequence numbers are independent for each side. The first packet sent by each side is always number 1. Each packet is numbered one greater than the previous packet, modulo 32. Every packet has a local channel number and a remote channel number. For all packets at least one channel number is zero. When a UUCP command is sent to the remote system, it is assigned a non-zero local channel number. All packets associated with that UUCP command sent by the local system are given the selected local channel number. All associated packets sent by the remote system are given the selected number as the remote channel number. This permits each UUCP command to be uniquely identified by the channel number on the originating system, and therefore each UUCP package can associate all file data and UUCP command responses with the appropriate command. This is a requirement for bidirectional UUCP transfers. The protocol maintains a single global file position, which starts at 0. For each incoming packet, any associated data is considered to occur at the current file position, and the file position is incremented by the amount of data contained. The exception is a packet of type `SPOS', which is used to change the file position. The reason for keeping track of the file position is described below. The header is as follows: `\007' Every packet begins with `^G'. `(PACKET << 3) + LOCCHAN' The five bit packet number combined with the three bit local channel number. `DATA', `SPOS' and `CLOSE' packets use the packet sequence number for the PACKET field. `NAK' packet types use the PACKET field for the sequence number to be resent. `ACK' and `SYNC' do not use the PACKET field, and generally leave it set to 0. Packets which are not associated with a UUCP command from the local system use a local channel number of 0. `(ACK << 3) + REMCHAN' The five bit packet acknowledgement combined with the three bit remote channel number. The packet acknowledgement is the number of the last packet successfully received; it is used by all packet types. Packets which are not sent in response to a UUCP command from the remote system use a remote channel number of 0. `(TYPE << 5) + (CALLER << 4) + LEN1' The three bit packet type combined with the one bit packet direction combined with the upper four bits of the data length. The packet direction bit is always 1 for packets sent by the calling UUCP, and 0 for packets sent by the called UUCP. This prevents confusion caused by echoed packets. LEN2 The lower eight bits of the data length. The twelve bits of data length permit packets ranging in size from 0 to 4095 bytes. CHECK The exclusive or of the second through fifth bytes of the header. This provides an additional check that the header is valid. If the data length is non-zero, the packet is immediately followed by the specified number of data bytes. The data bytes are followed by a four byte CRC 32 checksum, with the most significant byte first. The CRC is calculated over the contents of the data field. The defined packet types are as follows: 0 `DATA' This is a plain data packet. 1 `SYNC' `SYNC' packets are exchanged when the protocol is initialized, and are described further below. `SYNC' packets do not carry sequence numbers (that is, the PACKET field is ignored). 2 `ACK' This is an acknowledgement packet. Since `DATA' packets also carry packet acknowledgements, `ACK' packets are only used when one side has no data to send. `ACK' packets do not carry sequence numbers. 3 `NAK' This is a negative acknowledgement. This is sent when a packet is received incorrectly, and means that the packet number appearing in the PACKET field must be resent. `NAK' packets do not carry sequence numbers (the PACKET field is already used). 4 `SPOS' This packet changes the file position. The packet contains four bytes of data holding the file position, most significant byte first. The next packet received will be considered to be at the named file position. 5 `CLOSE' When the protocol is shut down, each side sends a `CLOSE' packet. This packet does have a sequence number, which could be used to ensure that all packets were correctly received (this is not needed by UUCP, however, which uses the higher level `H' command with an `HY' response). When the protocol starts up, both systems send a `SYNC' packet. The `SYNC' packet includes at least three bytes of data. The first two bytes are the maximum packet size the remote system should send, most significant byte first. The third byte is the window size the remote system should use. The remote system may send packets of any size up to the maximum. If there is a fourth byte, it is the number of channels the remote system may use (this must be between 1 and 7, inclusive). Additional data bytes may be defined in the future. The window size is the number of packets that may be sent before a packet is acknowledged. There is no requirement that every packet be acknowledged; any acknowledgement is considered to acknowledge all packets through the number given. In the current implementation, if one side has no data to send, it sends an `ACK' when half the window is received. Note that the `NAK' packet corresponds to the unused `g' protocol `SRJ' packet type, rather than to the `RJ' packet type. When a `NAK' is received, only the named packet should be resent, not any subsequent packets. Note that if both sides have data to send, but a packet is lost, it is perfectly reasonable for one side to continue sending packets, all of which will acknowledge the last packet correctly received, while the system whose packet was lost will be unable to send a new packet because the send window will be full. In this circumstance, neither side will time out and one side of the communication will be effectively shut down for a while. Therefore, any system with outstanding unacknowledged packets should arrange to time out and resend a packet even if data is being received. Commands are sent as a sequence of data packets with a non-zero local channel number. The last data packet for a command includes a trailing null byte (normally a command will fit in a single data packet). Files are sent as a sequence of data packets ending with one of length zero. The channel numbers permit a more efficient implementation of the UUCP file send command. Rather than send the command and then wait for the `SY' response before sending the file, the file data is sent beginning immediately after the `S' command is sent. If an `SN' response is received, the file send is aborted, and a final data packet of length zero is sent to indicate that the channel number may be reused. If an `SY' reponse with a file position indicator is received, the file send adjusts to the file position; this is why the protocol maintains a global file position. Note that the use of channel numbers means that each UUCP system may send commands and file data simultaneously. Moreover, each UUCP system may send multiple files at the same time, using the channel number to disambiguate the data. Sending a file before receiving an acknowledgement for the previous file helps to eliminate the round trip delays inherent in other UUCP protocols.  File: uucp.info, Node: j Protocol, Next: x Protocol, Prev: i Protocol, Up: Protocols UUCP `j' Protocol ================= The `j' protocol is a variant of the `i' protocol. It was also written by Ian Lance Taylor, and first appeared in Taylor UUCP version 1.04. The `j' protocol is a version of the `i' protocol designed for communication links which intercept a few characters, such as XON or XOFF. It is not efficient to use it on a link which intercepts many characters, such as a seven bit link. The `j' protocol performs no error correction or detection; that is presumed to be the responsibility of the `i' protocol. When the `j' protocol starts up, each system sends a printable ASCII string indicating which characters it wants to avoid using. The string begins with the ASCII character `^' (octal 136) and ends with the ASCII character `~' (octal 176). After sending this string, each system looks for the corresponding string from the remote system. The strings are composed of escape sequences: `\ooo', where `o' is an octal digit. For example, sending the string `^\021\023~' means that the ASCII XON and XOFF characters should be avoided. The union of the characters described in both strings (the string which is sent and the string which is received) is the set of characters which must be avoided in this conversation. Avoiding a printable ASCII character (octal 040 to octal 176, inclusive) is not permitted. After the exchange of characters to avoid, the normal `i' protocol start up is done, and the rest of the conversation uses the normal `i' protocol. However, each `i' protocol packet is wrapped to become a `j' protocol packet. Each `j' protocol packet consists of a seven byte header, followed by data bytes, followed by index bytes, followed by a one byte trailer. The packet header looks like this: `^' Every packet begins with the ASCII character `^', octal 136. HIGH LOW These two characters give the total number of bytes in the packet. Both HIGH and LOW are printable ASCII characters. The length of the packet is `(HIGH - 040) * 0100 + (LOW - 040)', where `040 <= HIGH < 0177' and `040 <= LOW < 0140'. This permits a length of 6079 bytes, but there is a further restriction on packet size described below. `=' The ASCII character `=', octal 075. DATA-HIGH DATA-LOW These two characters give the total number of data bytes in the packet. The encoding is as described for HIGH and LOW. The number of data bytes is the size of the `i' protocol packet wrapped inside this `j' protocol packet. `@' The ASCII character `@', octal 100. The header is followed by the number of data bytes given in DATA-HIGH and DATA-LOW. These data bytes are the `i' protocol packet which is being wrapped in the `j' protocol packet. However, each character in the `i' protocol packet which the `j' protocol must avoid is transformed into a printable ASCII character (recall that avoiding a printable ASCII character is not permitted). Two index bytes are used for each character which must be transformed. The index bytes immediately follow the data bytes. The index bytes are created in pairs. Each pair of index bytes encodes the location of a character in the `i' protocol packet which was transformed to become a printable ASCII character. Each pair of index bytes also encodes the precise transformation which was performed. When the sender finds a character which must be avoided, it will transform it using one or two operations. If the character is 0200 or greater, it will subtract 0200. If the resulting character is less than 020, or is equal to 0177, it will xor by 020. The result is a printable ASCII character. The zero based byte index of the character within the `i' protocol packet is determined. This index is turned into a two byte printable ASCII index, INDEX-HIGH and INDEX-LOW, such that the index is `(INDEX-HIGH - 040) * 040 + (INDEX-LOW - 040)'. INDEX-LOW is restricted such that `040 <= INDEX-LOW < 0100'. INDEX-HIGH is not permitted to be 0176, so `040 <= INDEX-HIGH < 0176'. INDEX-LOW is then modified to encode the transformation: * If the character transformation only had to subtract 0200, then INDEX-LOW is used as is. * If the character transformation only had to xor by 020, then 040 is added to INDEX-LOW. * If both operations had to be performed, then 0100 is added to INDEX-LOW. However, if the value of INDEX-LOW was initially 077, then adding 0100 would result in 0177, which is not a printable ASCII character. For that special case, INDEX-HIGH is set to 0176, and INDEX-LOW is set to the original value of INDEX-HIGH. The receiver decodes the index bytes as follows (this is the reverse of the operations performed by the sender, presented here for additional clarity): * The first byte in the index is INDEX-HIGH, and the second is INDEX-LOW. * If `040 <= INDEX-HIGH < 0176', the index refers to the data byte at position `(INDEX-HIGH - 040) * 040 + INDEX-LOW % 040'. * If `040 <= INDEX-LOW < 0100', then 0200 must be added to indexed byte. * If `0100 <= INDEX-LOW < 0140', then 020 must be xor'ed to the indexed byte. * If `0140 <= INDEX-LOW < 0177', then 0200 must be added to the indexed byte, and 020 must be xor'ed to the indexed byte. * If `INDEX-HIGH == 0176', the index refers to the data byte at position `(INDEX-LOW - 040) * 040 + 037'. 0200 must be added to the indexed byte, and 020 must be xor'ed to the indexed byte. This means the largest `i' protocol packet which may be wrapped inside a `j' protocol packet is `(0175 - 040) * 040 + (077 - 040) == 3007' bytes. The final character in a `j' protocol packet, following the index bytes, is the ASCII character `~' (octal 176). The motivation behind using an indexing scheme, rather than escape characters, is to avoid data movement. The sender may simply add a header and a trailer to the `i' protocol packet. Once the receiver has loaded the `j' protocol packet, it may scan the index bytes, transforming the data bytes, and then pass the data bytes directly on to the `i' protocol routine.  File: uucp.info, Node: x Protocol, Next: y Protocol, Prev: j Protocol, Up: Protocols UUCP `x' Protocol ================= The `x' protocol is used in Europe (and probably elsewhere) with machines that contain an builtin X.25 card and can send eight bit data transparently across X.25 circuits, without interference from the X.28 or X.29 layers. The protocol sends packets of 512 bytes, and relies on a write of zero bytes being read as zero bytes without stopping communication. It first appeared in the original System V UUCP implementation.  File: uucp.info, Node: y Protocol, Next: d Protocol, Prev: x Protocol, Up: Protocols UUCP `y' Protocol ================= The `y' protocol was developed by Jorge Cwik for use in FX UUCICO, a PC uucico program. It is designed for communication lines which handle error correction and flow control. It requires an eight bit clean connection. It performs error detection, but not error correction: when an error is detected, the line is dropped. It is a streaming protocol, like the `f' protocol; there are no packet acknowledgements, so the protocol is efficient over a half-duplex communication line such as PEP. Every packet contains a six byte header: sequence low byte sequence high byte A two byte sequence number, in little endian order. The first sequence number is 0. Since the first packet is always a sync packet (described below) the sequence number of the first data packet is always 1. Each system counts sequence numbers independently. length low byte length high byte A two byte data length, in little endian order. If the high bit of the sixteen bit field is clear, this is the number of data bytes which follow the six byte header. If the high bit is set, there is no data, and the length field is a type of control packet. checksum low byte checksum high byte A two byte checksum, in little endian order. The checksum is computed over the data bytes. The checksum algorithm is described below. If there are no data bytes, the checksum is sent as 0. When the protocol starts up, each side must send a sync packet. This is a packet with a normal six byte header followed by data. The sequence number of the sync packet should be 0. Currently at least four bytes of data must be sent with the sync packet. Additional bytes should be ignored. They are defined as follows: version The version number of the protocol. Currently this must be 1. Larger numbers should be ignored; it is the responsibility of the newer version to accommodate the older one. packet size The maximum data length to use divided by 256. This is sent as a single byte. The maximum data length permitted is 32768, which would be sent as 128. Customarily both systems will use the same maximum data length, the lower of the two requested. flags low byte flags high byte Two bytes of flags. None are currently defined. These bytes should be sent as 0, and ignored by the receiver. A length field with the high bit set is a control packet. The following control packet types are defined: 0xfffe `YPKT_ACK' Acknowledges correct receipt of a file. 0xfffd `YPKT_ERR' Indicates an incorrect checksum. 0xfffc `YPKT_BAD' Indicates a bad sequence number, an invalid length, or some other error. If a control packet other than `YPKT_ACK' is received, the connection is dropped. If a checksum error is detected for a received packet, a `YPKT_ERR' control packet is sent, and the connection is dropped. If a packet is received out of sequence, a `YPKT_BAD' control packet is sent, and the connection is dropped. The checksum is initialized to 0xffff. For each data byte in a packet it is modified as follows (where B is the byte before it has been transformed as described above): /* Rotate the checksum left. */ if ((ichk & 0x8000) == 0) ichk <<= 1; else { ichk <<= 1; ++ichk; } /* Add the next byte into the checksum. */ ichk += B; This is the same algorithm as that used by the `f' protocol. A command is sent as a sequence of data packets followed by a null byte. In the normal case, a command will fit into a single packet. The packet should be exactly the length of the command plus a null byte. If the command is too long, more packets are sent as required. A file is sent as a sequence of data packets, ending with a zero length packet. The data packets may be of any length greater than zero and less than or equal to the maximum permitted packet size specified in the initial sync packet. After the zero length packet ending a file transfer has been received, the receiving system sends a `YPKT_ACK' control packet. The sending system waits for the `YPKT_ACK' control packet before continuing; this wait should be done with a large timeout, since there may be a considerable amount of data buffered on the communication path.  File: uucp.info, Node: d Protocol, Next: h Protocol, Prev: y Protocol, Up: Protocols UUCP `d' Protocol ================= The `d' protocol is apparently used for DataKit muxhost (not RS-232) connections. No file size is sent. When a file has been completely transferred, a write of zero bytes is done; this must be read as zero bytes on the other end.  File: uucp.info, Node: h Protocol, Next: v Protocol, Prev: d Protocol, Up: Protocols UUCP `h' Protocol ================= The `h' protocol is apparently used in some places with HST modems. It does no error checking, and is not that different from the `t' protocol. I don't know the details.  File: uucp.info, Node: v Protocol, Prev: h Protocol, Up: Protocols UUCP `v' Protocol ================= The `v' protocol is used by UUPC/extended, a PC UUCP program. It is simply a version of the `g' protocol which supports packets of any size, and also supports sending packets of different sizes during the same conversation. There are many `g' protocol implementations which support both, but there are also many which do not. Using `v' ensures that everything is supported.  File: uucp.info, Node: Hacking, Next: Acknowledgements, Prev: Protocols, Up: Top Hacking Taylor UUCP ******************* This chapter provides the briefest of guides to the Taylor UUCP source code itself. * Menu: * System Dependence:: System Dependence * Naming Conventions:: Naming Conventions * Patches:: Patches  File: uucp.info, Node: System Dependence, Next: Naming Conventions, Prev: Hacking, Up: Hacking System Dependence ================= The code is carefully segregated into a system independent portion and a system dependent portion. The system dependent code is in the `unix' subdirectory, and also in the file `sysh.unx' (also known as `sysdep.h'). With the right configuration parameters, the system independent code calls only ANSI C functions. Some of the less common ANSI C functions are also provided in the `lib' directory. The replacement function `strtol' in `lib/strtol.c' assumes that the characters `A' to `F' and `a' to `f' appear in strictly sequential order. The function `igradecmp' in `uuconf/grdcmp.c' assumes that the upper and lower case letters appear in order. Both assumptions are true for ASCII and EBCDIC, but neither is guaranteed by ANSI C. Disregarding these caveats, I believe that the system independent portion of the code is strictly conforming. That's not too exciting, since all the work is done in the system dependent code. I think that this code can conform to POSIX 1003.1, given the right compilation parameters. I'm a bit less certain about this, though. The code has been used on a 16 bit segmented system with no function prototypes, so I'm fairly certain that all casts to long and pointers are done when necessary.  File: uucp.info, Node: Naming Conventions, Next: Patches, Prev: System Dependence, Up: Hacking Naming Conventions ================== I use a modified Hungarian naming convention for my variables and functions. As with all naming conventions, the code is rather opaque if you are not familiar with it, but becomes clear and easy to use with time. The first character indicates the type of the variable (or function return value). Sometimes additional characters are used. I use the following type prefixes: `a' array; the next character is the type of an element `b' byte or character `c' count of something `e' stdio FILE * `f' boolean `i' generic integer `l' double `o' file descriptor (as returned by open, creat, etc.) `p' generic pointer `q' pointer to structure `s' structure `u' void (function return values only) `z' character string A generic pointer (`p') is sometimes a `void *', sometimes a function pointer in which case the prefix is pf, and sometimes a pointer to another type, in which case the next character is the type to which it points (pf is overloaded). An array of strings (`char *[]') would be named `az' (array of string). If this array were passed to a function, the function parameter would be named `paz' (pointer to array of string). Note that the variable name prefixes do not necessarily indicate the type of the variable. For example, a variable prefixed with `i' may be int, long or short. Similarly, a variable prefixed with `b' may be a char or an int; for example, the return value of `getchar' would be caught in an int variable prefixed with `b'. For a non-local variable (extern or file static), the first character after the type prefix is capitalized. Most static variables and functions use another letter after the type prefix to indicate which module they come from. This is to help distinguish different names in the debugger. For example, all static functions in `protg.c', the `g' protocol source code, use a module prefix of `g'. This isn't too useful, as a number of modules use a module prefix of `s'.  File: uucp.info, Node: Patches, Prev: Naming Conventions, Up: Hacking Patches ======= I am always grateful for any patches sent in. Much of the flexibility and portability of the code is due to other people. Please do not hesitate to send me any changes you have found necessary or useful. When sending a patch, please send the output of the Unix `diff' program invoked with the `-c' option (if you have the GNU version of `diff', use the `-p' option). Always invoke `diff' with the original file first and the modified file second. If your `diff' does not support `-c' (or you don't have `diff'), send a complete copy of the modified file (if you have just changed a single function, you can just send the new version of the function). In particular, please do not send `diff' output without the `-c' option, as it is useless. If you have made a number of changes, it is very convenient for me if you send each change as a separate mail message. Sometimes I will think that one change is useful but another one is not. If they are in different messages it is much easier for me to apply one but not the other. I rarely apply the patches directly. Instead I work my way through the hunks and apply each one separately. This ensures that the naming remains consistent, and that I understand all the code. If you can not follow all these rules, then don't. But if you do, it makes it more likely that I will incorporate your changes. I am not paid for my UUCP work, and my available time is unfortunately very restricted. The package is important to me, and I do what I can, but I can not do all that I would like, much less all that everybody else would like. Finally, please do not be offended if I do not reply to messages for some time, even a few weeks. I am often behind on my mail, and if I think your message deserves a considered reply I will often put it aside until I have time to deal with it.  File: uucp.info, Node: Acknowledgements, Next: Index (concepts), Prev: Hacking, Up: Top Acknowledgements **************** This is a list of people who gave help or suggestions while I was working on the Taylor UUCP project. Appearance on this list does not constitute endorsement of the program, particularly since some of the comments were criticisms. I've probably left some people off, and I apologize for any oversight; it does not mean your contribution was unappreciated. First of all, I would like to thank the people at Infinity Development Systems (formerly AIRS, which lives on in the domain name) for permitting me to use their computers and `uunet' access. I would also like to thank Richard Stallman for founding the Free Software Foundation, and John Gilmore for writing the initial version of gnuucp (based on uuslave) which was a direct inspiration for this somewhat larger project. Chip Salzenberg has contributed many patches. Franc,ois Pinard tirelessly tested the code and suggested many improvements. He also put together the initial version of this manual. Doug Evans contributed the zmodem protocol. Marc Boucher contributed the code supporting the pipe port type. Jorge Cwik contributed the `y' protocol code. Finally, Verbus M. Counts and Centel Federal Systems, Inc., deserve special thanks, since they actually paid me money to port this code to System III. In alphabetical order: Meno Abels "Earle F. Ake - SAIC" (Michael Almond) (Christopher J. Ambler) Brian W. Antoine (John Antypas) (James Van Artsdalen) (Jim Avera) (Niels Baggesen) (Scott Ballantyne) Zacharias Beckman (Mike Bernson) (Roberto Biancardi) (Scott Blachowicz) (Andrey G Blochintsev) (Spider Boardman) Gregory Bond Marc Boucher Ard van Breemen (Dean Brooks) (Jim Brownfield) (Dave Buck) (Gordon Burditt) (Donald Burr) (Michael I Bushnell) Brian Campbell Andrew A. Chernov (Jonathan Clark) (Frank Conrad) Ed Carp (Mark Clements) (Verbus M. Counts) (John Cowan) Bob Cunningham (Jorge Cwik) (Klaus Dahlenburg) Damon (Bill Davidson) (Hubert Delahaye) (Mark Delany) Allen Delaney Gerriet M. Denkmann (Bob Denny) Drew Derbyshire (Steven S. Dick) (Gert Doering) (Uwe Doering) Hans-Dieter Doll (Dean Edmonds) Mark W. Eichin Andrew Evans (Doug Evans) Marc Evans Dan Everhart (Larry Fahnoe) Matthew Farwell (Bill Fenner) (Jose A. Fernandez) "David J. Fiander" Thomas Fischer Mister Flash (Ju"rgen Fluk) (Erik Forsberg) (Andy Fyfe) Lele Gaifax (James Gardiner [hunter]) Terry Gardner (David Gilbert) (Oleg Girko) (Jim Gottlieb) Benoit Grange (Eric Lee Green) (Daniel R. Guilderson) (Gregory Gulik) Richard H. Gumpertz Scott Guthridge Michael Haberler Daniel Hagerty (John Harkin) (Guy Harris) (Stephen Harris) Tom Ivar Helbekkmo Petri Helenius (B. Gabriel Helou) Bob Hemedinger Andrew Herbert (Kenneth Herron) Peter Honeyman (John Hood) Mark Horsburgh John Hughes Mike Ipatow Bill Irwin (Chiaki Ishikawa) (Andreas Israel) (Tim Iverson) (Bob Izenberg) (D.J.James) Rob Janssen (Eric S Johansson) Kevin Johnson (Robert Joop) Alan Judge (Christof Junge) Romain Kang (Ronald S. Karr) Brendan Kehoe (John Kennedy) (Jac Kersing) (Olaf Kirch) Gabor Kiss (Gero Kuhlmann) (Rob Kurver) "C.A. Lademann" (Kent Landfield) Tin Le (Gregory LeBaron) (Karl Lehenbauer) (Alexander Lehmann) (Merlyn LeRoy) (Chris Lewis) (Don Lewis) (Jay Vassos-Libove) (Bruce Lilly) Godfrey van der Linden Ted Lindgreen (Andrew Loewenstern) "Arne Ludwig" Matthew Lyle (David J. MacKenzie) John R MacMillan (Jens-Uwe Mager) Giles D Malet (Mark E. Mallett) (Jose A. Manas) (Peter Mandrella) (Alex Martelli) W Christopher Martin Yanek Martinson (Thomas Mechtersheimer) (Jean Mehat) (Udo Meyer) (Leslie Mikesell) (Trever Miller) (Mitch Mitchell) Emmanuel Mogenet (Rupert Mohr) Jason Molenda (Ian Moran) (John Paul Morrison) (Brian J. Murrell) (Dirk Musstopf) (Lyndon Nerenberg) (Rolf Nerstheimer) (Thomas Neumann) Richard E. Nickle (Stephan Niemz) (Raymond Nijssen) (Michael Nolan) david nugent Jim O'Connor (Kevin O'Gorman) Petri Ojala (Brian 'Doc' O'Neill) Peter Palfrader (Mike Park) Tim Peiffer (Don Phillips) "Mark Pizzolato 415-369-9366" John Plate (Dave Platt) (Mark Powell) Mark Powell (Tim Pozar) (Joey Pruett) Paul Pryor (Jeff Putsch) (Andreas Raab) Vadim Radionov Jarmo Raiha James Revell Scott Reynolds (Michael Richardson) Kenji Rikitake (Arnold Robbins) (Steve M. Robbins) Ollivier Robert Serge Robyns Lawrence E. Rosenman Jeff Ross Aleksey P. Rudnev "Heiko W.Rupp" (Wolfgang S. Rupprecht) (Tom Rushworth) Peter Rye (Joseph E. Sacco) (Rich Salz) Curt Sampson (Mike Sangrey) Nickolay Saukh Ignatios Souvatzis (Heiko Schlittermann) Eric Schnoebelen (Russell Schulz) Igor V. Semenyuk Christopher Sawtell (Bernd Schuler) (Christian Seyb) Marcus Shang (M. J. Shannon Jr.) (Michael Shields) (Peter da Silva) (Vince Skahan) (Patrick Smith) (Monty Solomon) (Bill Sommerfeld) Julian Stacey (Gjoen Stein) Harlan Stenn Ralf Stephan (Johannes Stille) (Hannu Strang) (Ralf E. Stranzenbach) (S. Sullivan) Shigeya Suzuki (Karl Swartz) Oleg Tabarovsky (Takatoshi Ikeda) John Theus (Bob Thrush) ppKarsten Thygesen Graham Toal (Richard Todd) Michael Ju. Tokarev Martin Tomes Len Tower Mark Towfiq (Marc Unangst) Matthias Urlichs Tomi Vainio (Adri Verhoef) Andrew Vignaux (Andreas Vogel) Dima Volodin (Jos Vos) (Johan Vromans) David Vrona (Marcel Waldvogel) (Stephen J. Walick) (Syd Weinstein) (Gerben Wierda) (Joe Wells) (T. William Wells) Peter Wemm (Greg A. Woods) (John Woods) Michael Yu.Yaroslavtsev Alexei K. Yushin (Jon Zeeff) Matthias Zepf Eric Ziegast  File: uucp.info, Node: Index (concepts), Next: Index (configuration file), Prev: Acknowledgements, Up: Top Concept Index ************* * Menu: * .Corrupt: Execution Subdirectories. * .Failed: Execution Subdirectories. * .Preserve: Other Spool Subdirectories. * .Received: Other Spool Subdirectories. * .Sequence: Other Spool Subdirectories. * .Status: Status Directory. * .Temp: Other Spool Subdirectories. * .Xqtdir: Execution Subdirectories. * /usr/spool/uucp: Miscellaneous (config). * /usr/spool/uucppublic: Miscellaneous (config). * accepting calls: Accepting Calls. * anonymous UUCP: config File Examples. * call configuration file: Configuration File Names. * call in login name: Configuration File Names. * call in password: Configuration File Names. * call out file: Configuration File Names. * call out login name: Configuration File Names. * call out password: Configuration File Names. * calling in: Accepting Calls. * calling out: Calling Other Systems. * changing spool directory: config File Examples. * chat scripts: Chat Scripts. * cleaning the spool directory: Spool Directory Cleaning. * config file: config File. * config file examples: config File Examples. * configuration file (call): Configuration File Names. * configuration file (config): config File. * configuration file (dial): dial File. * configuration file (dialcode): Configuration File Names. * configuration file (passwd): Configuration File Names. * configuration file (port): port File. * configuration file (sys): sys File. * d protocol: d Protocol. * debugging file: Log File Names. * dial file: dial File. * dialcode configuration file: Configuration File Names. * dialcode file: Configuration File Names. * dialer configuration file: dial File. * e protocol: e Protocol. * E UUCP protocol command: The E Command. * execution file format: Execution File Format. * f protocol: f Protocol. * final handshake: The Final Handshake. * G protocol: Big G Protocol. * g protocol: g Protocol. * gateway: Gateway Example. * grades: When to Call. * grades implementation: UUCP Grades. * h protocol: h Protocol. * H UUCP protocol command: The H Command. * i protocol: i Protocol. * initial handshake: The Initial Handshake. * j protocol: j Protocol. * L.XXX: Spool Lock Files. * LCK..SYS: Spool Lock Files. * LCK.XQT.NN: Spool Lock Files. * leaf site: Leaf Example. * lock directory: Miscellaneous (config). * lock files: UUCP Lock Files. * lock files in spool directory: Spool Lock Files. * log file: Log File Names. * LXQ.CMD: Spool Lock Files. * mail: Mail and News. * main configuration file: config File. * news: Mail and News. * parity in login names: Miscellaneous (config). * passwd configuration file: Configuration File Names. * passwd file: Configuration File Names. * port configuration file: port File. * port file: port File. * protocol d: d Protocol. * protocol e: e Protocol. * protocol f: f Protocol. * protocol G: Big G Protocol. * protocol g: g Protocol. * protocol h: h Protocol. * protocol i: i Protocol. * protocol j: j Protocol. * protocol t: t Protocol. * protocol v: v Protocol. * protocol x: x Protocol. * protocol y: y Protocol. * protocol, UUCP: UUCP Protocol. * public directory: Miscellaneous (config). * R UUCP protocol command: The R Command. * S UUCP protocol command: The S Command. * spool directory: The Spool Directory Layout. * spool directory, changing: config File Examples. * spool directory, cleaning: Spool Directory Cleaning. * spool directory, setting: Miscellaneous (config). * statistics file: Log File Names. * status files: Status Directory. * sys file: sys File. * sys file example (gateway): Gateway Example. * sys file example (leaf): Leaf Example. * system configuration file: sys File. * system lock files: Spool Lock Files. * system name: Miscellaneous (config). * system spool directories: System Spool Directories. * t protocol: t Protocol. * time strings: Time Strings. * unknown systems: Miscellaneous (config). * UUCP protocol: UUCP Protocol. * UUCP protocol E command: The E Command. * UUCP protocol H command: The H Command. * UUCP protocol R command: The R Command. * UUCP protocol S command: The S Command. * UUCP protocol X command: The X Command. * UUCP system name: Miscellaneous (config). * uucppublic: Miscellaneous (config). * v protocol: v Protocol. * x protocol: x Protocol. * X UUCP protocol command: The X Command. * X.* file format: Execution File Format. * y protocol: y Protocol. uucp-1.07/uucp.info-80000664000076400007640000002156607665322530010130 This is uucp.info, produced by makeinfo version 4.1 from uucp.texi. START-INFO-DIR-ENTRY * UUCP: (uucp). Transfer mail and news across phone lines. END-INFO-DIR-ENTRY This file documents Taylor UUCP, version 1.07. Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "Copying" are included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "Copying" may be included in a translation approved by the author instead of in the original English.  File: uucp.info, Node: Index (configuration file), Prev: Index (concepts), Up: Top Configuration File Index ************************ * Menu: * abort: dial File. * abort-chat: dial File. * abort-chat-fail: dial File. * abort-chat-program: dial File. * abort-chat-seven-bit: dial File. * abort-chat-timeout: dial File. * address: Placing the Call. * alias: Naming the System. * alternate: Naming the System. * baud in port file: port File. * baud in sys file: Placing the Call. * baud-range: port File. * call-local-size: File Transfer Control. * call-login: Logging In. * call-password: Logging In. * call-remote-size: File Transfer Control. * call-timegrade: When to Call. * call-transfer: File Transfer Control. * callback: Accepting a Call. * called-chat: Accepting a Call. * called-chat-fail: Accepting a Call. * called-chat-program: Accepting a Call. * called-chat-seven-bit: Accepting a Call. * called-chat-timeout: Accepting a Call. * called-local-size: File Transfer Control. * called-login: Accepting a Call. * called-remote-size: File Transfer Control. * called-timegrade: When to Call. * called-transfer: File Transfer Control. * callfile: Configuration File Names. * carrier in dial file: dial File. * carrier in port file: port File. * carrier-wait: dial File. * chat: Chat Scripts. * chat in dial file: dial File. * chat in sys file: Logging In. * chat-fail: Chat Scripts. * chat-fail in dial file: dial File. * chat-fail in sys file: Logging In. * chat-program: Chat Scripts. * chat-program in dial file: dial File. * chat-program in sys file: Logging In. * chat-seven-bit: Chat Scripts. * chat-seven-bit in dial file: dial File. * chat-seven-bit in sys file: Logging In. * chat-timeout: Chat Scripts. * chat-timeout in dial file: dial File. * chat-timeout in sys file: Logging In. * command: port File. * command-path: Miscellaneous (sys). * commands: Miscellaneous (sys). * complete: dial File. * complete-chat: dial File. * complete-chat-fail: dial File. * complete-chat-program: dial File. * complete-chat-seven-bit: dial File. * complete-chat-timeout: dial File. * debug in config file: Debugging Levels. * debug in sys file: Miscellaneous (sys). * debugfile: Log File Names. * default-alternates: Naming the System. * device: port File. * dial-device: port File. * dialcodefile: Configuration File Names. * dialer in dial file: dial File. * dialer in port file: port File. * dialer-sequence: port File. * dialfile: Configuration File Names. * dialtone: dial File. * dtr-toggle: dial File. * forward: File Transfer Control. * forward-from: File Transfer Control. * forward-to: File Transfer Control. * free-space: Miscellaneous (sys). * half-duplex in dial file: dial File. * half-duplex in port file: port File. * hardflow: port File. * hdb-files: Miscellaneous (config). * hostname: Miscellaneous (config). * local-receive: File Transfer Control. * local-send: File Transfer Control. * lockdir: Miscellaneous (config). * lockname: port File. * logfile: Log File Names. * max-file-time: File Transfer Control. * max-remote-debug: Miscellaneous (sys). * max-retries: When to Call. * max-uuxqts: Miscellaneous (config). * myname: Naming the System. * nodename: Miscellaneous (config). * passwdfile: Configuration File Names. * pause: dial File. * phone: Placing the Call. * port in port file: port File. * port in sys file: Placing the Call. * portfile: Configuration File Names. * protocol in port file: port File. * protocol in sys file: Protocol Selection. * protocol-parameter in dial file: dial File. * protocol-parameter in port file: port File. * protocol-parameter in sys file: Protocol Selection. * pubdir in config file: Miscellaneous (config). * pubdir in sys file: Miscellaneous (sys). * push: port File. * receive-request: File Transfer Control. * reliable in dial file: dial File. * reliable in port file: port File. * remote-receive: File Transfer Control. * remote-send: File Transfer Control. * request: File Transfer Control. * run-uuxqt: Miscellaneous (config). * send-request: File Transfer Control. * sequence: Miscellaneous (sys). * server-address: port File. * service: port File. * seven-bit in dial file: dial File. * seven-bit in port file: port File. * speed in port file: port File. * speed in sys file: Placing the Call. * speed-range: port File. * spool: Miscellaneous (config). * statfile: Log File Names. * stream: port File. * strip-login: Miscellaneous (config). * strip-proto: Miscellaneous (config). * sysfile: Configuration File Names. * system: Naming the System. * time: When to Call. * timegrade: When to Call. * timetable: Miscellaneous (config). * transfer: File Transfer Control. * type: port File. * unknown: Miscellaneous (config). * uuname: Miscellaneous (config). * v2-files: Miscellaneous (config). * version: port File.