diff --git a/BUGREPORT b/BUGREPORT new file mode 100644 index 0000000..9f9e236 --- /dev/null +++ b/BUGREPORT @@ -0,0 +1,25 @@ +As this software is in the Alpha state, there are still some bugs or +problems. If you encountered one please include following informations when +reporting it: +- The version you are using. +- Your `Config', `config.h' and `yaps.rc' file. +- Call `yaps -V' and redirect the output into a file. Include this file. +- A command to reproduce the bug. +- The output with the highest verbosity. This can be + done by starting yaps with several -v's (at least 4) and + redirecting stdout and stderr into a file, e.g.: + yaps -vvvv [your options] &> bug [bash] + - or - + yaps -vvvv [your options] >bug 2>&1 [sh] + - or - + see your shell manual. +- If the program terminates recompile it with debug information + (if not already done). Reproduce the problem. Start the program + under the control of the debugger. Save the location and the stack + frame into a file. Send the file. If a core file is generated, + start the debugger on the program/core pair, print the location + and the stack frame into a file. Send the file. +- DO NOT SEND CORE FILES!!! + +Send these information either to (preferred) or +. diff --git a/COPYING.GPL b/COPYING.GPL new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING.GPL @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..1d94d4d --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,17 @@ + Copyright (c) 1997 Ulrich Dessauer + All rights reserved. + + You may distribute under the terms of the GNU General Public + License. + + IN NO EVENT SHALL ULRICH DESSAUER BE LIABLE TO ANY PARTY FOR DIRECT, + INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF ULRICH DESSAUER + HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ULRICH DESSAUER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + BASIS, AND ULRICH DESSAUER HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + diff --git a/Config b/Config new file mode 100644 index 0000000..978cfa6 --- /dev/null +++ b/Config @@ -0,0 +1,65 @@ +# -*- sh -*- +# SECTION A.) Global configuration +# Select compiler command +CC = gcc +# +CFLAGS = -Wall -Wno-parentheses -Wshadow -Wstrict-prototypes -Wmissing-prototypes +LDFLAGS = +EXLIBS = +# +# Select scripting language(s) +# SLang (ftp://space.mit.edu/pub/davis/slang/) +#SLANG = True +# LUA (ftp://ftp.icad.puc-rio.br/pub/lua/) +#LUA = True +# +# Define this to disable debugging +#NDEBUG = True +# +# +# +# SECTION B.) yaps configuration +# Name and location of global configuration file +YAPS_CFGFILE = /etc/yaps.rc +# Name of local configuration file (location is $HOME.) +YAPS_LCFGFILE = .yapsrc +# Directory for installing the binary +YAPS_BINDIR = /usr/local/bin +# Directory for optional support files +YAPS_LIBDIR = /usr/local/lib/yaps +# User/Group/Mode for yaps +YAPS_USER = bin +YAPS_GROUP = uucp +YAPS_MODE = 2711 +# User/Group/Mode for global configuration file +YAPS_RCUSER = bin +YAPS_RCGROUP = bin +YAPS_RCMODE = 644 +# +# End of Configuration, the rest is for automatic +# configuration depending on settings above +LDEFS = +LLIBS = +ifdef SLANG +LDEFS += -DSCRIPT_SLANG +LLIBS += -lslang +MATH = True +endif +ifdef LUA +LDEFS += -DSCRIPT_LUA +LLIBS += -llualib -llua +MATH = True +endif +ifdef MATH +LLIBS += -lm +endif +# +ifdef NDEBUG +DDEFS = -DNDEBUG +G = -O +else +G = -g +endif +# +CFLAGS := $G $(CFLAGS) $(LDEFS) $(DDEFS) +LDFLAGS := $G $(LDFLAGS) diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..8c92d91 --- /dev/null +++ b/INSTALL @@ -0,0 +1 @@ +Read the Installation section in the README file! diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d88e3ee --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +# -*- sh -*- +# +include Config +# +#CFLAGS := -include mem.h $(CFLAGS) +CFLAGS := $(CFLAGS) +# +DEFS = -DCFGFILE=\"$(YAPS_CFGFILE)\" -DLCFGFILE=\"$(YAPS_LCFGFILE)\" \ + -DLIBDIR=\"$(YAPS_LIBDIR)\" +LIBS = -L. -lpager $(LLIBS) +OBJS = data.o util.o cfg.o tty.o cv.o asc.o scr.o tap.o ucp.o \ + slang.o lua.o #mem.o +YOBJS = yaps.o valid.o +DSTFLE = $(YAPS_BINDIR)/yaps + +all: yaps yaps.doc + @if [ -f contrib/Makefile ]; then \ + $(MAKE) -C contrib all ; \ + fi + +yaps: libpager.a $(YOBJS) + $(CC) $(LDFLAGS) $(YOBJS) -o $@ $(LIBS) + +libpager.a: $(OBJS) + rm -f $@ + ar rc $@ $(OBJS) + ranlib $@ + +yaps.o: yaps.c pager.h + $(CC) $(CFLAGS) $(DEFS) $< -c -o $@ + +yaps.doc: yaps.html + lynx -cfg=/dev/null -nolist -dump $< > $@ + +install: $(DSTFLE) $(CFGFILE) + if [ ! -d $(YAPS_LIBDIR) ]; then \ + install -d -m 755 -o $(YAPS_USER) -g $(YAPS_GROUP) $(YAPS_LIBDIR) ; \ + fi + @if [ -f contrib/Makefile ]; then \ + $(MAKE) -C contrib install ; \ + fi + +$(DSTFLE): yaps + install -o $(YAPS_USER) -g $(YAPS_GROUP) -m $(YAPS_MODE) -s yaps $@ + +$(CFGFILE): yaps.rc + @if [ ! -f $@ ]; then \ + install -o $(YAPS_RCUSER) -g $(YAPS_RCGROUP) -m $(YAPS_RCMODE) -s yaps.rc $@ ; \ + fi + +clean: + rm -f *.[oa] *~ .*~ yaps core .depend + @if [ -f contrib/Makefile ]; then \ + $(MAKE) -C contrib clean ; \ + fi + +depend: + $(CPP) $(CFLAGS) -MM *.c > .depend + @if [ -f contrib/Makefile ]; then \ + $(MAKE) -C contrib depend ; \ + fi + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/README b/README new file mode 100644 index 0000000..bff7b4e --- /dev/null +++ b/README @@ -0,0 +1,113 @@ + yaps -or- Yet Another Pager Software + ===================================== + +This software is primary designed to send message to so called pager devices +including cellular phones which are able to receive textual messages +(sometimes called SMS, short message system/service.) + +This is a simple command line driven program with a global and a personal +configuration file, but the protocol implementation is kept as a seperate +library, with which one may somedays write a client/server system. + +Copyright: +~~~~~~~~~~ + Copyright (c) 1997 Ulrich Dessauer + All rights reserved. + + You may distribute under the terms of the GNU General Public + License. + + IN NO EVENT SHALL ULRICH DESSAUER BE LIABLE TO ANY PARTY FOR DIRECT, + INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF ULRICH DESSAUER + HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ULRICH DESSAUER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + BASIS, AND ULRICH DESSAUER HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + +Installation: +~~~~~~~~~~~~~ +You need a system supporting the Posix system calls and library functions +(every modern Unix like OS should have this support) and you need an install +program, otherwise you have to install the result by hand. Gnumake and GCC +are a plus, otherwise you may have to tweak the Makefile and some source +files a bit. + +1.) Edit Config (which will be included by make) & edit config.h +1a.) If you do not have the SLang library installed, comment out + the line `SLANG = True' in Config or install it somewhere + the compiler can find it (/usr/local/{lib,include} seems to + be a not-to-bad place). READ THE ATTACHED COPYRIGHT NOTICE! +1b.) Dito for lua. +2.) Edit config.h. Comments should explain enough. +3.) `make depend' +4.) `make' + + If the program compiles fine: +5.) `make install' as root (if you have permissons). If you do not have + an install program, install it by hand: + a.) Copy the executable `yaps' to somewhere in your path + b.) Install yaps.rc as /etc/yaps.rc or as ~/.yapsrc +6.) edit `/etc/yaps.rc' for global settings +7.) Optional edit ~/.yapsrc for your private settings + +An example `yaps.rc' file is included and can be used directly for some +services available in Germany. Others may be added. See the `yaps.doc' file +for a complete description of the configuration files. + +Implementation: +~~~~~~~~~~~~~~~ +The current implementation allows sending messages to pager, where the +service company offers a modem gateway. Paging via Internet or other online +services is currently not supported, as they are mostly quite simple to +realize (sending an e-mail to a specific address, calling an URL, etc.) + +Following protocols are implemented: +- ASCII based. This could also be handled by programs like `chat', but is + included for completness. You can use simple send/expect strings to define + the communication. + +- SCRIPT based. This could be considered as an enhanced ASCII mode and + depends on the available scripting languages, that are integrated. + Currently SLang and Lua are supported, others may follow. + +- TAP (Telocator Alphanumeric Protocol). This is the result of the earlier + used IXO and PET protocols. The documentation for this protocol is + available via the Web: + http://www.mot.com/MIMS/MSPG/pcia_protocols/tap_v1p8/table_of_contents.html + (Thanks a lot to Motorola!) + +- UCP (Universal Computer Protocol). D2privat has kindly made the + description available to me, thanks a lot to Mannesmann Mobilfunk! Lots of + the protocol is NOT implemented, as this makes not much sense on dialup + lines. + +ATTENTION: If someone has the complete documentation for any other used +protocol for pager like devices, I would be glad if she/he could make them +available for me. If I had a testing possibility I'll try to implement that +protocol. + +Bugs, Problems, Comments: +~~~~~~~~~~~~~~~~~~~~~~~~~ +Please send any bug report, any encountered problem (with a detailed +description as possible) and any comments to or +. + +Files: +~~~~~~ +README: This file +COPYING.GPL, COPYRIGHT: About distributing the software +INSTALL: A pointer into the README file +BUGREPORT: How to report a bug +Makefile, Config: Make rules +yaps.rc: Sample configuration file +yaps.c: The driver program +valid.c, valid.h: Support routines for the driver program +remaining *.c, *.h: The paging library +yaps.html, yaps.doc: Documentation in HTML and plain text +yaps.1: A minimal manual page +yaps.lsm: The Linux Software Map entry +contrib/: Files not supported by the author diff --git a/asc.c b/asc.c new file mode 100644 index 0000000..ced4733 --- /dev/null +++ b/asc.c @@ -0,0 +1,372 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +# include +# include +# include +# include +# include "pager.h" + +/*{{{ typedefs */ +typedef struct { +# ifndef NDEBUG +# define MAGIC MKMAGIC('a', 's', 'c', '\0') + long magic; +# endif /* NDEBUG */ + void *sp; + void *ctab; + void (*logger) (char, char *, ...); + char *callid; + int deftout; + char *alogin; + char *alogout; + char *apid; + char *amsg; + char *anext; + char *async; + + date_t delay; + date_t expire; + Bool rds; +} asc; +/*}}}*/ +/*{{{ convert */ +static char * +escape_string (asc *a, char *str) +{ + int c; + char ch, prev; + char *ret; + int n; + + if (ret = malloc (strlen (str) * 2 + 16)) { + prev = '\0'; + ch = '\0'; + for (n = 0; *str; prev = ch) { + c = cv_conv (a -> ctab, (char_t) *str++); + if (c < 0) + continue; + ch = (char) c; + if ((ch == '<') && isspace (prev)) { + ret[n++] = '\\'; + ret[n++] = '<'; + } else if (ch == ' ') { + ret[n++] = '\\'; + ret[n++] = 's'; + } else if (ch == '\a') { + ret[n++] = '\\'; + ret[n++] = 'a'; + } else if (ch == '\b') { + ret[n++] = '\\'; + ret[n++] = 'b'; + } else if (ch == '\f') { + ret[n++] = '\\'; + ret[n++] = 'f'; + } else if (ch == '\n') { + ret[n++] = '\\'; + ret[n++] = 'n'; + } else if (ch == '\r') { + ret[n++] = '\\'; + ret[n++] = 'r'; + } else if (ch == '\t') { + ret[n++] = '\\'; + ret[n++] = 't'; + } else if (ch) { + if ((ch == '\\') || (ch == '^') || (ch == '%')) + ret[n++] = '\\'; + ret[n++] = ch; + } + } + ret[n] = '\0'; + } + return ret; +} + +static char * +convert_asc (asc *a, char *pat, char *pid, char *msg) +{ + char *str; + int siz, len; + char *ptr; + int plen; + char scr[32]; + + str = NULL; + siz = 0; + len = 0; + while (*pat) { + if (len + 2 >= siz) { + siz += 32; + if (! (str = Realloc (str, siz + 2))) + break; + } + if (*pat == '\\') { + ++pat; + if (*pat) { + ptr = NULL; + switch (*pat) { + case 'C': ptr = a -> callid; break; + case 'P': ptr = pid; break; + case 'M': ptr = msg; break; + case 'R': + strcpy (scr, (a -> rds ? "1" : "0")); + ptr = scr; + break; + default: + str[len++] = '\\'; + str[len++] = *pat; + break; + } + if (ptr && (ptr = escape_string (a, ptr))) { + if (plen = strlen (ptr)) { + if (len + plen + 2 >= siz) { + siz = len + plen + 32; + if (! (str = Realloc (str, siz))) + break; + } + strcpy (str + len, ptr); + len += plen; + } + free (ptr); + } + ++pat; + } + } else if (! iscntrl (*pat)) + str[len++] = *pat++; + else { + str[len++] = ' '; + ++pat; + } + } + if (str) + str[len] = '\0'; + return str; +} +/*}}}*/ +/*{{{ general sending routine */ +static int +do_send (asc *a, char *what, char *pat, char *pid, char *msg) +{ + int ret; + char *str; + + ret = ERR_FAIL; + if (a && a -> sp) + if (pat && *pat) { + if (str = convert_asc (a, pat, pid, msg)) { + if (tty_send_expect (a -> sp, a -> deftout, str, NULL) != -1) { + V (1, ("Ascii %s sent\n", what)); + ret = NO_ERR; + } else + V (1, ("Unable to send %s\n", what)); + free (str); + } + } else + ret = NO_ERR; + return ret; +} +/*}}}*/ +/*{{{ login/logout/transmit/next/sync */ +int +asc_login (void *ap, string_t *cid) +{ + asc *a = (asc *) ap; + + MCHK (a); + if (a) { + if (a -> callid) + free (a -> callid); + a -> callid = sextract (cid); + } + return a ? do_send (a, "login", a -> alogin, NULL, NULL) : ERR_ABORT; +} + +int +asc_logout (void *ap) +{ + asc *a = (asc *) ap; + + MCHK (a); + return a ? do_send (a, "logout", a -> alogout, NULL, NULL) : ERR_FATAL; +} + +int +asc_transmit (void *ap, char *pid, char *msg) +{ + asc *a = (asc *) ap; + int n; + + MCHK (a); + if (! a) + return ERR_FATAL; + if ((n = do_send (a, "pagerid", a -> apid, pid, NULL)) != NO_ERR) + return n; + return do_send (a, "message", a -> amsg, NULL, msg); +} + +int +asc_next (void *ap) +{ + asc *a = (asc *) ap; + + MCHK (a); + return a ? do_send (a, "next", a -> anext, NULL, NULL) : ERR_FATAL; +} + +int +asc_sync (void *ap) +{ + asc *a = (asc *) ap; + + MCHK (a); + return a ? do_send (a, "sync", a -> async, NULL, NULL) : ERR_FATAL; +} +/*}}}*/ +/*{{{ config */ +void +asc_config (void *ap, void (*logger) (char, char *, ...), + int deftout, char *alogin, char *alogout, char *apid, char *amsg, char *anext, char *async, + date_t *delay, date_t *expire, Bool rds) +{ + asc *a = (asc *) ap; + + MCHK (a); + if (a) { + a -> logger = logger; + if (deftout != -1) + a -> deftout = deftout; + if (alogin) { + if (a -> alogin) + free (a -> alogin); + a -> alogin = strdup (alogin); + } + if (alogout) { + if (a -> alogout) + free (a -> alogout); + a -> alogout = strdup (alogout); + } + if (apid) { + if (a -> apid) + free (a -> apid); + a -> apid = strdup (apid); + } + if (amsg) { + if (a -> amsg) + free (a -> amsg); + a -> amsg = strdup (amsg); + } + if (anext) { + if (a -> anext) + free (a -> anext); + a -> anext = strdup (anext); + } + if (async) { + if (a -> async) + free (a -> async); + a -> async = strdup (async); + } + if (delay) + a -> delay = *delay; + else + dat_clear (& a -> delay); + if (expire) + a -> expire = *expire; + else + dat_clear (& a -> expire); + a -> rds = rds; + } +} + +void +asc_set_convtable (void *ap, void *ctab) +{ + asc *a = (asc *) ap; + + MCHK (a); + if (a) { + if (a -> ctab) + cv_free (a -> ctab); + a -> ctab = ctab; + } +} + +void +asc_add_convtable (void *ap, void *ctab) +{ + asc *a = (asc *) ap; + + MCHK (a); + if (a) { + if (! a -> ctab) + a -> ctab = cv_new (); + if (a -> ctab) + cv_merge (a -> ctab, ctab, True); + } +} +/*}}}*/ +/*{{{ new/free/etc */ +void * +asc_new (void *sp) +{ + asc *a; + + if (a = (asc *) malloc (sizeof (asc))) { +# ifndef NDEBUG + a -> magic = MAGIC; +# endif /* NDEBUG */ + a -> sp = sp; + a -> ctab = NULL; + a -> logger = NULL; + a -> callid = NULL; + a -> deftout = 10; + a -> alogin = NULL; + a -> alogout = NULL; + a -> apid = NULL; + a -> amsg = NULL; + a -> anext = NULL; + a -> async = NULL; + dat_clear (& a -> delay); + dat_clear (& a -> expire); + a -> rds = False; + } + return (void *) a; +} + +void * +asc_free (void *ap) +{ + asc *a = (asc *) ap; + + MCHK (a); + if (a) { + if (a -> ctab) + cv_free (a -> ctab); + if (a -> callid) + free (a -> callid); + if (a -> alogin) + free (a -> alogin); + if (a -> alogout) + free (a -> alogout); + if (a -> apid) + free (a -> apid); + if (a -> amsg) + free (a -> amsg); + if (a -> anext) + free (a -> anext); + if (a -> async) + free (a -> async); + free (a); + } + return NULL; +} + +int +asc_preinit (void) +{ + return 0; +} + +void +asc_postdeinit (void) +{ +} +/*}}}*/ diff --git a/cfg.c b/cfg.c new file mode 100644 index 0000000..b2e6c72 --- /dev/null +++ b/cfg.c @@ -0,0 +1,553 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +# include +# include +# include +# include +# include "pager.h" + +/*{{{ typedefs */ +typedef unsigned long hash_t; + +typedef struct _entry { + hash_t hval; + char *var; + char *val; + struct _entry + *next; +} entry; + +typedef struct _charc { + hash_t hval; + char *name; + struct _charc + *next; +} charc; + +typedef struct _block { +# ifndef NDEBUG +# define MAGIC MKMAGIC ('c', 'f', 'g', '\0') + long magic; +# endif /* NDEBUG */ + hash_t hval; + char *name; + charc *use; + Bool done; + entry *e; + char *sep; + int seplen; + struct _block + *next; +} block; + +typedef struct _fifo { + void *dat; + struct _fifo + *next; +} fifo; + +typedef struct _fpstack { + FILE *fp; + struct _fpstack + *up; +} fpstack; +/*}}}*/ +/*{{{ support routines */ +static hash_t +calc_hash (char *str) +{ + hash_t hval; + + for (hval = 0; *str; ) { + hval <<= 2; + hval += *str++ & 0xff; + hval >>= 1; + } + return hval; +} + +static Bool +issame (hash_t h1, char *s1, hash_t h2, char *s2) +{ + if (h1 == h2) + if ((! s1) && (! s2)) + return True; + else if (s1 && s2 && (s1[0] == s2[0]) && (! strcmp (s1, s2))) + return True; + return False; +} + +static block * +new_block (char *bname, hash_t hval, char *sep) +{ + block *b; + + if (b = (block *) malloc (sizeof (block))) { + b -> name = NULL; + if ((! bname) || (b -> name = strdup (bname))) { + b -> sep = NULL; + b -> seplen = 0; + if ((! sep) || (b -> sep = strdup (sep))) { +# ifndef NDEBUG + b -> magic = MAGIC; +# endif /* NDEBUG */ + b -> hval = hval; + b -> use = NULL; + b -> done = False; + b -> e = NULL; + b -> next = NULL; + } else { + if (b -> name) + free (b -> name); + free (b); + b = NULL; + } + } else { + free (b); + b = NULL; + } + } + return b; +} + +static void +add_entry (block *base, block *b, char *var, char *val) +{ + hash_t hval; + entry *e, *etmp; + Bool add; + char *sav; + + if (b && var) { + if (*var == '+') { + ++var; + add = True; + } else + add = False; + hval = calc_hash (var); + for (e = b -> e; e; e = e -> next) + if (issame (hval, var, e -> hval, e -> var)) + break; + if (e) { + if (e -> val) + if (add) { + if (val && *val) { + sav = e -> val; + if (e -> val = malloc (strlen (sav) + strlen (val) + base -> seplen + 2)) { + sprintf (e -> val, "%s%s%s", sav, (base -> sep ? base -> sep : ""), val); + free (sav); + val = NULL; + } else + e -> val = sav; + } + } else { + free (e -> val); + e -> val = NULL; + } + } else if (e = (entry *) malloc (sizeof (entry))) + if (e -> var = strdup (var)) { + e -> hval = hval; + e -> val = NULL; + e -> next = b -> e; + b -> e = e; + } else { + free (e); + e = NULL; + } + if (e && (! e -> val) && val && *val) { + if (add && (base != b)) { + for (etmp = base -> e; etmp; etmp = etmp -> next) + if (issame (hval, var, etmp -> hval, etmp -> var)) + break; + if (etmp && etmp -> val) + if (e -> val = malloc (strlen (etmp -> val) + strlen (val) + base -> seplen + 2)) + sprintf (e -> val, "%s%s%s", etmp -> val, (base -> sep ? base -> sep : ""), val); + } + if (! e -> val) + e -> val = strdup (val); + } + } +} +/*}}}*/ +/*{{{ read configuration */ +void * +cfg_new (char *sep) +{ + return (void *) new_block (NULL, 0, sep); +} + +void * +cfg_read (char *fname, void *bp, char *sep) +{ + FILE *fp; + fpstack *fcur, *ftmp; + char *gline, *line; + char *ptr, *use; + charc *prv, *tmp; + block *base, *prev, *cur; + hash_t hval; + char *var, *val; + int plen; + Bool done; + int siz, len; + char *temp; + + if (bp) + base = (block *) bp; + else + if (! (base = new_block (NULL, 0, sep))) + return NULL; + cur = base; + if (fp = fopen (fname, "r")) + if (fcur = (fpstack *) malloc (sizeof (fpstack))) { + fcur -> fp = fp; + fcur -> up = NULL; + while (fcur) { + while (gline = getline (fcur -> fp, True)) { + for (line = gline; isspace (*line); ++line) + ; + if ((! *line) || (*line == '#')) { + free (gline); + continue; + } + if (*line == '[') { + use = NULL; + if (ptr = strchr (line, ']')) { + *ptr++ = '\0'; + while (isspace (*ptr)) + ++ptr; + if (*ptr) + use = ptr; + } + ptr = line + 1; + if (! *ptr) + cur = base; + else { + hval = calc_hash (ptr); + for (cur = base, prev = NULL; cur; cur = cur -> next) + if (issame (hval, ptr, cur -> hval, cur -> name)) + break; + else + prev = cur; + if ((! cur) && (cur = new_block (ptr, hval, NULL))) + if (prev) + prev -> next = cur; + if (cur && use) { + if (cur -> use) + for (prv = cur -> use; prv -> next; prv = prv -> next) + ; + else + prv = NULL; + while (*use) { + ptr = use; + use = skipch (ptr, ','); + if (tmp = (charc *) malloc (sizeof (charc))) + if (tmp -> name = strdup (ptr)) { + tmp -> hval = 0; + tmp -> next = NULL; + if (prv) + prv -> next = tmp; + else + cur -> use = tmp; + prv = tmp; + } else + free (tmp); + } + } + } + if (! cur) { + free (gline); + break; + } + } else if (*line == '|') { + ++line; + while (isspace (*line)) + ++line; + if (fp = fopen (line, "r")) + if (ftmp = (fpstack *) malloc (sizeof (fpstack))) { + ftmp -> fp = fp; + ftmp -> up = fcur; + fcur = ftmp; + } else + fclose (fp); + } else { + var = line; + val = skip (var); + if (var = strdup (var)) { + temp = NULL; + if ((*val == '{') && (! *(val + 1))) { + done = False; + siz = 0; + len = 0; + while (ptr = getline (fcur -> fp, False)) { + if ((*ptr != '}') || *(ptr + 1)) { + plen = strlen (ptr); + if (len + plen + 2 >= siz) { + siz = len + plen + 256; + if (! (temp = Realloc (temp, siz + 2))) { + siz = 0; + done = True; + } + } + if (len + plen < siz) { + if (len) + temp[len++] = '\n'; + strcpy (temp + len, ptr); + len += plen; + } + } else + done = True; + free (ptr); + if (done) + break; + } + if (temp) { + temp[len] = '\0'; + val = temp; + } else + val = NULL; + } else if (*val == '\\') + ++val; + add_entry (base, cur, var, val); + if (temp) + free (temp); + free (var); + } + } + free (gline); + } + ftmp = fcur; + fcur = fcur -> up; + fclose (ftmp -> fp); + free (ftmp); + } + } else + fclose (fp); + return (void *) base; +} +/*}}}*/ +/*{{{ add/change configuration */ +void +cfg_modify (void *bp, char *bname, char *var, char *val) +{ + block *base = (block *) bp; + block *use, *prv; + hash_t hval; + + MCHK (base); + if (base) { + if (! bname) + use = base; + else { + hval = calc_hash (bname); + for (use = base -> next, prv = base; use; use = use -> next) + if (issame (hval, bname, use -> hval, use -> name)) + break; + else + prv = use; + if (! use) + use = new_block (bname, hval, NULL); + } + if (use) + add_entry (base, use, var, val); + } +} +/*}}}*/ +/*{{{ end (free up) configuration */ +void * +cfg_end (void *bp) +{ + block *b = (block *) bp; + block *tmp; + charc *run; + entry *e; + + MCHK (b); + while (b) { + if (b -> name) + free (b -> name); + while (b -> use) { + run = b -> use; + b -> use = b -> use -> next; + if (run -> name) + free (run -> name); + free (run); + } + while (b -> e) { + e = b -> e; + b -> e = b -> e -> next; + if (e -> var) + free (e -> var); + if (e -> val) + free (e -> val); + free (e); + } + if (b -> sep) + free (b -> sep); + tmp = b; + b = b -> next; + free (tmp); + } + return NULL; +} +/*}}}*/ +/*{{{ retrieving */ +static char * +do_get (void *bp, char *bname, char *var, Bool glob, char *dflt) +{ + block *b = (block *) bp; + block *run, *tmp; + entry *e; + fifo *f, *l, *ft; + charc *use; + char *ptr, *sav; + charc *vars, *vprv, *vtmp, *vrun; + Bool first; + hash_t hval; + + MCHK (b); + if (! (var = strdup (var))) + return NULL; + vars = NULL; + vprv = NULL; + for (ptr = var; *ptr; ) { + sav = ptr; + ptr = skipch (ptr, ','); + if (vtmp = (charc *) malloc (sizeof (charc))) + if (vtmp -> name = strdup (sav)) { + vtmp -> hval = calc_hash (vtmp -> name); + vtmp -> next = NULL; + if (vprv) + vprv -> next = vtmp; + else + vars = vtmp; + vprv = vtmp; + } else + free (vtmp); + } + free (var); + if (! vars) + return NULL; + e = NULL; + f = NULL; + l = NULL; + first = True; + while ((! e) && bname) { + hval = calc_hash (bname); + for (run = b -> next; run; run = run -> next) + if (issame (hval, bname, run -> hval, run -> name)) + break; + bname = NULL; + if (run && (first || (! run -> done))) { + for (vrun = vars; vrun; vrun = vrun -> next) { + for (e = run -> e; e; e = e -> next) + if (issame (vrun -> hval, vrun -> name, e -> hval, e -> var)) + break; + if (e) + break; + } + if (! e) { + if (use = run -> use) { + if (first) { + for (tmp = b -> next; tmp; tmp = tmp -> next) + tmp -> done = False; + first = False; + } + for (; use; use = use -> next) + if (ft = (fifo *) malloc (sizeof (fifo))) { + ft -> dat = (void *) use; + ft -> next = NULL; + if (l) + l -> next = ft; + else { + f = ft; + l = ft; + } + } else + break; + } + if (f) { + ft = f; + f = f -> next; + if (! f) + l = NULL; + use = (charc *) ft -> dat; + bname = use -> name; + free (ft); + } else + bname = NULL; + run -> done = True; + } + } + } + if ((! e) && glob) + for (vrun = vars; vrun; vrun = vrun -> next) { + for (e = b -> e; e; e = e -> next) + if (issame (vrun -> hval, vrun -> name, e -> hval, e -> var)) + break; + if (e) + break; + } + while (vars) { + vtmp = vars; + vars = vars -> next; + free (vtmp -> name); + free (vtmp); + } + return e ? e -> val : dflt; +} + +static int +do_iget (void *bp, char *bname, char *var, Bool glob, int dflt) +{ + char *ret; + + ret = do_get (bp, bname, var, glob, NULL); + return ret ? atoi (ret) : dflt; +} + +static Bool +do_bget (void *bp, char *bname, char *var, Bool glob, int dflt) +{ + char *ret; + + ret = do_get (bp, bname, var, glob, NULL); + return ret ? ((*ret && strchr ("TtYy1+", *ret)) ? True : False) : dflt; +} + +char * +cfg_get (void *bp, char *bname, char *var, char *dflt) +{ + return do_get (bp, bname, var, True, dflt); +} + +int +cfg_iget (void *bp, char *bname, char *var, int dflt) +{ + return do_iget (bp, bname, var, True, dflt); +} + +Bool +cfg_bget (void *bp, char *bname, char *var, Bool dflt) +{ + return do_bget (bp, bname, var, True, dflt); +} + +char * +cfg_block_get (void *bp, char *bname, char *var, char *dflt) +{ + return do_get (bp, bname, var, False, dflt); +} + +int +cfg_block_iget (void *bp, char *bname, char *var, int dflt) +{ + return do_iget (bp, bname, var, False, dflt); +} + +Bool +cfg_block_bget (void *bp, char *bname, char *var, Bool dflt) +{ + return do_bget (bp, bname, var, False, dflt); +} +/*}}}*/ diff --git a/config.h b/config.h new file mode 100644 index 0000000..2bb8f39 --- /dev/null +++ b/config.h @@ -0,0 +1,134 @@ +/* -*- mode: c; mode: fold -*- */ +# ifndef __CONFIG_H +# define __CONFIG_H 1 + +/*{{{ changeable configuration */ +/* + * Define signal handling: + * POSIX_SIGNAL if you have the Posix sigaction() family + * BSD_SIGNAL if you have BSD like signal() handling + * SYSV_SIGNAL if you have SysV like signal() handling + * SIG_VOID_RETURN if your signal handler returns void + * SIG_INT_RETURN if your signal handler returns int + */ +# define POSIX_SIGNAL 1 +# define BSD_SIGNAL 0 +# define SYSV_SIGNAL 0 +# define SIG_VOID_RETURN 1 +# define SIG_INT_RETURN 0 + +/* + * Set each define to 1, if you have the matching header file, otherwise + * set it to 0. Remember, that some features may not available, if the + * header file is not available. + */ +/* + * Needed only by some systems, which do not define FD_SET etc. + * in sys/time.h + */ +# define HAVE_SYS_SELECT_H 0 + +/* + * If you have locales set this. This is useful to for character + * conversion/classification + */ +# define HAVE_LOCALE_H 0 + +/* + * If you have Posix regular expressions, set this. Otherwise a + * very weak replacement is used to find matching services + */ +# define HAVE_REGEX_H 0 + +/* + * one of these is required for SysV like lockfiles + */ +# define HAVE_SYS_SYSMACROS_H 1 +# define HAVE_SYS_MKDEV_H 0 + +/* + * Some system do not define the getopt stuff in unistd.h, but in + * a own include file getopt.h. Or (like the GNU libc) defines there + * the extended getopt_long version. + */ +# define HAVE_GETOPT_H 0 + +/* + * Set each define to 1, if your library supports the function, otherwise + * set it to 0. See above for note. + */ +/* + * If the library contains this function, a call to it is required + * to get valid return values from localtime + */ +# define HAVE_TZSET 0 + +/* + * If these are not set, chmod()/chown() are used + */ +# define HAVE_FCHMOD 0 +# define HAVE_FCHOWN 0 + +/* + * If you have sigsetjmp() you definitly want to set this, otherwise + * longjmp() from the signal handler leads into chaos + */ +# define HAVE_SIGSETJMP 0 + +/* + * Memory access functions. Nearly everybody has memcpy()/memset(), so + * choose the bcopy()/bzero() part only if you are missing the other two + */ +# define HAVE_MEMCPY 1 +# define HAVE_BCOPY 0 +# define HAVE_MEMSET 1 +# define HAVE_BZERO 0 + +/* + * If your library supports getopt at all + */ +# define HAVE_GETOPT 1 + +/* + * If your library supports long options (getopt_long(3)), then set this + * to one + */ +# define HAVE_GETOPT_LONG 0 + +/* + * If you have getopt(3), but your headerfile(s) does not declare + * optind/optarg set this to 1, otherwise to 0 + */ +# define NEED_OPTIND_OPTARG 0 + +/* + * If your realloc(3) function cannot handle realloc (NULL, size), then + * set this to 1, otherwise to 0 + */ +# define BROKEN_REALLOC 1 + +/* -------------- END OF CHANGEABLE PART ------------------ */ +/*}}}*/ +/*{{{ auto configuration part */ +/* + * Autoconfiguration + */ +# if ! HAVE_MEMCPY +# if HAVE_BCOPY +# define memcpy(aa,bb,cc) bcopy((bb),(aa),(cc)) +# else /* HAVE_BCOPY */ +# error "Neither memcopy() nor bcopy() available, aborted" +# endif /* HAVE_BCOPY */ +# endif /* BSD */ + +# if BROKEN_REALLOC +# define Realloc(ppp,sss) ((ppp) ? realloc ((ppp), (sss)) : malloc ((sss))) +# else /* BROKEN_REALLOC */ +# define Realloc realloc +# endif /* BROKEN_REALLOC */ + +# ifndef __GCC__ +# define inline +# endif /* __GCC__ */ +/*}}}*/ +# endif /* __CONFIG_H */ diff --git a/contrib/Makefile b/contrib/Makefile new file mode 100644 index 0000000..204aea1 --- /dev/null +++ b/contrib/Makefile @@ -0,0 +1,12 @@ +# -*- sh -*- +all: + @echo -n + +clean: + rm -f *~ core + +install: + @echo -n + +depend: + @echo -n diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..ad0d042 --- /dev/null +++ b/contrib/README @@ -0,0 +1,10 @@ +Here all available contributed files, a short description and +the author: + +m2y.pl: Simple perl script to use yaps in a mail to pager + enviroment. Just an example, for real use you have + to enhance it significant. (Ulrich Dessauer) + +tap.sl: Example for usage of scripting as a protocol + replacement. This implements a minimal subset + of TAP. diff --git a/contrib/m2y.pl b/contrib/m2y.pl new file mode 100755 index 0000000..31bad5a --- /dev/null +++ b/contrib/m2y.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl +# -*- perl -*- +# Untested mail -> yaps gateway (just an example) +# (C) by Ulrich Dessauer +# Create some receiver address, e.g. @yaps. and +# instruct your mailer to forward each mail to this address to +# this script. Ensure that there is only one receiver per mail. +# +# To be really useful, the script must be expanded to check for +# illegal characters in receiver/message (in this case the >'< +# as this is used as quoting in the system() call) and handling +# of multiple receivers in one call, etc. Happy Hacking! +# +# Markus made some comments and improvments +# to make this script slowly useful. +# +$yaps = "/usr/local/bin/yaps"; +$mailer = "/usr/lib/sendmail -t"; +$send_response = 1; +# +$recv = $ARGV[0]; +$recv = (split ('@', $recv))[0]; +die "No receiver given\n" + unless $recv; +undef $msg; +undef $from; +while () { + chomp; + if ($_ =~ /^Subject: /i) { + $msg = $_; + $msg =~ s/^Subject: //i; + undef $msg + if $msg eq ""; + } elsif ($_ =~ /^From: /i) { + $from = $_; + $from =~ s/^From: //i; + } +} +die "No message found\n" + unless $msg; +die "No sender found\n" + unless $from; +# +# Avoid apostroph in command line (otherwise the shell will stumble) +$recv =~ s/\'/\`/g; +$msg =~ s/\'/\`/g; +$from =~ s/\'/\`/g; +$n = system ("$yaps '$recv' '(EMail from $from) $msg'"); +die "Unable to send message to $recv\n" + if $n; +if ($send_response) { + die "Unable to invoke mailer for response\n" + unless open (OUT, "|$mailer"); + print OUT "From: yaps-mail\n"; + print OUT "To: $from\n"; + print OUT "Subject: Message sent to $recv\n"; + print OUT "\n"; + print OUT "Your message $msg\nto $recv has been sent\n"; + close (OUT); +} diff --git a/contrib/tap.sl b/contrib/tap.sl new file mode 100644 index 0000000..44bc1fd --- /dev/null +++ b/contrib/tap.sl @@ -0,0 +1,122 @@ +% -*- slang -*- +% +% (C) by Ulrich Dessauer +% +% This is an example for scripting by re-implementing TAP +% using SLang. This is a minimal implementation and has not +% all the fancy features the internal version has, but it +% should only be an example for the usage of scripting. +% +% +% Add these lines if you want to use it in your yaps.rc +% protocol script +% use-call-id False +% script-type SLang +% script-name /path/to/this/file/tap.sl +% scr-login tap_login +% scr-logout tap_logout +% scr-pagerid tap_pagerid +% scr-message tap_message +% scr-next tap_next +% scr-sync tap_sync +% +variable tap_pid = Null_String; +variable tap_t1 = 2; +variable tap_t3 = 10; + +define +tap_login (callid) +{ + variable n, ep; + + for (n = 0; n < 3; ++n) { + !if (send ("\r")) + return (ERR_FATAL); + if (expect (tap_t1, "ID=", 1) == 1) + break; + } + if (n == 3) + return (ERR_FATAL); + for (n = 0; n < 3; ++n) { + !if (send ("\033PG1\r")) + return (ERR_FATAL); + ep = expect (tap_t3, "\x06\r", "\x15\r", "\x1b\x04\r", 3); + if (ep == 1) + break; + else if ((ep == 2) or (ep == 3)) + return (ERR_ABORT); + } + if (n == 3) + return (ERR_FATAL); + if (expect (tap_t3, "\x1b[p\r", 1) != 1) + return (ERR_ABORT); + return (NO_ERR); +} + +define +tap_logout (dummy) +{ + variable n, ep; + + for (n = 0; n < 3; ++n) { + !if (send ("\x04\r")) + return (ERR_FATAL); + ep = expect (tap_t3, "\x1b\x04\r", "\x1e\r", 2); + if (ep == 1) + break; + else if (ep != 2) + return (ERR_FATAL); + } + if (n == 3) + return (ERR_FATAL); + return (NO_ERR); +} + +define +tap_pagerid (pid) +{ + tap_pid = conv (pid); + return (NO_ERR); +} + +define +tap_message (msg) +{ + variable str; + variable chk; + variable n, ep, len; + + str = "\x02" + tap_pid + "\r" + conv (msg) + "\r" + "\x03"; + len = strlen (str); + chk = 0; + for (n = 0; n < len; ++n) + chk = chk + (str[n] & 0xff); + str = str + Sprintf ("%c%c%c\r", ((chk shr 8) & 0xf) + 0x30, ((chk shr 4) & 0xf) + 0x30, (chk & 0xf) + 0x30, 3); + for (n = 0; n < 3; ++n) { + !if (send (str)) + return (ERR_FATAL); + ep = expect (tap_t3, "\x06\r", "\x15\r", "\x1e\r", "\x1b\x04\r", 4); + if (ep == 1) + break; + else if ((ep == 2) or (ep == 3)) + return (ERR_FATAL); + else if (ep == 4) + return (ERR_ABORT); + } + if (n == 3) + return (ERR_FATAL); + return (NO_ERR); +} + +define +tap_next (dummy) +{ + return (NO_ERR); +} + +define +tap_sync (dummy) +{ + return (ERR_FATAL); +} + diff --git a/cv.c b/cv.c new file mode 100644 index 0000000..9ac50b3 --- /dev/null +++ b/cv.c @@ -0,0 +1,221 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +# include +# include +# include +# include "pager.h" + +/*{{{ typedefs */ +typedef struct { +# ifndef NDEBUG +# define MAGIC MKMAGIC ('c', 'o', 'n', 'v') + long magic; +# endif /* NDEBUG */ + int c[256]; /* the conversion table */ +} conv; +/*}}}*/ +/*{{{ new/free */ +void * +cv_new (void) +{ + conv *c; + int n; + + if (c = (conv *) malloc (sizeof (conv))) { +# ifndef NDEBUG + c -> magic = MAGIC; +# endif /* NDEBUG */ + for (n = 0; n < 256; ++n) + c -> c[n] = n; + } + return (void *) c; +} + +void * +cv_free (void *cv) +{ + conv *c = (conv *) cv; + + MCHK (c); + if (c) + free (c); + return NULL; +} +/*}}}*/ +/*{{{ reverse */ +void * +cv_reverse (void *src) +{ + conv *s = (conv *) src; + conv *c; + int n; + + MCHK (s); + if (c = (conv *) cv_new ()) + for (n = 0; n < 256; ++n) + if (s -> c[n] != -1) + c -> c[s -> c[n]] = n; + return (void *) c; +} +/*}}}*/ +/*{{{ define/undefine */ +static char_t +getval (char *str) +{ + char_t ret; + + if (isdigit (*str)) + ret = (char_t) strtol (str, NULL, 0); + else if (! *(str + 1)) + ret = (char_t) *str; + else if (*str == '^') { + ++str; + ret = (char_t) (*str == '?' ? 0x7f : (*str & 0x1f)); + } else if (*str == '\\') { + ++str; + switch (*str) { + case 'a': ret = (char_t) '\a'; break; + case 'b': ret = (char_t) '\b'; break; + case 'e': ret = (char_t) '\x1b'; break; + case 'f': ret = (char_t) '\f'; break; + case 'l': ret = (char_t) '\012'; break; + case 'n': ret = (char_t) '\n'; break; + case 'r': ret = (char_t) '\r'; break; + case 's': ret = (char_t) ' '; break; + case 't': ret = (char_t) '\t'; break; + case 'v': ret = (char_t) '\v'; break; + default: ret = (char_t) *str; break; + } + } else + ret = (char_t) *str; + return ret; +} + +void +cv_define (void *cv, char_t src, char_t dst) +{ + conv *c = (conv *) cv; + + MCHK (c); + if (c) + c -> c[src] = dst; +} + +void +cv_sdefine (void *cv, char *src, char *dst) +{ + cv_define (cv, getval (src), getval (dst)); +} + +void +cv_undefine (void *cv, char_t ch) +{ + conv *c = (conv *) cv; + + MCHK (c); + if (c) + c -> c[ch] = ch; +} + +void +cv_sundefine (void *cv, char *ch) +{ + cv_undefine (cv, getval (ch)); +} + +void +cv_invalid (void *cv, char_t ch) +{ + conv *c = (conv *) cv; + + MCHK (c); + if (c) + c -> c[ch] = -1; +} + +void +cv_sinvalid (void *cv, char *ch) +{ + cv_invalid (cv, getval (ch)); +} +/*}}}*/ +/*{{{ read/write table */ +int +cv_read_table (void *cv, char *fname) +{ + conv *c = (conv *) cv; + FILE *fp; + char *line; + char *sp, *dp; + + MCHK (c); + if ((! c) || (! (fp = fopen (fname, "r")))) + return -1; + while (line = getline (fp, True)) { + for (sp = line; isspace (*sp); ++sp) + ; + if (*sp && (*sp != '#')) { + dp = skip (sp); + skip (dp); + if (*sp) + if (*dp) + c -> c[getval (sp)] = getval (dp); + else + c -> c[getval (sp)] = -1; + + } + free (line); + } + fclose (fp); + return 0; +} + +int +cv_write_table (void *cv, char *fname) +{ + conv *c = (conv *) cv; + FILE *fp; + int n; + + MCHK (c); + if ((! c) || (! (fp = fopen (fname, "w")))) + return -1; + fprintf (fp, "#\tThis file is generated automatically\n"); + for (n = 0; n < 256; ++n) { + if ((! (n & 0x80)) && isprint (n)) + fprintf (fp, "#\t%c\n", (char) n); + if (c -> c[n] != -1) + fprintf (fp, "0x%02x\t0x%02x\n", n, c -> c[n]); + else + fprintf (fp, "0x%02x\n", n); + } + fclose (fp); + return 0; +} +/*}}}*/ +/*{{{ merging */ +void +cv_merge (void *cv, void *in, Bool second) +{ + conv *c = (conv *) cv, + *i = (conv *) in; + int n; + + MCHK (c); + MCHK (i); + if (c && i) + for (n = 0; n < 256; ++n) + if (second || (c -> c[n] == n)) + c -> c[n] = i -> c[n]; +} +/*}}}*/ +/*{{{ converting */ +int +cv_conv (void *cv, char_t ch) +{ + conv *c = (conv *) cv; + + MCHK (c); + return c ? c -> c[ch] : (int) ch; +} +/*}}}*/ diff --git a/data.c b/data.c new file mode 100644 index 0000000..6dc9496 --- /dev/null +++ b/data.c @@ -0,0 +1,6 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +# include "pager.h" + +int verbose = 0; +int (*verbout) (char *, ...) = verbose_out; diff --git a/lua.c b/lua.c new file mode 100644 index 0000000..73e27ce --- /dev/null +++ b/lua.c @@ -0,0 +1,442 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +# ifdef SCRIPT_LUA +# include +# include +# include +# include +# include +# include +# include "pager.h" +# include "script.h" + +# define STARTUP "Startup.lua" + +/*{{{ statics & callable functions */ +static Bool isinit = False; +static double lua_no_err = (double) NO_ERR, + lua_err_fail = (double) ERR_FAIL, + lua_err_fatal = (double) ERR_FATAL, + lua_err_abort = (double) ERR_ABORT; +static script *ls = NULL; +static char *lline = NULL; +static int lsiz = 0; +static char *lcb = NULL; + +static void +lua_logger (void) +{ + lua_Object obj; + char *sav, *str; + char typ; + + if (((obj = lua_getparam (1)) != LUA_NOOBJECT) && + (str = lua_getstring (obj)) && + (sav = strdup (str))) { + typ = *sav; + if (((obj = lua_getparam (2)) == LUA_NOOBJECT) || + (! (str = lua_getstring (obj)))) { + str = sav; + typ = LG_INF; + } + if (ls && ls -> logger) + (*ls -> logger) (typ, "%s\n", str); + free (sav); + } +} + +static void +lua_callback (void *sp, string_t *s, char_t sep, void *data) +{ + int len; + char *str; + lua_Object obj; + + if (str = sextract (s)) { + len = strlen (str); + if (len + 2 >= lsiz) { + lsiz = len + 64; + if (! (lline = Realloc (lline, lsiz + 4))) + lsiz = 0; + } + if (lline) { + sprintf (lline, "%s%c", str, (char) sep); + if (lcb && (obj = lua_getglobal (lcb)) && lua_isfunction (obj)) { + lua_pushstring (lline); + lua_callfunction (obj); + } + } + free (str); + } +} + +static void +lua_setcb (void) +{ + lua_Object obj1, obj2; + char *sep, *func; + + if ((obj1 = lua_getparam (1)) != LUA_NOOBJECT) + obj2 = lua_getparam (2); + else + obj2 = LUA_NOOBJECT; + if (lcb) { + free (lcb); + lcb = NULL; + } + if ((obj1 == LUA_NOOBJECT) || (! lua_isstring (obj1)) || (! (sep = lua_getstring (obj1)))) { + if (ls && ls -> sp) + tty_set_line_callback (ls -> sp, NULL, NULL, NULL); + } else { + if (ls && ls -> sp) + tty_set_line_callback (ls -> sp, lua_callback, sep, NULL); + if ((obj2 != LUA_NOOBJECT) && lua_isstring (obj2) && (func = lua_getstring (obj2))) + lcb = strdup (func); + } +} + +static void +lua_get_line (void) +{ + if (lline) + lua_pushstring (lline); + else + lua_pushnil (); +} + +static void +lua_hangup (void) +{ + lua_Object obj; + double sec; + int msec; + + if (((obj = lua_getparam (1)) != LUA_NOOBJECT) && + lua_isnumber (obj)) { + sec = lua_getnumber (obj); + msec = (int) (sec * 1000.0); + } else + msec = 500; + if (ls && ls -> sp) + tty_hangup (ls -> sp, msec); +} + +static void +do_send (Bool dcv) +{ + int ret; + lua_Object obj; + char *str; + + ret = 0; + if (((obj = lua_getparam (1)) != LUA_NOOBJECT) && + lua_isstring (obj) && (str = lua_getstring (obj))) + if (ls && ls -> sp) { + if (dcv) + str = scr_convert (ls, str); + if (str) { + if (tty_send_string (ls -> sp, str) != -1) + ret = 1; + if (dcv) + free (str); + } + } + if (ret) + lua_pushnumber (1.0); + else + lua_pushnil (); +} + +static void +lua_send (void) +{ + do_send (False); +} + +static void +lua_csend (void) +{ + do_send (True); +} + +static void +lua_expect (void) +{ + int ret; + lua_Object obj; + int tout; + char *str; + int cnt, siz; + char **ex; + int *len; + int start; + int n; + + if (((obj = lua_getparam (1)) != LUA_NOOBJECT) && lua_isnumber (obj)) { + tout = (int) lua_getnumber (obj); + start = 2; + } else { + tout = 5; + start = 1; + } + ex = NULL; + cnt = 0; + siz = 0; + while (((obj = lua_getparam (start)) != LUA_NOOBJECT) && + lua_isstring (obj) && (str = lua_getstring (obj))) { + if (cnt >= siz) { + siz += 4; + if (! (ex = (char **) Realloc (ex, (siz + 2) * sizeof (char *)))) + break; + } + if (ex[cnt] = strdup (str)) + ++cnt; + } + ret = -1; + if (ex) { + ex[cnt] = NULL; + if ((cnt > 0) && (len = (int *) malloc ((cnt + 1) * sizeof (int)))) { + for (n = 0; n < cnt; ++n) + len[n] = strlen (ex[n]); + len[cnt] = 0; + if (ls && ls -> sp) + ret = tty_expect_list (ls -> sp, tout, ex, len); + free (len); + for (n = 0; n < cnt; ++n) + free (ex[n]); + } + free (ex); + } + lua_pushnumber ((double) ret); +} + +static void +lua_send_expect (void) +{ + int ret; + lua_Object obj; + int tout; + char *str; + + ret = 0; + if ((obj = lua_getparam (1)) != LUA_NOOBJECT) { + if (lua_isnumber (obj)) { + tout = (int) lua_getnumber (obj); + obj = lua_getparam (2); + } + if ((obj != LUA_NOOBJECT) && lua_isstring (obj) && (str = lua_getstring (obj))) + if (ls && ls -> sp && (tty_send_expect (ls -> sp, tout, str, NULL) != -1)) + ret = 1; + } + if (ret) + lua_pushnumber (1.0); + else + lua_pushnil (); +} + +static void +lua_drain (void) +{ + lua_Object obj; + int sec; + + if (((obj = lua_getparam (1)) != LUA_NOOBJECT) && lua_isnumber (obj)) + sec = (int) lua_getnumber (obj); + else + sec = 1; + if (ls && ls -> sp) + tty_drain (ls -> sp, sec); +} + +static void +lua_cvdef (void) +{ + lua_Object obj1, obj2; + char *src, *dst; + int n; + + if (ls) { + if (! ls -> ctab) + ls -> ctab = cv_new (); + if (ls -> ctab) + if (((obj1 = lua_getparam (1)) != LUA_NOOBJECT) && lua_isstring (obj1) && (src = lua_getstring (obj1))) { + if ((obj2 = lua_getparam (2)) == LUA_NOOBJECT) + cv_undefine (ls -> ctab, (char_t) *src); + else if (lua_isstring (obj2) && (dst = lua_getstring (obj2))) + cv_define (ls -> ctab, (char_t) *src, (char_t) *dst); + else if (lua_isnumber (obj2)) { + n = (int) lua_getnumber (obj2); + if (n < 0) + cv_invalid (ls -> ctab, (char_t) *src); + else + cv_define (ls -> ctab, (char_t) *src, (char_t) n); + } + } + } +} + +static void +lua_conv (void) +{ + char *ret; + lua_Object obj; + char *str; + + ret = NULL; + if (((obj = lua_getparam (1)) != LUA_NOOBJECT) && lua_isstring (obj) && (str = lua_getstring (obj))) + if (ls) + ret = scr_convert (ls, str); + if (ret) { + lua_pushstring (ret); + free (ret); + } else + lua_pushnil (); +} +/*}}}*/ +/*{{{ init/deinit */ +static int +lua_init (script *s, char *libdir) +{ + char *fname; + + if (! isinit) { + iolib_open (); + strlib_open (); + mathlib_open (); + lua_pushnumber (lua_no_err); lua_storeglobal ("NO_ERR"); + lua_pushnumber (lua_err_fail); lua_storeglobal ("ERR_FAIL"); + lua_pushnumber (lua_err_fatal); lua_storeglobal ("ERR_FATAL"); + lua_pushnumber (lua_err_abort); lua_storeglobal ("ERR_ABORT"); + /* void logger (string str); */ + lua_register ("logger", lua_logger); + /* void setcb ([string sep[, string|function func]]); */ + lua_register ("setcb", lua_setcb); + /* string|nil get_line (void); */ + lua_register ("get_line", lua_get_line); + /* void hangup ([num sec]); */ + lua_register ("hangup", lua_hangup); + /* num|nil send (string line); */ + lua_register ("send", lua_send); + /* num|nil csend (string line); */ + lua_register ("csend", lua_csend); + /* num expect (num tout, string, s1, ..., string sn); */ + lua_register ("expect", lua_expect); + /* num|nil send_expect (num tout, string str); */ + lua_register ("send_expect", lua_send_expect); + /* void drain ([num sec]); */ + lua_register ("drain", lua_drain); + /* void cvdef (string src[, string|num dst]); */ + lua_register ("cvdef", lua_cvdef); + /* string|nil conv (string str); */ + lua_register ("conv", lua_conv); + if (libdir && (fname = malloc (strlen (libdir) + sizeof (STARTUP) + 4))) { + sprintf (fname, "%s/%s", libdir, STARTUP); + if (access (fname, R_OK) != -1) + lua_dofile (fname); + free (fname); + } + lline = NULL; + lsiz = 0; + isinit = True; + } + return NO_ERR; +} + +static void +lua_deinit (script *s) +{ + if (lline) { + free (lline); + lline = NULL; + lsiz = 0; + } +} +/*}}}*/ +/*{{{ execution */ +static int +lua_execute (script *s, char *func, char *parm) +{ + int err; + lua_Object obj; + double ret; + + err = NO_ERR; + if ((obj = lua_getglobal (func)) && lua_isfunction (obj)) { + ls = s; + lua_beginblock (); + lua_pushnumber ((double) s -> delay.day); lua_storeglobal ("delay_day"); + lua_pushnumber ((double) s -> delay.mon); lua_storeglobal ("delay_mon"); + lua_pushnumber ((double) s -> delay.year); lua_storeglobal ("delay_year"); + lua_pushnumber ((double) s -> delay.hour); lua_storeglobal ("delay_hour"); + lua_pushnumber ((double) s -> delay.min); lua_storeglobal ("delay_min"); + lua_pushnumber ((double) s -> delay.sec); lua_storeglobal ("delay_sec"); + lua_pushnumber ((double) s -> expire.day); lua_storeglobal ("expire_day"); + lua_pushnumber ((double) s -> expire.mon); lua_storeglobal ("expire_mon"); + lua_pushnumber ((double) s -> expire.year); lua_storeglobal ("expire_year"); + lua_pushnumber ((double) s -> expire.hour); lua_storeglobal ("expire_hour"); + lua_pushnumber ((double) s -> expire.min); lua_storeglobal ("expire_min"); + lua_pushnumber ((double) s -> expire.sec); lua_storeglobal ("expire_sec"); + lua_pushnumber ((double) s -> rds); lua_storeglobal ("rds"); + if (parm) + lua_pushstring (parm); + else + lua_pushnil (); + if (lua_callfunction (obj)) + err = ERR_FATAL; + else if ((obj = lua_getresult (1)) && (obj != LUA_NOOBJECT)) { + ret = lua_getnumber (obj); + if (ret == lua_no_err) + err = NO_ERR; + else if (ret == lua_err_fail) + err = ERR_FAIL; + else if (ret == lua_err_fatal) + err = ERR_FATAL; + else if (ret == lua_err_abort) + err = ERR_ABORT; + } + lua_endblock (); + if (ls -> sp) + tty_set_line_callback (ls -> sp, NULL, NULL, NULL); + ls = NULL; + } + return err; +} +/*}}}*/ +/*{{{ loading */ +static int +lua_load_string (script *s, char *scr) +{ + return lua_dostring (scr) ? ERR_FATAL : NO_ERR; +} + +static int +lua_load_file (script *s, char *fname) +{ + return lua_dofile (fname) ? ERR_FATAL : NO_ERR; +} +/*}}}*/ +/*{{{ preinit/postdeinit/scriptentry */ +static int +lua_preinit (char *libdir) +{ + return lua_init (NULL, libdir); +} + +static void +lua_postdeinit (void) +{ + lua_deinit (NULL); +} + +funcs flua = { + "Lua", + lua_init, + lua_deinit, + lua_execute, + lua_load_string, + lua_load_file, + lua_preinit, + lua_postdeinit +}; +/*}}}*/ +# endif /* SCRIPT_LUA */ diff --git a/pager.h b/pager.h new file mode 100644 index 0000000..47f91c3 --- /dev/null +++ b/pager.h @@ -0,0 +1,207 @@ +/* -*- mode: c; mode: fold -*- */ +# ifndef __PAGER_H +# define __PAGER_H 1 +# include + +/*{{{ definitions, macros */ +# ifndef NDEBUG +# define V(lvl,msg) ((void) (((lvl) <= verbose) && verbout ? (*verbout) msg, fflush (stdout) : 0)) +# define MCHK(xxx) ((void) ((xxx) && ((xxx) -> magic != MAGIC) ? fprintf (stderr, "Invalid magic: expect %ld got %ld in %s:%d\n", MAGIC, (xxx) -> magic, __FILE__, __LINE__) : 0)) +# define MKMAGIC(ch1,ch2,ch3,ch4) \ + ((long) ((((unsigned char) (ch1)) << 24) | \ + (((unsigned char) (ch2)) << 16) | \ + (((unsigned char) (ch3)) << 8) | \ + ((unsigned char) (ch4)))) +# else /* NDEBUG */ +# define V(lvl,msg) +# define MCHK(xxx) +# endif /* NDEBUG */ + +# define NO_ERR 0 +# define ERR_FAIL (-1) +# define ERR_FATAL (-2) +# define ERR_ABORT (-3) + +# define ECONT(xxx) (((xxx) == NO_ERR) || ((xxx) == ERR_FAIL)) +# define ESTOP(xxx) (((xxx) == ERR_FATAL) || ((xxx) == ERR_ABORT)) + +# define LGS_SENT '+' +# define LGF_SENT '-' +# define LGS_INF '*' +# define LGF_INF '/' + +# define LG_INF 'i' +# define LG_COST 'c' +# define LG_SSESSION 's' +# define LG_ESESSION 'e' +# define LG_PROTO 'p' +/*}}}*/ +/*{{{ typedefs */ +typedef enum { + False = 0, + True = ! False +} Bool; + +typedef enum { + Unknown, + Ascii, + Script, + Tap, + Ucp +} Protocol; + +typedef unsigned char char_t; + +typedef struct { + char_t *str; /* the string itself */ + int len; /* the current length */ + int size; /* the allocated size */ +} string_t; + +typedef struct { + int day, mon, year; + int hour, min, sec; +} date_t; +/*}}}*/ +/*{{{ prototypes */ +/*{{{ utility */ +extern char *skip (char *str); +extern char *skipch (char *str, char ch); +extern char *getline (FILE *fp, Bool cont); +extern int verbose_out (char *, ...); +/*}}}*/ +/*{{{ string handling */ +extern string_t *snewc (char *str); +extern string_t *snew (char_t *str, int len); +extern Bool sexpand (string_t *s, int nsize); +extern Bool scopy (string_t *dst, string_t *src); +extern Bool scat (string_t *dst, string_t *src); +extern Bool scopyc (string_t *dst, char *src); +extern Bool scatc (string_t *dst, char *src); +extern string_t *scut (string_t *str, int start, int len); +extern void sdel (string_t *str, int start, int len); +extern Bool sput (string_t *str, string_t *ins, int pos, int len); +extern Bool sputc (string_t *str, char *ins, int pos, int len); +extern char *sextract (string_t *s); +extern char *schar (string_t *s); +extern void *sfree (string_t *s); +extern void srelease (string_t *s); +extern Bool siscntrl (string_t *s, int pos); +extern Bool sisspace (string_t *s, int pos); +extern Bool sisdigit (string_t *s, int pos); +extern int stoi (string_t *s); +/*}}}*/ +/*{{{ date handling */ +extern date_t *dat_free (date_t *d); +extern date_t *dat_parse (char *str); +extern int dat_diff (date_t *d1, date_t *d2); +extern void dat_clear (date_t *d); +extern void dat_localtime (date_t *d); +/*}}}*/ +/*{{{ tty handling */ +extern void *tty_open (char *dev, char *lckprefix, char *lckmethod); +extern void *tty_close (void *sp); +extern Bool tty_reopen (void *s, int msec); +extern void tty_hangup (void *sp, int msec); +extern int tty_fd (void *sp); +extern int tty_setup (void *sp, Bool raw, Bool modem, int speed, int bpb, int sb, char parity); +extern void tty_set_line_callback (void *sp, void (*func) (void *, string_t *, char_t, void *), char *sep, void *data); +extern void tty_suspend_callback (void *sp, Bool susp); +extern int tty_send (void *sp, char *str, int len); +extern int tty_send_string (void *sp, char *str); +extern int tty_expect (void *sp, int tout, ...); +extern int tty_expect_list (void *sp, int tout, char **strs, int *lens); +extern int tty_expect_string (void *sp, int tout, char *str); +extern int tty_send_expect (void *sp, int deftout, char *str, char **opts); +extern void tty_mdrain (void *sp, int msecs); +extern void tty_drain (void *sp, int secs); +/*}}}*/ +/*{{{ configuration */ +extern void *cfg_new (char *sep); +extern void *cfg_read (char *fname, void *bp, char *sep); +extern void *cfg_end (void *bp); +extern void cfg_modify (void *bp, char *bname, char *var, char *val); +extern char *cfg_get (void *bp, char *bname, char *var, char *dflt); +extern int cfg_iget (void *bp, char *bname, char *var, int dflt); +extern Bool cfg_bget (void *bp, char *bname, char *var, Bool dflt); +extern char *cfg_block_get (void *bp, char *bname, char *var, char *dflt); +extern int cfg_block_iget (void *bp, char *bname, char *var, int dflt); +extern Bool cfg_block_bget (void *bp, char *bname, char *var, Bool dflt); +/*}}}*/ +/*{{{ converting */ +extern void *cv_new (void); +extern void *cv_free (void *cv); +extern void *cv_reverse (void *src); +extern void cv_define (void *cv, char_t src, char_t dst); +extern void cv_sdefine (void *cv, char *src, char *dst); +extern void cv_undefine (void *cv, char_t ch); +extern void cv_sundefine (void *cv, char *ch); +extern void cv_invalid (void *cv, char_t ch); +extern void cv_sinvalid (void *cv, char *ch); +extern int cv_read_table (void *cv, char *fname); +extern int cv_write_table (void *cv, char *fname); +extern void cv_merge (void *cv, void *in, Bool second); +extern int cv_conv (void *cv, char_t ch); +/*}}}*/ +/*{{{ ASCII protocol */ +extern int asc_login (void *ap, string_t *callid); +extern int asc_logout (void *ap); +extern int asc_transmit (void *ap, char *pid, char *msg); +extern int asc_next (void *ap); +extern int asc_sync (void *ap); +extern void asc_config (void *ap, void (*logger) (char, char *, ...), + int deftout, char *alogin, char *alogout, char *apid, char *amsg, char *anext, char *async, + date_t *delay, date_t *expire, Bool rds); +extern void asc_set_convtable (void *ap, void *ctab); +extern void asc_add_convtable (void *ap, void *ctab); +extern void *asc_new (void *sp); +extern void *asc_free (void *ap); +extern int asc_preinit (void); +extern void asc_postdeinit (void); +/*}}}*/ +/*{{{ scripting protocol */ +extern int scr_execute (void *sp, char *label, char *parm); +extern int scr_load_string (void *sp, char *scr); +extern int scr_load_file (void *sp, char *fname); +extern void scr_config (void *sp, void (*logger) (char, char *, ...), date_t *delay, date_t *expire, Bool rds); +extern void scr_set_convtable (void *sp, void *ctab); +extern void scr_add_convtable (void *sp, void *ctab); +extern void *scr_new (void *sp, char *typ, char *libdir); +extern void *scr_free (void *sp); +extern int scr_preinit (char *libdir); +extern void scr_postdeinit (void); +/*}}}*/ +/*{{{ Telocator Alphanumeric Protocol */ +extern int tap_login (void *tp, char *stype, char ttype, char *passwd, string_t *callid); +extern int tap_logout (void *tp); +extern int tap_transmit (void *tp, string_t **field, Bool last); +extern void tap_config (void *tp, void (*logger) (char, char *, ...), Bool pre16); +extern void tap_timeouts (void *tp, int t1, int t2, int t3, int t4, int t5); +extern void tap_retries (void *tp, int n1, int n2, int n3, int licnt, int locnt); +extern void tap_set_convtable (void *tp, void *ctab); +extern void tap_add_convtable (void *tp, void *ctab); +extern void *tap_new (void *sp); +extern void *tap_free (void *tp); +extern int tap_preinit (void); +extern void tap_postdeinit (void); +/*}}}*/ +/*{{{ Universal Computer Protocol */ +extern int ucp_login (void *up, string_t *callid); +extern int ucp_logout (void *up); +extern int ucp_transmit (void *up, string_t *pagerid, string_t *msg, Bool last); +extern void ucp_config (void *up, void (*logger) (char, char *, ...), + Bool xtend, int stout, int retry, int rtout, + date_t *delay, date_t *expire, Bool rds); +extern void ucp_set_convtable (void *up, void *ctab); +extern void ucp_add_convtable (void *up, void *ctab); +extern void *ucp_new (void *sp); +extern void *ucp_free (void *up); +extern int ucp_preinit (void); +extern void ucp_postdeinit (void); +/*}}}*/ +/*}}}*/ +/*{{{ global variables */ +extern int verbose; +extern int (*verbout) (char *, ...); +/*}}}*/ +# endif /* __PAGER_H */ diff --git a/scr.c b/scr.c new file mode 100644 index 0000000..5b65ab6 --- /dev/null +++ b/scr.c @@ -0,0 +1,189 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +# include +# include +# include "pager.h" +# include "script.h" + +/*{{{ available scripts */ +static funcs *flist[] = { +# ifdef SCRIPT_SLANG + & fslang, +# endif /* SCRIPT_SLANG */ +# ifdef SCRIPT_LUA + & flua, +# endif /* SCRIPT_LUA */ + NULL +}; +/*}}}*/ +/*{{{ convert */ +char * +scr_convert (script *s, char *str) +{ + char *ret; + int n, m; + int c; + + if (ret = malloc (strlen (str) + 1)) { + for (n = 0, m = 0; str[n]; ++n) + if ((c = cv_conv (s -> ctab, (char_t) str[n])) > 0) + ret[m++] = (char) c; + ret[m] = '\0'; + } + return ret; +} +/*}}}*/ +/*{{{ execute */ +int +scr_execute (void *sp, char *label, char *parm) +{ + script *s = (script *) sp; + + MCHK (s); + if ((! s) || (! s -> f) || (! s -> f -> fexec)) + return ERR_FATAL; + return (*s -> f -> fexec) (s, label, parm); +} +/*}}}*/ +/*{{{ script loading */ +int +scr_load_string (void *sp, char *scr) +{ + script *s = (script *) sp; + + MCHK (s); + if ((! s) || (! s -> f) || (! s -> f -> fsload)) + return ERR_FATAL; + return (*s -> f -> fsload) (s, scr); +} + +int +scr_load_file (void *sp, char *fname) +{ + script *s = (script *) sp; + + MCHK (s); + if ((! s) || (! s -> f) || (! s -> f -> ffload)) + return ERR_FATAL; + return (*s -> f -> ffload) (s, fname); +} +/*}}}*/ +/*{{{ configuration */ +void +scr_config (void *sp, void (*logger) (char, char *, ...), date_t *delay, date_t *expire, Bool rds) +{ + script *s = (script *) sp; + + MCHK (s); + if (s) { + s -> logger = logger; + if (delay) + s -> delay = *delay; + else + dat_clear (& s -> delay); + if (expire) + s -> expire = *expire; + else + dat_clear (& s -> expire); + s -> rds = False; + } +} + +void +scr_set_convtable (void *sp, void *ctab) +{ + script *s = (script *) sp; + + MCHK (s); + if (s) { + if (s -> ctab) + cv_free (s -> ctab); + s -> ctab = ctab; + } +} + +void +scr_add_convtable (void *sp, void *ctab) +{ + script *s = (script *) sp; + + MCHK (s); + if (s) { + if (! s -> ctab) + s -> ctab = cv_new (); + if (s -> ctab) + cv_merge (s -> ctab, ctab, True); + } +} +/*}}}*/ +/*{{{ new/free/etc */ +void * +scr_new (void *sp, char *typ, char *libdir) +{ + script *s; + int n; + + if (s = (script *) malloc (sizeof (script))) { +# ifndef NDEBUG + s -> magic = MAGIC; +# endif /* NDEBUG */ + s -> sp = sp; + s -> ctab = NULL; + s -> logger = NULL; + dat_clear (& s -> delay); + dat_clear (& s -> expire); + s -> rds = False; + s -> priv = NULL; + for (n = 0; flist[n]; ++n) + if (flist[n] -> typ && (! strcmp (flist[n] -> typ, typ))) + break; + if (flist[n]) { + s -> f = flist[n]; + if (s -> f -> finit) + if ((*s -> f -> finit) (s, libdir) < 0) + s = scr_free (s); + } else + s = scr_free (s); + } + return s; +} + +void * +scr_free (void *sp) +{ + script *s = (script *) sp; + + MCHK (s); + if (s) { + if (s -> f && s -> f -> fdeinit) + (*s -> f -> fdeinit) (s); + if (s -> ctab) + cv_free (s -> ctab); + free (s); + } + return NULL; +} + +int +scr_preinit (char *libdir) +{ + int n; + + for (n = 0; flist[n]; ++n) + if (flist[n] -> fpreinit) + if ((*flist[n] -> fpreinit) (libdir) < 0) + return -1; + return 0; +} + +void +scr_postdeinit (void) +{ + int n; + + for (n = 0; flist[n]; ++n) + if (flist[n] -> fpostdeinit) + (*flist[n] -> fpostdeinit) (); + +} +/*}}}*/ diff --git a/script.h b/script.h new file mode 100644 index 0000000..a8c06a0 --- /dev/null +++ b/script.h @@ -0,0 +1,41 @@ +/* -*- mode: c; mode: fold -*- */ +# ifndef __SCRIPT_H +# define __SCRIPT_H 1 +/*{{{ typedefs */ +typedef struct script script; + +typedef struct { + char *typ; + int (*finit) (script *, char *); + void (*fdeinit) (script *); + int (*fexec) (script *, char *, char *); + int (*fsload) (script *, char *); + int (*ffload) (script *, char *); + int (*fpreinit) (char *); + void (*fpostdeinit) (void); +} funcs; + +struct script { +# ifndef NDEBUG +# define MAGIC MKMAGIC ('s', 'c', 'r', '\0') + long magic; +# endif /* NDEBUG */ + void *sp; + void *ctab; + void (*logger) (char, char *, ...); + date_t delay; + date_t expire; + Bool rds; + void *priv; + funcs *f; +}; +/*}}}*/ + +extern char *scr_convert (script *s, char *str); +# ifdef SCRIPT_SLANG +extern funcs fslang; +# endif /* SCRIPT_SLANG */ +# ifdef SCRIPT_LUA +extern funcs flua; +# endif /* SCRIPT_LUA */ +# endif /* __SCRIPT_H */ diff --git a/slang.c b/slang.c new file mode 100644 index 0000000..e1f20b1 --- /dev/null +++ b/slang.c @@ -0,0 +1,692 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +# ifdef SCRIPT_SLANG +# ifndef FLOAT_TYPE +# define FLOAT_TYPE +# endif /* FLOAT_TYPE */ +# include +# include +# include +# include +# include +# include "pager.h" +# include "script.h" + +# define STARTUP "Startup.sl" + +/*{{{ string class */ +static char *ifmt = NULL; +static char *ffmt = NULL; + +static inline char * +make_scratch (unsigned char typ, VOID_STAR p) +{ + char buf[128]; + + buf[0] = '\0'; + switch (typ) { + case INT_TYPE: + sprintf (buf, (ifmt ? ifmt : "%d"), *(int *) p); + break; + case FLOAT_TYPE: + sprintf (buf, (ffmt ? ffmt : "%f"), (double) (*(float64 *) p)); + break; + } + return strdup (buf); +} + +static int +binop_string (int op, unsigned char atyp, unsigned char btyp, + VOID_STAR ap, VOID_STAR bp) +{ + char *scratch; + char *a, *b; + int la, lb; + int len; + int n; + char *ptr; + char *res; + + scratch = NULL; + if ((atyp == STRING_TYPE) && (btyp == STRING_TYPE)) { + a = (char *) ap; + b = (char *) bp; + } else if (atyp == STRING_TYPE) { + a = (char *) ap; + scratch = make_scratch (btyp, bp); + b = scratch; + } else { + b = (char *) ap; + scratch = make_scratch (atyp, ap); + a = scratch; + } + if (! (a && b)) + return 0; + res = NULL; + switch (op) { + case SLANG_PLUS: + la = strlen (a); + lb = strlen (b); + if (res = SLMALLOC (la + lb + 1)) { + strcpy (res, a); + strcat (res, b); + } + break; + case SLANG_MINUS: + la = strlen (a); + lb = strlen (b); + if (ptr = strstr (a, b)) { + if (res = SLMALLOC (la - lb + 1)) { + len = (int) ((unsigned long) ptr - (unsigned long) a); + strncpy (res, a, len); + res[len] = '\0'; + strcat (res, a + len + lb); + } + } else if (res = SLMALLOC (la + 1)) + strcpy (res, a); + break; + case SLANG_TIMES: + la = strlen (a); + if ((n = atoi (b)) > 0) { + if (res = SLMALLOC (la * n + 1)) { + ptr = res; + while (n-- > 0) { + strcpy (ptr, a); + while (*ptr) + ++ptr; + } + } + } else if (res = SLMALLOC (2)) + res[0] = '\0'; + break; + case SLANG_DIVIDE: + for (ptr = a; *ptr; ++ptr) + if (! strchr (b, *ptr)) + break; + if (res = SLMALLOC (strlen (ptr) + 1)) { + strcpy (res, ptr); + if (res[0]) { + for (ptr = res; *ptr; ++ptr) + ; + --ptr; + while (ptr != res) + if (strchr (b, *ptr)) + *ptr-- = '\0'; + else + break; + } + } + break; + case SLANG_EQ: + SLang_push_integer (strcmp (a, b) == 0 ? 1 : 0); + break; + case SLANG_NE: + SLang_push_integer (strcmp (a, b) ? 1 : 0); + break; + case SLANG_GT: + SLang_push_integer (strcmp (a, b) > 0 ? 1 : 0); + break; + case SLANG_GE: + SLang_push_integer (strcmp (a, b) >= 0 ? 1 : 0); + break; + case SLANG_LT: + SLang_push_integer (strcmp (a, b) < 0 ? 1 : 0); + break; + case SLANG_LE: + SLang_push_integer (strcmp (a, b) <= 0 ? 1 : 0); + break; + default: + return 0; + } + if (scratch) + free (scratch); + if (res) + SLang_push_malloced_string (res); + return 1; +} + +static int +unop_string (int op, unsigned char typ, VOID_STAR p) +{ + char *s; + char *r; + int len; + + s = (char *) p; + switch (op) { + case SLANG_ABS: + SLang_push_integer (strlen (s)); + break; + case SLANG_SIGN: + SLang_push_integer (*s ? 1 : 0); + break; + case SLANG_SQR: + break; + case SLANG_MUL2: + len = strlen (s); + if (r = SLMALLOC (len * 2 + 1)) { + strcpy (r, s); + strcat (r, s); + SLang_push_malloced_string (r); + } + break; + case SLANG_CHS: + SLang_push_string (s); + break; + default: + return 0; + } + return 1; +} + +static void +get_format (char **var) +{ + char *fmt; + int fre; + + if (*var) { + free (*var); + *var = NULL; + } + if (SLang_pop_string (& fmt, & fre)) + return; + *var = strdup (fmt); + if (fre) + SLFREE (fmt); +} + +static void +str_iformat (void) +{ + get_format (& ifmt); +} + +static void +str_fformat (void) +{ + get_format (& ffmt); +} + +static SLang_Name_Type avail_string[] = { + /* void str_iformat (string format); */ + MAKE_INTRINSIC (".str_iformat", str_iformat, VOID_TYPE, 0), + /* void str_fformat (string format); */ + MAKE_INTRINSIC (".str_fformat", str_fformat, VOID_TYPE, 0), +/* + MAKE_INTRINSIC (".", , _TYPE, 0), + MAKE_INTRINSIC (".function_name", c_function, TYPE, 0), + MAKE_VARIABLE (".", & , TYPE, ), + MAKE_VARIABLE (".var", &c_variable, TYPE, flag), +*/ + SLANG_END_TABLE +}; + +static int +string_class (void) +{ + if ((! SLang_register_class (STRING_TYPE, NULL, NULL)) || + (! SLang_add_binary_op (STRING_TYPE, STRING_TYPE, + (VOID_STAR) binop_string)) || + (! SLang_add_binary_op (STRING_TYPE, INT_TYPE, + (VOID_STAR) binop_string)) || + (! SLang_add_binary_op (STRING_TYPE, FLOAT_TYPE, + (VOID_STAR) binop_string)) || + (! SLang_add_unary_op (STRING_TYPE, + (VOID_STAR) unop_string)) || + (! SLang_add_table (avail_string, "string"))) + return 0; + return 1; +} +/*}}}*/ +/*{{{ statics */ +static Bool isinit = False; +static script *sls = NULL; +static char *slline = NULL; +static int slsiz = 0; +static SLang_Name_Type *slcb = NULL; +/*}}}*/ +/*{{{ callable functions and variables */ +static void +sllogger (void) +{ + char *str; + int fre; + int typ; + + if (SLang_pop_string (& str, & fre) || + SLang_pop_integer (& typ)) + return; + if (sls && sls -> logger) { + if (! typ) + typ = LG_INF; + (*sls -> logger) ((char) typ, "%s\n", str); + } + if (fre) + SLFREE (str); +} + +static void +slcallback (void *sp, string_t *s, char_t sep, void *data) +{ + int len; + char *str; + + if (str = sextract (s)) { + len = strlen (str); + if (len + 2 >= slsiz) { + slsiz = len + 64; + if (! (slline = Realloc (slline, slsiz + 4))) + slsiz = 0; + } + if (slline) { + sprintf (slline, "%s%c", str, (char) sep); + if (slcb) { + SLang_push_string (slline); + SLexecute_function (slcb); + } + } + free (str); + } +} + +static void +slsetcb (void) +{ + char *func, *sep; + int fr1, fr2; + + if (SLang_pop_string (& sep, & fr2) || + SLang_pop_string (& func, & fr1)) + return; + if (func && *func) + slcb = SLang_get_function (func); + else + slcb = NULL; + if (sls && sls -> sp) + tty_set_line_callback (sls -> sp, slcallback, sep, NULL); + if (fr1) + SLFREE (func); + if (fr2) + SLFREE (sep); +} + +static void +slclrcb (void) +{ + slcb = NULL; + if (sls && sls -> sp) + tty_set_line_callback (sls -> sp, NULL, NULL, NULL); +} + +static void +slget_line (void) +{ + SLang_push_string (slline ? slline : ""); +} + +static void +slhangup (void) +{ + int msec; + + if (SLang_pop_integer (& msec)) + return; + if (sls && sls -> sp) + tty_hangup (sls -> sp, msec); +} + +static int +slsend (void) +{ + int ret; + char *str; + int fre; + + if (SLang_pop_string (& str, & fre)) + return 0; + ret = 0; + if (sls && sls -> sp) + if (tty_send_string (sls -> sp, str) != -1) + ret = 1; + if (fre) + SLFREE (str); + return ret; +} + +static int +slcsend (void) +{ + int ret; + char *str; + int fre; + char *rstr; + + if (SLang_pop_string (& str, & fre)) + return 0; + ret = 0; + if (sls && sls -> sp) + if (rstr = scr_convert (sls, str)) { + if (tty_send_string (sls -> sp, rstr) != -1) + ret = 1; + free (rstr); + } + if (fre) + SLFREE (str); + return ret; +} + +static int +slexpect (void) +{ + int ret; + int cnt; + char **str; + int *fre; + int *len; + int tout; + int n; + + if (SLang_pop_integer (& cnt)) + return -1; + ret = -1; + if ((str = (char **) malloc ((cnt + 2) * sizeof (char *))) && + (fre = (int *) malloc ((cnt + 2) * sizeof (int))) && + (len = (int *) malloc ((cnt + 2) * sizeof (int)))) { + for (n = cnt - 1; n >= 0; --n) + if (SLang_pop_string (& str[n], & fre[n])) + return -1; + else + len[n] = strlen (str[n]); + str[cnt] = NULL; + len[cnt] = 0; + if (SLang_pop_integer (& tout)) + return -1; + if (sls && sls -> sp) + ret = tty_expect_list (sls -> sp, tout, str, len); + for (n = 0; n < cnt; ++n) + if (fre[n]) + SLFREE (str[n]); + free (str); + free (fre); + free (len); + } + return ret; +} + +static int +slsend_expect (void) +{ + int tout; + char *str; + int fre; + int ret; + + if (SLang_pop_string (& str, & fre) || + SLang_pop_integer (& tout)) + return 0; + ret = 0; + if (sls && sls -> sp) + if (tty_send_expect (sls -> sp, tout, str, NULL) != -1) + ret = 1; + if (fre) + SLFREE (str); + return ret; +} + +static void +sldrain (void) +{ + int secs; + + if (SLang_pop_integer (& secs)) + return; + if (sls && sls -> sp) + tty_drain (sls -> sp, secs); +} + +static void +slcvdef (void) +{ + int src, dst; + + if (SLang_pop_integer (& dst) || + SLang_pop_integer (& src)) + return; + if (sls) { + if (! sls -> ctab) + sls -> ctab = cv_new (); + if (sls -> ctab) + cv_define (sls -> ctab, (char_t) src, (char_t) dst); + } +} + +static void +slcvundef (void) +{ + int ch; + + if (SLang_pop_integer (& ch)) + return; + if (sls && sls -> ctab) + cv_undefine (sls -> ctab, (char_t) ch); +} + +static void +slcvinval (void) +{ + int ch; + + if (SLang_pop_integer (& ch)) + return; + if (sls) { + if (! sls -> ctab) + sls -> ctab = cv_new (); + if (sls -> ctab) + cv_invalid (sls -> ctab, (char_t) ch); + } +} +static void +slconv (void) +{ + char *str; + int fre; + char *rstr; + + if (SLang_pop_string (& str, & fre)) + return; + if (sls) + rstr = scr_convert (sls, str); + else + rstr = NULL; + SLang_push_string (rstr ? rstr : str); + if (rstr) + free (rstr); + if (fre) + SLFREE (str); +} + +static int no_err = NO_ERR, + err_fail = ERR_FAIL, + err_fatal = ERR_FATAL, + err_abort = ERR_ABORT; +static date_t sldelay, slexpire; +static int slrds = 0; +static int xFalse = (int) False, + xTrue = (int) True; +static char xNull_String[2] = ""; +/*}}}*/ +/*{{{ function/variable table */ +static SLang_Name_Type avail[] = { + /* void logger (string str); */ + MAKE_INTRINSIC (".logger", sllogger, VOID_TYPE, 0), + /* void setcb (string func, string sep); */ + MAKE_INTRINSIC (".setcb", slsetcb, VOID_TYPE, 0), + /* void clrcb (void); */ + MAKE_INTRINSIC (".clrcb", slclrcb, VOID_TYPE, 0), + /* string line (void); */ + MAKE_INTRINSIC (".line", slget_line, VOID_TYPE, 0), + /* void hangup (int msec); */ + MAKE_INTRINSIC (".hangup", slhangup, VOID_TYPE, 0), + /* int send (string str); */ + MAKE_INTRINSIC (".send", slsend, INT_TYPE, 0), + /* int csend (string str); */ + MAKE_INTRINSIC (".csend", slcsend, INT_TYPE, 0), + /* int expect (int tout, string e1, ..., string en, + int cnt); */ + MAKE_INTRINSIC (".expect", slexpect, INT_TYPE, 0), + /* int send_expect (int tout, string str); */ + MAKE_INTRINSIC (".send_expect", slsend_expect, INT_TYPE, 0), + /* void drain (int secs); */ + MAKE_INTRINSIC (".drain", sldrain, INT_TYPE, 0), + /* void cvdef (int src, int dst); */ + MAKE_INTRINSIC (".cvdef", slcvdef, VOID_TYPE, 0), + /* void cvundef (int ch); */ + MAKE_INTRINSIC (".cvundef", slcvundef, VOID_TYPE, 0), + /* void cvinval (int ch); */ + MAKE_INTRINSIC (".cvinval", slcvinval, VOID_TYPE, 0), + /* string conv (string str); */ + MAKE_INTRINSIC (".conv", slconv, VOID_TYPE, 0), + + MAKE_VARIABLE (".NO_ERR", & no_err, INT_TYPE, 1), + MAKE_VARIABLE (".ERR_FAIL", & err_fail, INT_TYPE, 1), + MAKE_VARIABLE (".ERR_FATAL", & err_fatal, INT_TYPE, 1), + MAKE_VARIABLE (".ERR_ABORT", & err_abort, INT_TYPE, 1), + MAKE_VARIABLE (".delay_day", & sldelay.day, INT_TYPE, 1), + MAKE_VARIABLE (".delay_mon", & sldelay.mon, INT_TYPE, 1), + MAKE_VARIABLE (".delay_year", & sldelay.year, INT_TYPE, 1), + MAKE_VARIABLE (".delay_hour", & sldelay.hour, INT_TYPE, 1), + MAKE_VARIABLE (".delay_min", & sldelay.min, INT_TYPE, 1), + MAKE_VARIABLE (".delay_sec", & sldelay.sec, INT_TYPE, 1), + MAKE_VARIABLE (".expire_day", & slexpire.day, INT_TYPE, 1), + MAKE_VARIABLE (".expire_mon", & slexpire.mon, INT_TYPE, 1), + MAKE_VARIABLE (".expire_year", & slexpire.year, INT_TYPE, 1), + MAKE_VARIABLE (".expire_hour", & slexpire.hour, INT_TYPE, 1), + MAKE_VARIABLE (".expire_min", & slexpire.min, INT_TYPE, 1), + MAKE_VARIABLE (".expire_sec", & slexpire.sec, INT_TYPE, 1), + MAKE_VARIABLE (".rds", & slrds, INT_TYPE, 1), + MAKE_VARIABLE (".False", & xFalse, INT_TYPE, 1), + MAKE_VARIABLE (".True", & xTrue, INT_TYPE, 1), + MAKE_VARIABLE (".Null_String", & xNull_String, STRING_TYPE, 1), +/* + MAKE_INTRINSIC (".", , _TYPE, 0), + MAKE_INTRINSIC (".function_name", c_function, TYPE, 0), + MAKE_VARIABLE (".", & , TYPE, ), + MAKE_VARIABLE (".var", &c_variable, TYPE, flag), +*/ + SLANG_END_TABLE +}; +/*}}}*/ +/*{{{ init/deinit */ +static int +slang_init (script *s, char *libdir) +{ + char *fname; + + if (! isinit) { + if ((! init_SLang ()) || (! init_SLmath ()) || + (! init_SLunix ()) || (! init_SLfiles ()) || + (! string_class ()) || (! SLang_add_table (avail, "yaps"))) + return -1; + if (libdir && (fname = malloc (strlen (libdir) + sizeof (STARTUP) + 4))) { + sprintf (fname, "%s/%s", libdir, STARTUP); + if (access (fname, R_OK) != -1) + if ((! SLang_load_file (fname)) || SLang_Error) + SLang_restart (1); + free (fname); + } + isinit = True; + } + if (slline) + slline[0] = '\0'; + return NO_ERR; +} + +static void +slang_deinit (script *s) +{ + if (slline) { + free (slline); + slline = NULL; + } + slsiz = 0; +} +/*}}}*/ +/*{{{ execute */ +static int +slang_execute (script *s, char *label, char *parm) +{ + SLang_Name_Type *func; + int ret; + + ret = NO_ERR; + if (func = SLang_get_function (label)) { + SLang_push_string (parm ? parm : ""); + sldelay = s -> delay; + slexpire = s -> expire; + slrds = s -> rds; + sls = s; + SLexecute_function (func); + if (sls -> sp) + tty_set_line_callback (sls -> sp, NULL, NULL, NULL); + sls = NULL; + if (SLang_Error || SLang_pop_integer (& ret)) { + ret = ERR_FATAL; + SLang_restart (1); + } + } + return ret; +} +/*}}}*/ +/*{{{ loading */ +static int +slang_load_string (script *s, char *scr) +{ + int err; + + err = ERR_FATAL; + if (SLang_load_string (scr) && (! SLang_Error)) + err = NO_ERR; + else + SLang_restart (1); + return err; +} + +static int +slang_load_file (script *s, char *fname) +{ + int err; + + err = ERR_FATAL; + if (SLang_load_file (fname) && (! SLang_Error)) + err = NO_ERR; + else + SLang_restart (1); + return err; +} +/*}}}*/ +/*{{{ preinit/postdeinit/scriptentry */ +static int +slang_preinit (char *libdir) +{ + return slang_init (NULL, libdir); +} + +static void +slang_postdeinit (void) +{ + slang_deinit (NULL); +} + +funcs fslang = { + "SLang", + slang_init, + slang_deinit, + slang_execute, + slang_load_string, + slang_load_file, + slang_preinit, + slang_postdeinit +}; +/*}}}*/ +# endif /* SCRIPT_SLANG */ diff --git a/tap.c b/tap.c new file mode 100644 index 0000000..413bf8b --- /dev/null +++ b/tap.c @@ -0,0 +1,725 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +# include +# include +# include +# include +# include "pager.h" + +/*{{{ definitions & typedefs */ +# define DEF_SERVICE_TYPE "PG" +# define DEF_TERMINAL_TYPE '1' + +# define CHKTYPE(sss,ccc) ((((sss)[0] & 0xff) << 16) | (((sss)[1] & 0xff) << 8) | ((ccc) & 0xff)) +# define CHKSTYPE(cc1,cc2,cc3) ((((cc1) & 0xff) << 16) | (((cc2) & 0xff) << 8) | ((cc3) & 0xff)) + +typedef enum { + None, + PG1 +} Type; + +typedef struct _rmsg { + string_t *str; + int rnr; + struct _rmsg *next; +} rmsg; + +typedef struct { +# ifndef NDEBUG +# define MAGIC MKMAGIC ('t', 'a', 'p', '\0') + long magic; +# endif /* NDEBUG */ + void *sp; /* the serial connection */ + void *ctab; /* the conversion table */ + void (*logger) (char, char *, ...); + string_t + *callid; /* caller id */ + Type typ; /* the connection type */ + Bool pre16; /* if p.version before 1.6 is used */ + /* timing and retry values */ + int t1, t2, t3, t4, t5; + int n1, n2, n3; + int licnt, locnt; + + rmsg *r, *prv; /* response message(s) on callback */ +} tap; +/*}}}*/ +/*{{{ convert */ +static int +field_convert (tap *t, string_t *ptr, string_t *cf) +{ + int n; + int ch; + + cf -> str = NULL; + cf -> size = ptr -> len + 32; + if (! (cf -> str = malloc (cf -> size + 2))) + return ERR_FATAL; + for (n = 0, cf -> len = 0; n < ptr -> len; ++n) { + if (cf -> len + 2 >= cf -> size) + if (! sexpand (cf, cf -> size + 32)) + return ERR_FATAL; + ch = cv_conv (t -> ctab, ptr -> str[n]); + if (ch != -1) + switch ((char_t) ch) { + case '\x0d': case '\x0a': case '\x1b': + case '\x02': case '\x03': case '\x1f': + case '\x17': case '\x04': case '\x1a': + if (! t -> pre16) { + cf -> str[cf -> len++] = '\x1a'; + cf -> str[cf -> len++] = (char_t) (ch + 0x40); + } + break; + default: + cf -> str[cf -> len++] = (char_t) ch; + break; + } + } + if (cf -> str) { + cf -> str[cf -> len] = '\0'; + return NO_ERR; + } + return ERR_FATAL; +} + +static char chkstr[] = "0123456789:;<=>?"; +static string_t ** +convert_tap (tap *t, string_t **field) +{ + string_t **ret; + int fcnt; + string_t *cf; + char_t *ptr; + int cnt, siz; + char_t tmp[260]; + Bool needchk; + unsigned long chk; + int fld, off, len; + int n, m; + + ret = NULL; + for (fcnt = 0; field[fcnt]; ++fcnt) + ; + if (cf = (string_t *) malloc ((fcnt + 1) * sizeof (string_t))) { + for (fld = 0; fld < fcnt; ++fld) + if (field_convert (t, field[fld], & cf[fld]) != NO_ERR) { + while (--fld >= 0) + free (cf[fld].str); + free (cf); + break; + } + if (fld < fcnt) + return NULL; + cnt = 0; + siz = 0; + n = 0; + needchk = False; + for (fld = 0, off = 0; fld < fcnt; ) { + ptr = cf[fld].str + off; + len = cf[fld].len - off; + do { + if (! n) + tmp[n++] = '\x02'; + if (len) { + tmp[n++] = *ptr++; + --len; + ++off; + } + if (! len) { + off = 0; + ++fld; + tmp[n++] = '\r'; + if (fld == fcnt) { + tmp[n++] = '\x03'; + needchk = True; + } else if (n >= 230) { + tmp[n++] = '\x17'; + needchk = True; + } + } else if (n >= 250) { + tmp[n++] = '\x1f'; + needchk = True; + } + if (needchk) { + needchk = False; + for (m = 0, chk = 0; m < n; ++m) + chk += tmp[m] & 0xff; + tmp[n++] = chkstr[(chk >> 8) & 0xf]; + tmp[n++] = chkstr[(chk >> 4) & 0xf]; + tmp[n++] = chkstr[chk & 0xf]; + tmp[n++] = '\r'; + tmp[n] = '\0'; + if (cnt >= siz) { + siz += 4; + if (! (ret = (string_t **) Realloc (ret, (siz + 1) * sizeof (string_t *)))) + return NULL; + } + if (! (ret[cnt] = snew (tmp, n))) { + while (--cnt >= 0) + sfree (ret[cnt]); + free (ret); + return NULL; + } + ++cnt; + ret[cnt] = NULL; + n = 0; + } + } while (len); + } + for (fld = 0; fld < fcnt; ++fld) + free (cf[fld].str); + free (cf); + } + return ret; +} +/*}}}*/ +/*{{{ callback interface for receiving response numbers */ +static void +getresponse (void *sp, string_t *s, char_t ch, void *data) +{ + tap *t = (tap *) data; + rmsg *r; + + if (t && (! t -> pre16) && s && (s -> len > 0)) + if (sisdigit (s, 0)) { + if (r = (rmsg *) malloc (sizeof (rmsg))) + if (r -> str = snew (s -> str, s -> len + 2)) { + r -> rnr = stoi (r -> str); + r -> next = NULL; + if (t -> prv) + t -> prv -> next = r; + else + t -> r = r; + t -> prv = r; + } else + free (r); + } else if ((! siscntrl (s, 0)) && (r = t -> prv) && scatc (r -> str, "\n")) { + if (sisspace (s, 0)) + sdel (s, 0, 1); + scat (r -> str, s); + } +} + +static void +free_resp (tap *t) +{ + rmsg *tmp; + + if (t) { + while (t -> r) { + tmp = t -> r; + t -> r = t -> r -> next; + sfree (tmp -> str); + free (tmp); + } + t -> prv = NULL; + } +} + +static void +setcb (tap *t) +{ + if (! t -> pre16) { + free_resp (t); + tty_suspend_callback (t -> sp, False); + } +} + +static void +clrcb (tap *t) +{ + if (! t -> pre16) { + tty_suspend_callback (t -> sp, True); + free_resp (t); + } +} +/*}}}*/ +/*{{{ response handling */ +static int +show_response (tap *t) +{ + rmsg *r; + int nr, typ; + char *line, *ptr; + + for (r = t -> r, nr = -1; r; r = r -> next) { + nr = r -> rnr; + if (line = sextract (r -> str)) { + for (ptr = line; isdigit (*ptr); ++ptr) + ; + while (isspace (*ptr)) + ++ptr; + } else + ptr = "(none)"; + switch (nr) { + case 110: + V (1, ("TAP Version: %s\n", ptr)); + break; + case 111: + V (1, ("processing input\n")); + break; + case 112: + V (1, ("maximum pages entered\n")); + break; + case 113: + V (1, ("maximum time reached\n")); + break; + case 114: + V (1, ("Welcome: %s\n", ptr)); + break; + case 115: + V (1, ("Byebye: %s\n", ptr)); + break; + case 211: + V (1, ("Page(s) sent successful\n")); + break; + case 212: + V (1, ("Long message truncated and sent\n")); + break; + case 213: + V (1, ("Message accepted - held for deferred delivery\n")); + break; + case 214: + V (1, ("%d character maximum, message has been truncated and sent\n", atoi (ptr))); + break; + case 501: + V (1, ("Timeout during input\n")); + break; + case 502: + V (1, ("Unexpected character received before transaction\n")); + break; + case 503: + V (1, ("Excessive attempts to send/re-send a transaction with checksum errors\n")); + break; + case 504: + V (1, ("Message have to be empty for tone-only pager\n")); + break; + case 505: + V (1, ("Message must not contain characters for a numeric pager\n")); + break; + case 506: + V (1, ("Excessive invalid pages received\n")); + break; + case 507: + V (1, ("Invalid logon attempt: Incorrectly formed logon sequence\n")); + break; + case 508: + V (1, ("Invalid Logon attempt: Service type and category given is not supported\n")); + break; + case 509: + V (1, ("Invalid Logon attempt: Invalid password supplied\n")); + break; + case 510: + V (1, ("Illegal Pager ID\n")); + break; + case 511: + V (1, ("Invalid Pager ID\n")); + break; + case 512: + V (1, ("Temporarily cannot deliver to Pager ID\n")); + break; + case 513: + V (1, ("Long message rejected for exceeding maximum character length\n")); + break; + case 514: + V (1, ("Checksum error\n")); + break; + case 515: + V (1, ("Message format error\n")); + break; + case 516: + V (1, ("Message quota temporarily exceeded\n")); + break; + case 517: + V (1, ("%d character maximum, message rejected\n", atoi (ptr))); + break; + default: + typ = nr / 100; + switch (typ) { + default: + V (1, ("Response")); + break; + case 1: + V (1, ("Informal")); + break; + case 2: + V (1, ("Success")); + break; + case 5: + V (1, ("Fail")); + break; + } + V (1, (" %03d: %s\n", r -> rnr, ptr)); + break; + } + if (line) + free (line); + } + return nr; +} +/*}}}*/ +/*{{{ login/logout */ +int +tap_login (void *tp, char *stype, char ttype, char *passwd, string_t *callid) +{ + tap *t = (tap *) tp; + int n, ep; + char *cbuf; + int clen; + int err; + + MCHK (t); + if ((! t) || (! t -> sp)) + return ERR_ABORT; + if (! stype) + stype = DEF_SERVICE_TYPE; + else if (strlen (stype) != 2) { + V (1, ("Invalid service type %s\n", stype)); + return ERR_ABORT; + } + if (! ttype) + ttype = DEF_TERMINAL_TYPE; + switch (CHKTYPE (stype, ttype)) { + case CHKSTYPE ('P', 'G', '1'): + t -> typ = PG1; + break; + } + if (t -> typ == None) { + V (1, ("Invalid service/terminal type combination\n")); + return ERR_ABORT; + } + t -> callid = sfree (t -> callid); + if (callid && (! (t -> callid = snew (callid -> str, callid -> len)))) + return ERR_ABORT; + for (n = 0; n < t -> n1; ++n) + if (tty_send (t -> sp, "\r", 1) != 1) + V (1, ("Unable to send initial CR\n")); + else if (tty_expect (t -> sp, t -> t1, "ID=", 3, NULL) == 1) { + V (1, ("Send initial CR\n")); + break; + } + if (n == t -> n1) { + V (1, ("Didn't got initial response\n")); + return ERR_ABORT; + } + if (cbuf = malloc ((passwd ? strlen (passwd) : 0) + strlen (stype) + 16)) { + if (passwd) + sprintf (cbuf, "\x1b%s%c%s\r", stype, ttype, passwd); + else + sprintf (cbuf, "\x1b%s%c\r", stype, ttype); + clen = strlen (cbuf); + } else { + V (1, ("Out of memory!\n")); + return ERR_ABORT; + } + err = NO_ERR; + for (n = 0; n < t -> licnt; ++n) + if (tty_send (t -> sp, cbuf, clen) != clen) + V (1, ("Unable to send Logon request\n")); + else { + setcb (t); + ep = tty_expect (t -> sp, t -> t3, "\x06\r", 2, "ID=", 3, "\x15\r", 2, "\x1b\x04\r", 3, NULL); + if ((! t -> pre16) && t -> r) + show_response (t); + if (ep == 1) { + V (1, ("Login request accepted\n")); + break; + } else if (ep == 2) + V (1, ("Oops, got ID= again\n")); + else if (ep == 3) + V (1, ("Login request not accepted\n")); + else if (ep == 4) { + V (1, ("Logout forced\n")); + n = t -> licnt; + err = ERR_ABORT; + break; + } + } + free (cbuf); + clrcb (t); + if (n == t -> licnt) { + V (1, ("Could not login\n")); + return (err == NO_ERR) ? ERR_FATAL : err; + } + if (tty_expect (t -> sp, t -> t3, "\x1b[p\r", 4, NULL) != 1) { + V (1, ("Didn't got Go Ahead message\n")); + return ERR_FATAL; + } else + V (1, ("Successful login\n")); + return NO_ERR; +} + +int +tap_logout (void *tp) +{ + tap *t = (tap *) tp; + int err; + int n, ep; + + MCHK (t); + if ((! t) || (! t -> sp)) + return ERR_FATAL; + err = NO_ERR; + for (n = 0; n < t -> locnt; ++n) + if (tty_send (t -> sp, "\x04\r", 2) != 2) + V (1, ("Unable to send logout request\n")); + else { + setcb (t); + ep = tty_expect (t -> sp, t -> t3, "\x1b\x04\r", 3, "\x1e\r", 2, NULL); + if ((! t -> pre16) && t -> r) + show_response (t); + if (ep == 1) { + V (1, ("Message sending completed\n")); + break; + } else if (ep == 2) { + V (1, ("Message sending failed (%d)\n", ep)); + err = ERR_FATAL; + n = t -> locnt; + break; + } + } + clrcb (t); + if (n == t -> locnt) { + if (err == NO_ERR) + err = ERR_FAIL; + V (1, ("Unable to logout\n")); + return err; + } else + V (1, ("Logout successful\n")); + return NO_ERR; +} +/*}}}*/ +/*{{{ transmit */ +int +tap_transmit (void *tp, string_t **field, Bool last) +{ + tap *t = (tap *) tp; + int err; + string_t **snd; + char *ptr; + int len; + int n, m, ep; + + MCHK (t); + if ((! t) || (! t -> sp)) + return ERR_FATAL; + switch (t -> typ) { + default: + case None: + V (1, ("Invalid service/terminal type\n")); + return ERR_FAIL; + case PG1: + for (n = 0; field[n]; ++n) + ; + if (n != 2) { + V (1, ("PG1 service/terminal type needs exactly two fields\n")); + return ERR_FAIL; + } + break; + } + err = ERR_FATAL; + if (snd = convert_tap (t, field)) { + err = NO_ERR; + for (m = 0; snd[m]; ++m) { + ptr = (char *) snd[m] -> str; + len = snd[m] -> len; + for (n = 0; n < t -> n2; ++n) + if (tty_send (t -> sp, ptr, len) != len) + V (1, ("Unable to transmit part\n")); + else { + setcb (t); + ep = tty_expect (t -> sp, t -> t3, "\x06\r", 2, "\x15\r", 2, "\x1e\r", 2, "\x1b\x04\r", 3, NULL); + if ((! t -> pre16) && t -> r) + show_response (t); + if (ep == 1) { + V (1, ("Transmited part\n")); + break; + } else if (ep == 2) + V (1, ("Can't transmit part\n")); + else if (ep == 3) { + V (1, ("Can't transmit this message\n")); + err = ERR_FAIL; + n = t -> n2; + break; + } else if (ep == 4) { + V (1, ("Logout forced\n")); + err = ERR_ABORT; + n = t -> n2; + break; + } + } + sfree (snd[m]); + clrcb (t); + if (n == t -> n2) { + V (1, ("Unable to send part %d\n", m)); + for (n = m + 1; snd[n]; ++n) + sfree (snd[n]); + break; + } + } + if (! snd[m]) + V (1, ("Sent message\n")); + else if (err == NO_ERR) + err = ERR_FAIL; + free (snd); + } else + V (1, ("Unable to convert message\n")); + return err; +} +/*}}}*/ +/*{{{ configuration */ +void +tap_config (void *tp, void (*logger) (char, char *, ...), Bool pre16) +{ + tap *t = (tap *) tp; + + MCHK (t); + if (t) { + t -> logger = logger; + if (t -> pre16 = pre16) + tty_suspend_callback (t -> sp, True); + } +} + +void +tap_timeouts (void *tp, int t1, int t2, int t3, int t4, int t5) +{ + tap *t = (tap *) tp; + + MCHK (t); + if (t) { + if (t1 != -1) + t -> t1 = t1; + if (t2 != -1) + t -> t2 = t2; + if (t3 != -1) + t -> t3 = t3; + if (t4 != -1) + t -> t4 = t4; + if (t5 != -1) + t -> t5 = t5; + } +} + +void +tap_retries (void *tp, int n1, int n2, int n3, int licnt, int locnt) +{ + tap *t = (tap *) tp; + + MCHK (t); + if (t) { + if (n1 != -1) + t -> n1 = n1; + if (n2 != -1) + t -> n2 = n2; + if (n3 != -1) + t -> n3 = n3; + if (licnt != -1) + t -> licnt = licnt; + if (locnt != -1) + t -> locnt = locnt; + } +} + +void +tap_set_convtable (void *tp, void *ctab) +{ + tap *t = (tap *) tp; + + MCHK (t); + if (t) { + if (t -> ctab) + cv_free (t -> ctab); + t -> ctab = ctab; + } +} + +static void +new_convtab (tap *t) +{ + int n; + + if (t -> ctab = cv_new ()) + for (n = 128; n < 256; ++n) + cv_invalid (t -> ctab, (char_t) n); +} + +void +tap_add_convtable (void *tp, void *ctab) +{ + tap *t = (tap *) tp; + + MCHK (t); + if (t) { + if (! t -> ctab) + new_convtab (t); + if (t -> ctab) + cv_merge (t -> ctab, ctab, True); + } +} +/*}}}*/ +/*{{{ new/free/etc */ +void * +tap_new (void *sp) +{ + tap *t; + + if (t = (tap *) malloc (sizeof (tap))) { +# ifndef NDEBUG + t -> magic = MAGIC; +# endif /* NDEBUG */ + t -> sp = sp; + new_convtab (t); + t -> logger = NULL; + t -> callid = NULL; + t -> typ = None; + t -> pre16 = False; + t -> t1 = 2; + t -> t2 = 1; + t -> t3 = 10; + t -> t4 = 4; + t -> t5 = 8; + t -> n1 = 3; + t -> n2 = 3; + t -> n3 = 3; + t -> licnt = 3; + t -> locnt = 3; + t -> r = NULL; + t -> prv = NULL; + tty_set_line_callback (t -> sp, getresponse, "\r", (void *) t); + tty_suspend_callback (t -> sp, True); + } + return (void *) t; +} + +void * +tap_free (void *tp) +{ + tap *t = (tap *) tp; + + MCHK (t); + if (t) { + if (t -> ctab) + cv_free (t -> ctab); + if (t -> sp) + tty_set_line_callback (t -> sp, NULL, NULL, NULL); + if (t -> callid) + sfree (t -> callid); + if (t -> r) + free_resp (t); + free (t); + } + return NULL; +} + +int +tap_preinit (void) +{ + return 0; +} + +void +tap_postdeinit (void) +{ +} +/*}}}*/ diff --git a/tty.c b/tty.c new file mode 100644 index 0000000..8c8d0ee --- /dev/null +++ b/tty.c @@ -0,0 +1,1132 @@ +/* -*- mode: c; mode: fold -*- */ +/*{{{ includes */ +# include "config.h" +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# if HAVE_SYS_SELECT_H +# include +# endif /* HAVE_SYS_SELECT_H */ +# if HAVE_SYS_SYSMACROS_H +# include +# elif HAVE_SYS_MKDEV_H +# include +# else /* ! HAVE_SYS_SYSMACROS_H && ! HAVE_SYS_MKDEV_H */ +# define major(xx) (((xx) >> 8) & 0xff) +# define minor(xx) ((xx) & 0xff) +# endif /* HAVE_SYS_SYSMACROS_H || HAVE_SYS_MKDEV_H */ +# include "pager.h" +/*}}}*/ +/*{{{ statics and typedefs */ +static struct { + int speed; + speed_t tok; +} stab[] = { + { 300, B300 }, + { 1200, B1200 }, + { 2400, B2400 }, + { 4800, B4800 }, + { 9600, B9600 }, + { 19200, B19200 }, + { 38400, B38400 }, +# ifdef B57600 + { 57600, B57600 }, +# endif /* B57600 */ +# ifdef B115200 + { 115200, B115200 }, +# endif /* B115200 */ +# ifdef B230400 + { 230400, B230400 }, +# endif /* B230400 */ +# ifdef B460800 + { 460800, B460800 }, +# endif /* B460800 */ + { -1, B9600 } +}; + +static char *lckmth[] = { + "ascii", + "binary", + "lower", + "upper", + "sysv4", + "timeout", + NULL +}; + +typedef struct { +# ifndef NDEBUG +# define MAGIC MKMAGIC ('t', 't', 'y', '\0') + long magic; +# endif /* NDEBUG */ + char *lck; + char *device; + struct termios tty, sav; + int fd; + string_t *line; + char *sep; + void (*callback) (void *, string_t *, char_t, void *); + void *data; + Bool suspend; +} serial; + +typedef struct _expect { + int idx; + char *str; + int pos; + int len; + struct _expect + *next; +} expect; +/*}}}*/ +/*{{{ support routines */ +static char * +mkprint (char *str, int len) +{ + static char *buf = NULL; + static int size = 0; + int n; + char ch; + char extra; + char *ptr; + + if (len >= size) { + size = len + 128; + if (! (buf = Realloc (buf, size + 2))) + return NULL; + } + extra = '\0'; + for (n = 0; len > 0; ++str, --len) { + if (n + 8 >= size) { + size += 128; + if (! (buf = Realloc (buf, size + 2))) + break; + } + ch = *str; + if (ch & 0x80) { + buf[n++] = '<'; + buf[n++] = 'M'; + buf[n++] = '-'; + ch &= 0x7f; + extra = '>'; + } + switch (ch) { + case '\x00': ptr = ""; break; + case '\x01': ptr = ""; break; + case '\x02': ptr = ""; break; + case '\x03': ptr = ""; break; + case '\x04': ptr = ""; break; + case '\x05': ptr = ""; break; + case '\x06': ptr = ""; break; + case '\x07': ptr = ""; break; + case '\x08': ptr = ""; break; + case '\x09': ptr = ""; break; + case '\x0a': ptr = ""; break; + case '\x0b': ptr = ""; break; + case '\x0c': ptr = ""; break; + case '\x0d': ptr = ""; break; + case '\x0e': ptr = ""; break; + case '\x0f': ptr = ""; break; + case '\x10': ptr = ""; break; + case '\x11': ptr = ""; break; + case '\x12': ptr = ""; break; + case '\x13': ptr = ""; break; + case '\x14': ptr = ""; break; + case '\x15': ptr = ""; break; + case '\x16': ptr = ""; break; + case '\x17': ptr = ""; break; + case '\x18': ptr = ""; break; + case '\x19': ptr = ""; break; + case '\x1a': ptr = ""; break; + case '\x1b': ptr = ""; break; + case '\x1c': ptr = ""; break; + case '\x1d': ptr = ""; break; + case '\x1e': ptr = ""; break; + case '\x1f': ptr = ""; break; + case '\x7f': ptr = ""; break; + default: + ptr = NULL; + buf[n++] = ch; + break; + } + if (ptr) + while (*ptr) + buf[n++] = *ptr++; + if (extra) { + buf[n++] = extra; + extra = '\0'; + } + } + if (buf) + buf[n] = '\0'; + return buf; +} + +static inline void +msleep (int msec) +{ + struct timeval tv; + + if (msec > 0) { + do { + tv.tv_sec = msec / 1000; + tv.tv_usec = (msec % 1000) * 1000; + errno = 0; + } while ((select (0, NULL, NULL, NULL, & tv) < 0) && (errno == EINTR)); + } +} + +static inline int +data_ready (int fd, int *msec) +{ + int ret = 0; + struct timeval tv; + fd_set fset; + + FD_ZERO (& fset); + FD_SET (fd, & fset); + tv.tv_sec = *msec / 1000; + tv.tv_usec = (*msec % 1000) * 1000; + if (((ret = select (fd + 1, & fset, NULL, NULL, & tv)) > 0) && FD_ISSET (fd, & fset)) { + *msec = tv.tv_sec * 1000 + (tv.tv_usec / 1000); + return 1; + } + return ret; +} + +static Bool +do_lock (serial *s, char *dev, char *prefix, char *method) +{ + Bool binary; + Bool lower, upper; + Bool sysv4; + int tout; + struct stat st; + char *ptr, *sav, *val; + int len; + char *bdev; + int fd; + char buf[32]; + int n, m; + pid_t pid; + + s -> lck = NULL; + if (prefix) { + binary = False; + lower = False; + upper = False; + sysv4 = False; + tout = 0; + if (method && (method = strdup (method))) { + for (ptr = method; *ptr; ) { + sav = ptr; + ptr = skipch (ptr, ','); + val = skipch (sav, '='); + len = strlen (sav); + for (n = 0; lckmth[n]; ++n) + if (! strncmp (lckmth[n], sav, len)) + break; + switch (n) { + case 0: /* ascii */ + binary = False; + break; + case 1: /* binary */ + binary = True; + break; + case 2: /* lower */ + lower = True; + upper = False; + break; + case 3: /* upper */ + lower = False; + upper = True; + break; + case 4: /* sysv4 */ + sysv4 = True; + break; + case 5: /* timeout */ + tout = atoi (val); + break; + } + } + free (method); + } + if (sysv4) { + bdev = NULL; + if ((stat (dev, & st) != -1) && S_ISCHR (st.st_mode) && (bdev = malloc (96))) + sprintf (bdev, "%03d.%03d.%03d", major (st.st_dev), major (st.st_rdev), minor (st.st_rdev)); + } else { + if (bdev = strrchr (dev, '/')) + ++bdev; + else + bdev = dev; + bdev = strdup (bdev); + } + len = strlen (prefix); + if (bdev && (s -> lck = malloc (len + strlen (bdev) + 4))) { + sprintf (s -> lck, "%s%s", prefix, bdev); + free (bdev); + if (upper || lower) { + ptr = s -> lck + len; + while (*ptr) { + if (lower) + *ptr = tolower (*ptr); + else if (upper) + *ptr = toupper (*ptr); + ++ptr; + } + } + do { + for (n = 0; n < 2; ++n) { + if ((fd = open (s -> lck, O_CREAT | O_EXCL | O_WRONLY, 0600)) != -1) + break; + if ((! n) && ((fd = open (s -> lck, O_RDONLY)) != -1)) { + pid = 0; + if (binary) { + if (read (fd, & pid, sizeof (pid)) != sizeof (pid)) + pid = 0; + } else { + if ((m = read (fd, buf, sizeof (buf) - 1)) > 1) { + buf[m - 1] = '\0'; + pid = (int) atoi (buf); + } + } + close (fd); + fd = -1; + if ((pid > 0) && (kill (pid, 0) < 0) && (errno == ESRCH)) + unlink (s -> lck); + else + break; + } + } + if ((fd < 0) && (tout > 0)) + sleep (1); + } while ((fd < 0) && (tout-- > 0)); + if (fd != -1) { + pid = getpid (); + if (binary) + write (fd, & pid, sizeof (pid)); + else { + sprintf (buf, "%10d\n", (int) pid); + write (fd, buf, strlen (buf)); + } +# if HAVE_FCHMOD + fchmod (fd, 0644); +# else /* HAVE_FCHMOD */ + chmod (s -> lck, 0644); +# endif /* HAVE_FCHMOD */ +# if HAVE_FCHOWN + fchown (fd, geteuid (), getegid ()); +# else /* HAVE_FCHOWN */ + chown (s -> lck, geteuid (), getegid ()); +# endif /* HAVE_FCHOWN */ + close (fd); + } else { + free (s -> lck); + s -> lck = NULL; + return False; + } + } else { + if (bdev) + free (bdev); + return False; + } + } + return True; +} + +static void +do_unlock (serial *s) +{ + if (s -> lck) { + unlink (s -> lck); + free (s -> lck); + s -> lck = NULL; + } +} +/*}}}*/ +/*{{{ open/close/reopen */ +void * +tty_open (char *dev, char *lckprefix, char *lckmethod) +{ + serial *s; + int n; + + if (s = (serial *) malloc (sizeof (serial))) { +# ifndef NDEBUG + s -> magic = MAGIC; +# endif /* NDEBUG */ + if (do_lock (s, dev, lckprefix, lckmethod)) { + if ((s -> fd = open (dev, O_RDWR)) != -1) { + n = tcgetattr (s -> fd, & s -> sav); + if ((n < 0) || (! (s -> device = strdup (dev)))) { + close (s -> fd); + do_unlock (s); + free (s); + s = NULL; + } else { + s -> tty = s -> sav; + s -> line = NULL; + s -> sep = NULL; + s -> callback = NULL; + s -> data = NULL; + s -> suspend = False; + } + } else { + do_unlock (s); + free (s); + s = NULL; + } + } else { + free (s); + s = NULL; + } + } + return s; +} + +void * +tty_close (void *sp) +{ + serial *s = (serial *) sp; + + MCHK (s); + if (s) { + if (s -> fd != -1) { + tcsetattr (s -> fd, TCSANOW, & s -> sav); + close (s -> fd); + } + do_unlock (s); + if (s -> device) + free (s -> device); + if (s -> sep) + free (s -> sep); + if (s -> line) + sfree (s -> line); + free (s); + } + return NULL; +} + +Bool +tty_reopen (void *sp, int msec) +{ + serial *s = (serial *) sp; + + MCHK (s); + if (s -> fd != -1) { + close (s -> fd); + if (msec > 0) + msleep (msec); + s -> fd = -1; + } + if (s -> device && ((s -> fd = open (s -> device, O_RDWR)) != -1)) + tcsetattr (s -> fd, TCSANOW, & s -> tty); + return s -> fd != -1 ? True : False; +} +/*}}}*/ +/*{{{ hangup, get fd */ +void +tty_hangup (void *sp, int msec) +{ + serial *s = (serial *) sp; + struct termios tmp; + + MCHK (s); + V (2, ("[Hangup] ")); + if (s && (s -> fd != -1)) { + tmp = s -> tty; + cfsetispeed (& tmp, B0); + cfsetospeed (& tmp, B0); + if (tcsetattr (s -> fd, TCSANOW, & tmp) != -1) { + msleep (msec); + tcsetattr (s -> fd, TCSANOW, & s -> tty); + } + tty_reopen (s, msec); + } + V (2, ("\n")); +} + +int +tty_fd (void *sp) +{ + serial *s = (serial *) sp; + + MCHK (s); + return s ? s -> fd : -1; +} +/*}}}*/ +/*{{{ configuration */ +int +tty_setup (void *sp, Bool raw, Bool modem, int speed, int bpb, int sb, char par) +{ + serial *s = (serial *) sp; + int n; + + MCHK (s); + if ((! s) || (s -> fd < 0)) + return -1; + if ((bpb < 5) || (bpb > 8) || + ((sb != 1) && (sb != 2)) || + ((par != 'n') && (par != 'e') && (par != 'o'))) + return -1; + for (n = 0; stab[n].speed > 0; ++n) + if (stab[n].speed == speed) + break; + if (stab[n].speed < 0) + return -1; + if (raw) { + s -> tty.c_iflag &= ~(IGNCR | ICRNL | INLCR | ISTRIP | IXON | IXOFF); + s -> tty.c_iflag |= IGNBRK; + s -> tty.c_oflag = 0; + s -> tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG); +# ifdef CRTSCTS +# ifdef sun + /* SunOS 4.x needs RTSCTS off if carrier detect is on */ + /* but there is no carrier present (fkk) */ + s -> tty.c_cflag &= ~CRTSCTS; +# else /* sun */ + s -> tty.c_cflag |= CRTSCTS; +# endif /* sun */ +# endif /* CRTSCTS */ + s -> tty.c_cc[VMIN] = 0; + s -> tty.c_cc[VTIME] = 0; + } else { + s -> tty = s -> sav; + s -> tty.c_lflag |= ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG; + } + if (modem) { +# if 0 + s -> tty.c_cflag &= ~CLOCAL; +# else + s -> tty.c_cflag |= CLOCAL; +# endif + s -> tty.c_cflag |= HUPCL; + } else { + s -> tty.c_cflag |= CLOCAL; + s -> tty.c_cflag &= ~HUPCL; + } + cfsetispeed (& s -> tty, stab[n].tok); + cfsetospeed (& s -> tty, stab[n].tok); + s -> tty.c_cflag &= ~(CSIZE); + s -> tty.c_cflag |= CREAD; + switch (sb) { + default: + case 1: s -> tty.c_cflag &= ~CSTOPB; break; + case 2: s -> tty.c_cflag |= CSTOPB; break; + } + switch (bpb) { + case 5: s -> tty.c_cflag |= CS5; break; + case 6: s -> tty.c_cflag |= CS6; break; + case 7: s -> tty.c_cflag |= CS7; break; + default: + case 8: s -> tty.c_cflag |= CS8; break; + } + switch (par) { + default: + case 'n': + s -> tty.c_cflag &= ~PARENB; + break; + case 'e': + s -> tty.c_cflag &= ~PARODD; + s -> tty.c_cflag |= PARENB; + break; + case 'o': + s -> tty.c_cflag |= PARENB | PARODD; + break; + } + return tcsetattr (s -> fd, TCSANOW, & s -> tty) < 0 ? -1 : 0; +} +/*}}}*/ +/*{{{ callback */ +void +tty_set_line_callback (void *sp, void (*func) (void *, string_t *, char_t, void *), char *sep, void *data) +{ + serial *s = (serial *) sp; + + MCHK (s); + if (s) { + if (! (s -> callback = func)) { + if (s -> line) + s -> line = sfree (s -> line); + if (s -> sep) { + free (s -> sep); + s -> sep = NULL; + } + s -> data = NULL; + } else { + if (s -> sep) + free (s -> sep); + s -> sep = sep ? strdup (sep) : NULL; + s -> data = data; + } + s -> suspend = False; + } +} + +void +tty_suspend_callback (void *sp, Bool susp) +{ + serial *s = (serial *) sp; + + MCHK (s); + if (s) + if (s -> suspend = susp) + if (s -> line) + s -> line -> len = 0; +} +/*}}}*/ +/*{{{ sending */ +int +tty_send (void *sp, char *str, int len) +{ + serial *s = (serial *) sp; + int n, sent; + + MCHK (s); + if ((! s) || (s -> fd < 0) || (! str)) + return -1; + V (2, ("[Send] %s", mkprint (str, len))); + sent = 0; + while (len > 0) + if ((n = write (s -> fd, str, len)) > 0) { + str += n; + sent += n; + len -= n; + } else if (! n) + break; + else if (errno == EIO) { + if (! tty_reopen (s, 0)) + break; + } else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + msleep (200); + else if (errno != EINTR) + break; + V (2, ("\n")); + return sent; +} + +int +tty_send_string (void *sp, char *str) +{ + return str ? tty_send (sp, str, strlen (str)) : -1; +} +/*}}}*/ +/*{{{ expecting */ +static void +addline (serial *s, char ch) +{ + if (s -> callback && s -> sep && (! s -> suspend) && ch) { + if (! s -> line) + s -> line = snew (NULL, 32); + else if (s -> line -> len + 1 >= s -> line -> size) + if (! sexpand (s -> line, s -> line -> size * 2)) + return; + if (strchr (s -> sep, ch)) { + (*s -> callback) ((void *) s, s -> line, (char_t) ch, s -> data); + s -> line -> len = 0; + } else + s -> line -> str[s -> line -> len++] = (char_t) ch; + } +} + +static int +do_expect (serial *s, int tout, expect *base) +{ + int ret; + int msec; + int n; + expect *run; + char ch; + + if ((! s) || (s -> fd < 0)) + return -1; + V (2, ("[Expect] ")); + for (run = base; run; run = run -> next) + run -> pos = 0; + msec = (tout > 0) ? tout * 1000 : 0; + ret = 0; + while (! ret) + if ((n = data_ready (s -> fd, & msec)) > 0) { + while ((n = read (s -> fd, & ch, 1)) == 1) { + addline (s, ch); + V (3, ("%s", mkprint (& ch, 1))); + for (run = base; run; run = run -> next) + if (run -> str[run -> pos] == ch) { + run -> pos++; + if (run -> pos == run -> len) { + ret = run -> idx; + break; + } + } else + run -> pos = 0; + if (ret) + break; + } + if (n < 0) + if (errno == EIO) + tty_reopen (s, 0); + } else if (! n) + break; + if (verbose > 1) + if (run) + printf (" got %s", mkprint (run -> str, run -> len)); + else + printf (" timeout"); + V (2, ("\n")); + return ret; +} + +int +tty_expect (void *sp, int tout, ...) +{ + va_list par; + int ret; + char *ptr; + expect *base, *prev, *tmp; + int idx; + + va_start (par, tout); + MCHK ((serial *) sp); + ret = 0; + base = NULL; + prev = NULL; + idx = 1; + while (ptr = va_arg (par, char *)) + if (tmp = (expect *) malloc (sizeof (expect))) { + tmp -> idx = idx++; + tmp -> str = ptr; + tmp -> pos = 0; + tmp -> len = va_arg (par, int); + tmp -> next = NULL; + if (prev) + prev -> next = tmp; + else + base = tmp; + prev = tmp; + } else + break; + if (! ptr) + ret = do_expect ((serial *) sp, tout, base); + else + ret = -1; + while (base) { + tmp = base; + base = base -> next; + free (tmp); + } + va_end (par); + return ret; +} + +int +tty_expect_list (void *sp, int tout, char **strs, int *lens) +{ + int n; + int ret; + expect *base, *prev, *tmp; + + MCHK ((serial *) sp); + base = NULL; + prev = NULL; + for (n = 0; strs[n]; ++n) + if (tmp = (expect *) malloc (sizeof (expect))) { + tmp -> idx = n + 1; + tmp -> str = strs[n]; + tmp -> pos = 0; + tmp -> len = lens[n]; + tmp -> next = NULL; + if (prev) + prev -> next = tmp; + else + base = tmp; + prev = tmp; + } else + break; + if (strs[n]) + ret = -1; + else + ret = do_expect ((serial *) sp, tout, base); + while (base) { + tmp = base; + base = base -> next; + free (tmp); + } + return ret; +} + +int +tty_expect_string (void *sp, int tout, char *str) +{ + expect tmp; + + MCHK ((serial *) sp); + if (! str) + return -1; + tmp.idx = 1; + tmp.str = str; + tmp.pos = 0; + tmp.len = strlen (str); + tmp.next = NULL; + return do_expect ((serial *) sp, tout, & tmp); +} +/*}}}*/ +/*{{{ send/expect */ +static int +tonum (char ch) +{ + switch (ch) { + default: + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + } +} + +static char * +expand (char *str, char **opts, int ocnt) +{ + char *ret; + int len, siz; + int idx, olen; + + ret = NULL; + len = 0; + siz = 0; + while (*str) { + if (len >= siz) { + siz += (siz ? siz : 32); + if (! (ret = Realloc (ret, siz + 2))) + break; + } + if (*str == '\\') { + ++str; + if (*str) { + switch (*str) { + case 'a': ret[len++] = '\a'; break; + case 'b': ret[len++] = '\b'; break; + case 'f': ret[len++] = '\f'; break; + case 'l': ret[len++] = '\012'; break; + case 'n': ret[len++] = '\n'; break; + case 'r': ret[len++] = '\r'; break; + case 's': ret[len++] = ' '; break; + case 't': ret[len++] = '\t'; break; + default: ret[len++] = *str; break; + } + ++str; + } + } else if (*str == '^') { + ++str; + if (*str) { + if (*str == '?') + ret[len++] = '\x7f'; + else + ret[len++] = *str & 0x1f; + ++str; + } + } else if (*str == '%') { + ++str; + idx = 0; + while (isdigit (*str)) { + idx *= 10; + idx += tonum (*str++); + } + if ((idx >= 0) && (idx < ocnt)) { + olen = strlen (opts[idx]); + if (len + olen >= siz) { + siz = len + olen + 64; + if (! (ret = Realloc (ret, siz + 2))) + break; + } + strcpy (ret + len, opts[idx]); + len += olen; + } + } else if ((*str == '\'') || (*str == '"')) + ++str; + else + ret[len++] = *str++; + } + if (ret) + ret[len] = '\0'; + return ret; +} + +static int +handle_command (void *sp, char *str) +{ + serial *s = (serial *) sp; + char *p1; + int mult; + int ret; + + ret = 0; + V (2, ("[Cmd")); + for (p1 = str; *p1; ) { + if (isdigit (*p1)) { + mult = 0; + while (isdigit (*p1)) { + mult *= 10; + mult += tonum (*p1++); + } + } else + mult = 1; + if (*p1) + switch (*p1++) { + case 'd': + V (2, (" Dzz %d", mult)); + sleep (mult); + break; + case 'D': + V (2, (" Mdzz %d", mult)); + msleep (mult); + break; + case 'b': + if (s && (s -> fd != -1)) { + V (2, (" Brk %d", mult)); + tcsendbreak (s -> fd, mult); + } + break; + case 'h': + if (s && (s -> fd != -1)) { + V (2, (" Hup")); + tty_hangup (sp, mult * 500); + } + break; + case 'o': + if (s && (s -> fd != -1)) { + V (2, (" Drain")); + tcdrain (s -> fd); + } + break; + case '<': + if (s && (s -> fd != -1)) { + V (2, (" Iflush")); + tcflush (s -> fd, TCIFLUSH); + } + break; + case '>': + if (s && (s -> fd != -1)) { + V (2, (" Oflush")); + tcflush (s -> fd, TCOFLUSH); + } + break; + case 'f': + V (2, (" fail")); + ret = -1; + break; + case 's': + V (2, (" success")); + ret = 1; + break; + } + } + V (2, ("]\n")); + return ret; +} + +static int +expect_expr (void *sp, int deftout, char *line) +{ + int ret; + char **ex; + int cnt, siz; + int n, m, tout, slen; + char *ptr; + char *p1, *p2; + char **list; + int *len; + + MCHK ((serial *) sp); + ex = NULL; + cnt = 0; + siz = 0; + for (ptr = line; *ptr; ) { + if (cnt >= siz) { + siz += 4; + if (! (ex = (char **) Realloc (ex, (siz + 1) * sizeof (char *)))) + break; + } + ex[cnt++] = ptr; + while (*ptr && (*ptr != '-')) + ++ptr; + if (*ptr) + *ptr++ = '\0'; + } + if (! ex) + return -1; + ret = 0; + for (n = 0; n < cnt; ++n) { + ptr = ex[n]; + if (isdigit (*ptr)) { + tout = 0; + while (isdigit (*ptr)) { + tout *= 10; + tout += tonum (*ptr++); + } + } else + tout = deftout; + p1 = ptr; + for (siz = 1, p2 = p1; p2; p2 = strchr (p2, '|')) + ++siz, ++p2; + if ((list = (char **) malloc ((siz + 1) * sizeof (char *))) && + (len = (int *) malloc ((siz + 1) * sizeof (int)))) { + for (n = 0, p1 = ptr; p1; ++n) { + p2 = p1; + if (p1 = strchr (p1, '|')) + *p1++ = '\0'; + list[n] = p2; + len[n] = strlen (p2); + } + list[n] = NULL; + len[n] = 0; + if (tty_expect_list (sp, tout, list, len) != 1) + ret = -1; + free (list); + free (len); + } else { + ret = -1; + break; + } + if ((ret < 0) && (n + 1 < cnt)) { + ++n; + ptr = ex[n]; + ret = 0; + if (*ptr == '!') { + if (m = handle_command (sp, ptr + 1)) { + if (m < 0) + ret = -1; + break; + } + } else { + slen = strlen (ptr); + if (tty_send (sp, ptr, slen) != slen) { + ret = -1; + break; + } + } + } else + n = cnt; + } + if (ex) + free (ex); + return ret; +} + +int +tty_send_expect (void *sp, int deftout, char *str, char **opts) +{ + serial *s = (serial *) sp; + int ret; + int ocnt; + char *sav, *ptr, *line; + int n; + char quote; + Bool esc; + + MCHK (s); + ret = -1; + if (opts) + for (ocnt = 0; opts[ocnt]; ++ocnt) + ; + else + ocnt = 0; + if (str = strdup (str)) { + ret = 0; + for (ptr = str; *ptr && (! ret); ) { + sav = ptr; + esc = False; + quote = '\0'; + while (*ptr) { + if (esc) + esc = False; + else if (*ptr == '\\') + esc = True; + else if (quote) { + if (*ptr == quote) + quote = '\0'; + } else if ((*ptr == '\'') || (*ptr == '"')) + quote = *ptr; + else if (isspace (*ptr)) + break; + ++ptr; + } + if (*ptr) { + *ptr++ = '\0'; + while (isspace (*ptr)) + ++ptr; + } + if (line = expand (sav, opts, ocnt)) { + if (line[0] == '<') { + if (expect_expr (sp, deftout, line + 1) < 0) + ret = -1; + } else if (line[0] == '!') { + if ((n = handle_command (sp, line + 1)) < 0) + ret = -1; + else if (n > 0) + while (*ptr) + ++ptr; + } else { + n = strlen (line); + if (tty_send (sp, line, n) != n) + ret = -1; + } + free (line); + } else + ret = -1; + } + free (str); + } + return ret; +} +/*}}}*/ +/*{{{ draining */ +void +tty_mdrain (void *sp, int msecs) +{ + serial *s = (serial *) sp; + int n, m; + char ch; + + MCHK (s); + if (s && (s -> fd != -1)) { + V (2, ("[Drain] ")); + if (msecs < 0) + msecs = 0; + do { + if ((n = data_ready (s -> fd, & msecs)) > 0) { + while ((m = read (s -> fd, & ch, 1)) == 1) { + addline (s, ch); + V (2, ("%s", mkprint (& ch, 1))); + } + if (m < 0) + if (errno == EIO) + tty_reopen (s, 0); + } + } while (n != 0); + V (2, ("\n")); + } +} + +void +tty_drain (void *sp, int secs) +{ + tty_mdrain (sp, secs * 1000); +} +/*}}}*/ diff --git a/ucp.c b/ucp.c new file mode 100644 index 0000000..6d7b3fc --- /dev/null +++ b/ucp.c @@ -0,0 +1,1801 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +# include +# include +# include +# include "pager.h" + +/*{{{ typedefs */ +typedef struct { +# ifndef NDEBUG +# define MAGIC MKMAGIC ('u', 'c', 'p', '\0') + long magic; +# endif /* NDEBUG */ + void *sp; /* the serial connection */ + void *ctab; /* the conversion table */ + void (*logger) (char, char *, ...); + char *uline; /* received input line */ + + string_t + *callid; /* caller id */ + Bool xtend; /* use extended UCP version */ + int send_tout; /* timeout during sending */ + int send_retry; /* # of retries to send a message */ + int rds_tout; /* timeout for status report */ + date_t delay; /* delay message */ + date_t expire; /* expire message */ + Bool rds; /* request delivery status */ + + int cnr; /* current transaction number */ +} ucp; + +typedef struct { + char *AdC; + char *OAdC; + char *PID; + char *na; + char *MT; + char *Msg; + + char *adc; + char *oadc; + int pid; + int mt; + string_t *msg; +} bsimple; + +typedef struct { + char *AdC; + char *OAdC; + char *AC; + char *NRq; + char *NAdC; + char *NT; + char *NPID; + char *na1; + char *na2; + char *na3; + char *DD; + char *DDT; + char *VP; + char *RPID; + char *SCTS; + char *DSt; + char *Rsn; + char *DSCTS; + char *MT; + char *NB; + char *Msg; + char *MMS; + char *na4; + char *DCS; + char *MCL; + char *RPI; + char *na5; + char *na6; + char *res1; + char *res2; + char *res3; + char *res4; + char *res5; + + char *adc; + char *oadc; + char *ac; + Bool nrq; + char *nadc; + int nt; + int npid; + Bool dd; + date_t ddt; + date_t vp; + int rpid; + date_t scts; + int dst; + int rsn; + date_t dscts; + int mt; + int nb; + string_t *msg; + Bool mms; + Bool dcs; + int mcl; + int rpi; +} bextend; + +typedef struct { + char *Res; + char *MVP; + char *EC; + char *MSG; + + Bool ack; + date_t mvp; + int ec; + char *adc; + date_t scts; +} banswer; + +typedef struct { + int trn; + int len; + char ttyp; + int ot; + char *data; + int cnt; + union { + bsimple s; + bextend e; + banswer a; + } b; + int chksum; +} frame; +/*}}}*/ +/*{{{ support routines */ +static char_t +hex (int val) +{ + switch (val) { + default: + case 0: return (char_t) '0'; + case 1: return (char_t) '1'; + case 2: return (char_t) '2'; + case 3: return (char_t) '3'; + case 4: return (char_t) '4'; + case 5: return (char_t) '5'; + case 6: return (char_t) '6'; + case 7: return (char_t) '7'; + case 8: return (char_t) '8'; + case 9: return (char_t) '9'; + case 10: return (char_t) 'A'; + case 11: return (char_t) 'B'; + case 12: return (char_t) 'C'; + case 13: return (char_t) 'D'; + case 14: return (char_t) 'E'; + case 15: return (char_t) 'F'; + } +} + +static int +unhex (char ch) +{ + switch (ch) { + default: + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'A': + case 'a': return 10; + case 'B': + case 'b': return 11; + case 'C': + case 'c': return 12; + case 'D': + case 'd': return 13; + case 'E': + case 'e': return 14; + case 'F': + case 'f': return 15; + } +} +/*}}}*/ +/*{{{ frame basics */ +static frame * +new_frame (char ttyp, int ot) +{ + frame *f; + bsimple *s; + bextend *e; + banswer *a; + + if (f = (frame *) malloc (sizeof (frame))) { + f -> trn = 0; + f -> len = 0; + f -> ttyp = ttyp; + f -> ot = ot; + f -> data = NULL; + f -> cnt = 0; + memset (& f -> b, 0, sizeof (f -> b)); + f -> chksum = 0; + if (f -> ttyp == 'R') + switch (f -> ot) { + case 1: + case 31: + case 51: + case 52: + case 53: + case 55: + case 56: + case 57: + case 58: + a = & f -> b.a; + a -> Res = NULL; + a -> MVP = NULL; + a -> EC = NULL; + a -> MSG = NULL; + + a -> ack = False; + dat_clear (& a -> mvp); + a -> ec = -1; + a -> adc = NULL; + dat_clear (& a -> scts); + break; + } + else if (f -> ttyp == 'O') + switch (f -> ot) { + case 1: + case 31: + s = & f -> b.s; + s -> AdC = NULL; + s -> OAdC = NULL; + s -> PID = NULL; + s -> na = NULL; + s -> MT = NULL; + s -> Msg = NULL; + + s -> adc = NULL; + s -> oadc = NULL; + s -> pid = -1; + s -> mt = -1; + s -> msg = NULL; + break; + case 51: + case 52: + case 53: + case 55: + case 56: + case 57: + case 58: + e = & f -> b.e; + e -> AdC = NULL; + e -> OAdC = NULL; + e -> AC = NULL; + e -> NRq = NULL; + e -> NAdC = NULL; + e -> NT = NULL; + e -> NPID = NULL; + e -> na1 = NULL; + e -> na2 = NULL; + e -> na3 = NULL; + e -> DD = NULL; + e -> DDT = NULL; + e -> VP = NULL; + e -> RPID = NULL; + e -> SCTS = NULL; + e -> DSt = NULL; + e -> Rsn = NULL; + e -> DSCTS = NULL; + e -> MT = NULL; + e -> NB = NULL; + e -> Msg = NULL; + e -> MMS = NULL; + e -> na4 = NULL; + e -> DCS = NULL; + e -> MCL = NULL; + e -> RPI = NULL; + e -> na5 = NULL; + e -> na6 = NULL; + e -> res1 = NULL; + e -> res2 = NULL; + e -> res3 = NULL; + e -> res4 = NULL; + e -> res5 = NULL; + + e -> adc = NULL; + e -> oadc = NULL; + e -> ac = NULL; + e -> nrq = False; + e -> nadc = NULL; + e -> nt = -1; + e -> npid = -1; + e -> dd = False; + dat_clear (& e -> ddt); + dat_clear (& e -> vp); + e -> rpid = -1; + dat_clear (& e -> scts); + e -> dst = -1; + e -> rsn = -1; + dat_clear (& e -> dscts); + e -> mt = -1; + e -> nb = -1; + e -> msg = NULL; + e -> mms = False; + e -> dcs = False; + e -> mcl = -1; + e -> rpi = -1; + break; + } + } + return f; +} + +static void +free_body (frame *f) +{ +# define dfr(xxx) if (xxx) free (xxx) + bsimple *s; + bextend *e; + banswer *a; + + s = NULL; + e = NULL; + a = NULL; + if (f -> ttyp == 'O') + switch (f -> ot) { + case 1: + case 31: + s = & f -> b.s; + break; + case 51: + case 52: + case 53: + case 55: + case 56: + case 57: + case 58: + e = & f -> b.e; + break; + } + else if (f -> ttyp == 'R') + switch (f -> ot) { + case 1: + case 31: + case 51: + case 52: + case 53: + case 55: + case 56: + case 57: + case 58: + a = & f -> b.a; + break; + } + if (s) { + dfr (s -> AdC); + dfr (s -> OAdC); + dfr (s -> PID); + dfr (s -> na); + dfr (s -> MT); + dfr (s -> Msg); + dfr (s -> adc); + dfr (s -> oadc); + sfree (s -> msg); + } else if (e) { + dfr (e -> AdC); + dfr (e -> OAdC); + dfr (e -> AC); + dfr (e -> NRq); + dfr (e -> NAdC); + dfr (e -> NT); + dfr (e -> NPID); + dfr (e -> na1); + dfr (e -> na2); + dfr (e -> na3); + dfr (e -> DD); + dfr (e -> DDT); + dfr (e -> VP); + dfr (e -> RPID); + dfr (e -> SCTS); + dfr (e -> DSt); + dfr (e -> Rsn); + dfr (e -> DSCTS); + dfr (e -> MT); + dfr (e -> NB); + dfr (e -> Msg); + dfr (e -> MMS); + dfr (e -> na4); + dfr (e -> DCS); + dfr (e -> MCL); + dfr (e -> RPI); + dfr (e -> na5); + dfr (e -> na6); + dfr (e -> res1); + dfr (e -> res2); + dfr (e -> res3); + dfr (e -> res4); + dfr (e -> res5); + + dfr (e -> adc); + dfr (e -> oadc); + dfr (e -> ac); + dfr (e -> nadc); + sfree (e -> msg); + } else if (a) { + dfr (a -> Res); + dfr (a -> MVP); + dfr (a -> EC); + dfr (a -> MSG); + dfr (a -> adc); + } +# undef dfr +} + +static void +free_frame (frame *f) +{ + if (f) { + if (f -> data) + free (f -> data); + free_body (f); + free (f); + } +} +/*}}}*/ +/*{{{ assemble */ +static char * +encode (ucp *u, string_t *s, Bool docv) +{ + char *ret; + char_t ch; + int c; + int len; + int n, m; + + len = s ? (s -> len * 2) : 0; + if (ret = malloc (len + 2)) { + for (n = 0, m = 0; n < len; ++m) { + if (docv) { + if ((c = cv_conv (u -> ctab, s -> str[m])) < 0) + continue; + ch = (char_t) c; + } else + ch = s -> str[m]; + ret[n++] = hex ((ch >> 4) & 0xf); + ret[n++] = hex (ch & 0xf); + } + ret[n] = '\0'; + } + return ret; +} + +static inline void +gbool (char **ptr, Bool what, char *t, char *f) +{ + if (*ptr) + free (*ptr); + if (what) + *ptr = t ? strdup (t) : NULL; + else + *ptr = f ? strdup (f) : NULL; +} + +static inline void +gdate (char **ptr, date_t *d, int len) +{ + if (*ptr) + free (*ptr); + if (d -> day > 0) { + if (*ptr = malloc (14)) { + sprintf (*ptr, "%02d%02d%02d%02d%02d%02d", + d -> day, d -> mon, d -> year % 100, + d -> hour, d -> min, d -> sec); + if (len < 12) + (*ptr)[len] = '\0'; + } + } else + *ptr = NULL; +} + +static inline void +gint (char **ptr, int val, char *fmt, int def) +{ + if (*ptr) + free (*ptr); + if (val == -1) + val = def; + if (val != -1) { + if (*ptr = malloc (32)) + sprintf (*ptr, (fmt ? fmt : "%d"), val); + } else + *ptr = NULL; +} + +static inline void +gstr (char **ptr, char *str) +{ + if (*ptr) + free (*ptr); + *ptr = str ? strdup (str) : NULL; +} + +static string_t * +assemble_frame (ucp *u, frame *f) +{ + bsimple *s; + bextend *e; + banswer *a; + string_t *ret; + Bool fail; + char buf[64], *ptr; + int len, n; + unsigned long chk; + + if (! (ret = snew (NULL, 128))) + return NULL; + if (! (scopyc (ret, "\x02") && scatc (ret, "00/00000/"))) + return sfree (ret); + sprintf (buf, "%c/%02d/", f -> ttyp, f -> ot); + if (! scatc (ret, buf)) + return sfree (ret); + fail = False; + if (f -> ttyp == 'R') { + a = & f -> b.a; + switch (f -> ot) { + default: + fail = True; + break; + case 1: + case 31: + case 51: + case 52: + case 53: + case 55: + case 56: + case 57: + case 58: + gbool (& a -> Res, a -> ack, "A", "N"); + if (a -> MSG) { + free (a -> MSG); + a -> MSG = NULL; + } + if (a -> ack) { + gdate (& a -> MVP, & a -> mvp, 10); + ptr = NULL; + gdate (& ptr, & a -> scts, 12); + if (ptr) { + if (a -> adc) { + if (a -> MSG = malloc (strlen (ptr) + strlen (a -> adc) + 4)) + sprintf (a -> MSG, "%s:%s", a -> adc, ptr); + free (ptr); + } else + a -> MSG = ptr; + } else if (a -> adc) + a -> MSG = strdup (a -> adc); + } else + gint (& a -> EC, a -> ec, NULL, 3); + if (((f -> ot == 1) || (f -> ot == 31)) && a -> ack) { + if (! (scatc (ret, a -> Res) && scatc (ret, "/") && + scatc (ret, a -> MSG) && scatc (ret, "/"))) + fail = True; + } else if (! (scatc (ret, a -> Res) && scatc (ret, "/") && + scatc (ret, (a -> ack ? a -> MVP : a -> EC)) && scatc (ret, "/") && + scatc (ret, a -> MSG) && scatc (ret, "/"))) + fail = True; + break; + } + } else if (f -> ttyp == 'O') { + s = & f -> b.s; + e = & f -> b.e; + switch (f -> ot) { + default: + fail = True; + break; + case 1: + gstr (& s -> AdC, s -> adc); + gstr (& s -> OAdC, s -> oadc); + gstr (& s -> na, NULL); + gint (& s -> MT, s -> mt, NULL, 3); + if (s -> Msg) + free (s -> Msg); + s -> Msg = encode (u, s -> msg, True); + if (! (scatc (ret, s -> AdC) && scatc (ret, "/") && + scatc (ret, s -> OAdC) && scatc (ret, "/") && + scatc (ret, s -> na) && scatc (ret, "/") && + scatc (ret, s -> MT) && scatc (ret, "/") && + scatc (ret, s -> Msg) && scatc (ret, "/"))) + fail = True; + break; + case 31: + gstr (& s -> AdC, s -> adc); + gint (& s -> PID, s -> pid, "%04d", -1); + if (! (scatc (ret, s -> AdC) && scatc (ret, "/") && + scatc (ret, s -> PID) && scatc (ret, "/"))) + fail = True; + break; + case 51: + case 52: + case 53: + case 55: + case 56: + case 57: + case 58: + gstr (& e -> AdC, e -> adc); + gstr (& e -> OAdC, e -> oadc); + gstr (& e -> AC, e -> ac); + gbool (& e -> NRq, e -> nrq, "1", NULL); + gstr (& e -> NAdC, (e -> nrq ? e -> nadc : NULL)); + gint (& e -> NT, (e -> nrq ? e -> nt : -1), NULL, -1); + gint (& e -> NPID, (e -> nrq ? e -> npid : -1), "%04d", -1); + gstr (& e -> na1, NULL); + gstr (& e -> na2, NULL); + gstr (& e -> na3, NULL); + gbool (& e -> DD, e -> dd, "1", NULL); + if (e -> dd) + gdate (& e -> DDT, & e -> ddt, 10); + else + gstr (& e -> DDT, NULL); + gdate (& e -> VP, & e -> vp, 10); + gint (& e -> RPID, e -> rpid, "%04d", -1); + gdate (& e -> SCTS, & e -> scts, 12); + gint (& e -> DSt, e -> dst, NULL, -1); + gint (& e -> Rsn, e -> rsn, "%03d", -1); + gdate (& e -> DSCTS, & e -> dscts, 12); + gint (& e -> MT, e -> mt, NULL, 3); + if (e -> mt == 4) + if (e -> nb != -1) + len = e -> nb; + else + len = e -> msg ? e -> msg -> len * 8 : 0; + else + len = -1; + gint (& e -> NB, len, NULL, -1); + if (e -> Msg) + free (e -> Msg); + e -> Msg = encode (u, e -> msg, (e -> mt == 4 ? False : True)); + gbool (& e -> MMS, e -> mms, "1", NULL); + gstr (& e -> na4, NULL); + gbool (& e -> DCS, e -> dcs, "1", NULL); + gint (& e -> MCL, e -> mcl, NULL, -1); + gint (& e -> RPI, e -> rpi, NULL, -1); + gstr (& e -> na5, NULL); + gstr (& e -> na6, NULL); + gstr (& e -> res1, NULL); + gstr (& e -> res2, NULL); + gstr (& e -> res3, NULL); + gstr (& e -> res4, NULL); + gstr (& e -> res5, NULL); + if (! (scatc (ret, e -> AdC) && scatc (ret, "/") && + scatc (ret, e -> OAdC) && scatc (ret, "/") && + scatc (ret, e -> AC) && scatc (ret, "/") && + scatc (ret, e -> NRq) && scatc (ret, "/") && + scatc (ret, e -> NAdC) && scatc (ret, "/") && + scatc (ret, e -> NT) && scatc (ret, "/") && + scatc (ret, e -> NPID) && scatc (ret, "/") && + scatc (ret, e -> na1) && scatc (ret, "/") && + scatc (ret, e -> na2) && scatc (ret, "/") && + scatc (ret, e -> na3) && scatc (ret, "/") && + scatc (ret, e -> DD) && scatc (ret, "/") && + scatc (ret, e -> DDT) && scatc (ret, "/") && + scatc (ret, e -> VP) && scatc (ret, "/") && + scatc (ret, e -> RPID) && scatc (ret, "/") && + scatc (ret, e -> SCTS) && scatc (ret, "/") && + scatc (ret, e -> DSt) && scatc (ret, "/") && + scatc (ret, e -> Rsn) && scatc (ret, "/") && + scatc (ret, e -> DSCTS) && scatc (ret, "/") && + scatc (ret, e -> MT) && scatc (ret, "/") && + scatc (ret, e -> NB) && scatc (ret, "/") && + scatc (ret, e -> Msg) && scatc (ret, "/") && + scatc (ret, e -> MMS) && scatc (ret, "/") && + scatc (ret, e -> na4) && scatc (ret, "/") && + scatc (ret, e -> DCS) && scatc (ret, "/") && + scatc (ret, e -> MCL) && scatc (ret, "/") && + scatc (ret, e -> RPI) && scatc (ret, "/") && + scatc (ret, e -> na5) && scatc (ret, "/") && + scatc (ret, e -> na6) && scatc (ret, "/") && + scatc (ret, e -> res1) && scatc (ret, "/") && + scatc (ret, e -> res2) && scatc (ret, "/") && + scatc (ret, e -> res3) && scatc (ret, "/") && + scatc (ret, e -> res4) && scatc (ret, "/") && + scatc (ret, e -> res5) && scatc (ret, "/"))) + fail = True; + break; + } + } else + fail = True; + if (! fail) { + len = ret -> len - 1 + 2; + sprintf (buf, "%02d/%05d", f -> trn, len); + if (sputc (ret, buf, 1, -1)) { + for (n = 1, chk = 0; n < ret -> len; ++n) + chk += ret -> str[n] & 0xff; + if (sexpand (ret, ret -> len + 4)) { + ret -> str[ret -> len++] = hex ((chk >> 4) & 0xf); + ret -> str[ret -> len++] = hex (chk & 0xf); + ret -> str[ret -> len++] = '\x03'; + } + } else + fail = True; + } + if (fail) + ret = sfree (ret); + return ret; +} +/*}}}*/ +/*{{{ parse */ +static date_t +parse_date (char *str) +{ + date_t ret; + int n; + int val; + time_t tim; + struct tm *tt; + + ret.day = 0; + ret.mon = 0; + ret.year = 0; + ret.hour = 0; + ret.min = 0; + ret.sec = 0; + if (str) + for (n = 0; (n < 6) && *str && *(str + 1); ++n) { + val = unhex (*str) * 10 + unhex (*(str + 1)); + str += 2; + switch (n) { + case 0: ret.day = val; break; + case 1: ret.mon = val; break; + case 2: ret.year = val; break; + case 3: ret.hour = val; break; + case 4: ret.min = val; break; + case 5: ret.sec = val; break; + } + } + time (& tim); + if (tt = localtime (& tim)) + ret.year += ((tt -> tm_year + 1900) / 100) * 100; + return ret; +} + +static string_t * +parse_message (ucp *u, int mt, char *msg) +{ + string_t *ret; + int len; + int n; + char_t ch; + void *ct; + + if ((mt != 4) && (u -> ctab)) + ct = cv_reverse (u -> ctab); + else + ct = NULL; + len = strlen (msg); + if (ret = snew (NULL, len / 2 + 2)) + for (n = 0; n + 1 < len; n += 2) { + ch = (unhex (msg[n]) << 4) | unhex (msg[n + 1]); + ret -> str[ret -> len++] = cv_conv (ct, ch); + } + if (ct) + cv_free (ct); + return ret; +} + +static Bool +parse_body (ucp *u, frame *f) +{ + Bool ret; + char *dat, *ptr, *sav; + char **rev; + Bool done, fail; + int n; + + if (! (dat = strdup (f -> data))) + return False; + ret = False; + done = False; + fail = False; + for (ptr = dat, n = 0; (! done) && (! fail) && ptr; ++n) { + sav = ptr; + if (ptr = strchr (ptr, '/')) + *ptr++ = '\0'; + rev = NULL; + if (f -> ttyp == 'O') { + switch (f -> ot) { + case 1: + switch (n) { + case 0: rev = & f -> b.s.AdC; break; + case 1: + rev = & f -> b.s.OAdC; + f -> b.s.adc = f -> b.s.AdC ? strdup (f -> b.s.AdC) : NULL; + break; + case 2: + rev = & f -> b.s.na; + f -> b.s.oadc = f -> b.s.OAdC ? strdup (f -> b.s.OAdC) : NULL; + break; + case 3: rev = & f -> b.s.MT; break; + case 4: + rev = & f -> b.s.Msg; + f -> b.s.mt = f -> b.s.MT ? atoi (f -> b.s.MT) : -1; + done = True; + break; + } + break; + case 31: + switch (n) { + case 0: rev = & f -> b.s.AdC; break; + case 1: + rev = & f -> b.s.PID; + f -> b.s.adc = f -> b.s.AdC ? strdup (f -> b.s.AdC) : NULL; + done = True; + break; + } + break; + case 51: + case 52: + case 53: + case 55: + case 56: + case 57: + case 58: + switch (n) { + case 0: rev = & f -> b.e.AdC; break; + case 1: + rev = & f -> b.e.OAdC; + f -> b.e.adc = f -> b.e.AdC ? strdup (f -> b.e.AdC) : NULL; + break; + case 2: + rev = & f -> b.e.AC; + f -> b.e.oadc = f -> b.e.OAdC ? strdup (f -> b.e.OAdC) : NULL; + break; + case 3: + rev = & f -> b.e.NRq; + f -> b.e.ac = f -> b.e.AC ? strdup (f -> b.e.AC) : NULL; + break; + case 4: + rev = & f -> b.e.NAdC; + if (f -> b.e.NRq) + f -> b.e.nrq = (atoi (f -> b.e.NRq) == 1 ? True : False); + else + f -> b.e.nrq = False; + break; + case 5: + rev = & f -> b.e.NT; + f -> b.e.nadc = f -> b.e.NAdC ? strdup (f -> b.e.NAdC) : NULL; + break; + case 6: + rev = & f -> b.e.NPID; + f -> b.e.nt = f -> b.e.NT ? atoi (f -> b.e.NT) : 0; + break; + case 7: + rev = & f -> b.e.na1; + f -> b.e.npid = f -> b.e.NPID ? atoi (f -> b.e.NPID) : -1; + break; + case 8: rev = & f -> b.e.na2; break; + case 9: rev = & f -> b.e.na3; break; + case 10: rev = & f -> b.e.DD; break; + case 11: + rev = & f -> b.e.DDT; + if (f -> b.e.DD) + f -> b.e.dd = (atoi (f -> b.e.DD) == 1 ? True : False); + else + f -> b.e.dd = False; + break; + case 12: + rev = & f -> b.e.VP; + f -> b.e.ddt = parse_date (f -> b.e.DDT); + break; + case 13: + rev = & f -> b.e.RPID; + f -> b.e.vp = parse_date (f -> b.e.VP); + break; + case 14: + rev = & f -> b.e.SCTS; + f -> b.e.rpid = f -> b.e.RPID ? atoi (f -> b.e.RPID) : -1; + break; + case 15: + rev = & f -> b.e.DSt; + f -> b.e.scts = parse_date (f -> b.e.SCTS); + break; + case 16: + rev = & f -> b.e.Rsn; + f -> b.e.dst = f -> b.e.DSt ? atoi (f -> b.e.DSt) : -1; + break; + case 17: + rev = & f -> b.e.DSCTS; + f -> b.e.rsn = f -> b.e.Rsn ? atoi (f -> b.e.Rsn) : 0; + break; + case 18: + rev = & f -> b.e.MT; + f -> b.e.dscts = parse_date (f -> b.e.DSCTS); + break; + case 19: + rev = & f -> b.e.NB; + f -> b.e.mt = f -> b.e.MT ? atoi (f -> b.e.MT) : -1; + break; + case 20: + rev = & f -> b.e.Msg; + f -> b.e.nb = f -> b.e.NB ? atoi (f -> b.e.NB) : 0; + break; + case 21: + rev = & f -> b.e.MMS; + if (f -> b.e.Msg) + if (! (f -> b.e.msg = parse_message (u, f -> b.e.mt, f -> b.e.Msg))) + fail = True; + break; + case 22: + rev = & f -> b.e.na4; + if (f -> b.e.MMS) + f -> b.e.mms = (atoi (f -> b.e.MMS) == 1 ? True : False); + else + f -> b.e.mms = False; + break; + case 23: rev = & f -> b.e.DCS; break; + case 24: + rev = & f -> b.e.MCL; + if (f -> b.e.DCS) + f -> b.e.dcs = (atoi (f -> b.e.DCS) == 1 ? True : False); + else + f -> b.e.dcs = False; + break; + case 25: + rev = & f -> b.e.RPI; + f -> b.e.mcl = f -> b.e.MCL ? atoi (f -> b.e.MCL) : -1; + break; + case 26: + rev = & f -> b.e.na5; + f -> b.e.rpi = f -> b.e.RPI ? atoi (f -> b.e.RPI) : -1; + break; + case 27: rev = & f -> b.e.na6; break; + case 28: rev = & f -> b.e.res1; break; + case 29: rev = & f -> b.e.res2; break; + case 30: rev = & f -> b.e.res3; break; + case 31: rev = & f -> b.e.res4; break; + case 32: + rev = & f -> b.e.res5; + done = True; + break; + } + break; + } + } else if (f -> ttyp == 'R') { + switch (f -> ot) { + case 1: + case 31: + switch (n) { + case 0: rev = & f -> b.a.Res; break; + case 1: + if (f -> b.a.Res) { + if (f -> b.a.Res[0] == 'A') { + f -> b.a.ack = True; + rev = & f -> b.a.MSG; + done = True; + } else if (f -> b.a.Res[0] == 'N') { + f -> b.a.ack = False; + rev = & f -> b.a.EC; + } else + fail = True; + } + break; + case 2: + if (! f -> b.a.ack) { + f -> b.a.ec = f -> b.a.EC ? atoi (f -> b.a.EC) : -1; + rev = & f -> b.a.MSG; + done = True; + } + break; + } + break; + case 51: + case 52: + case 53: + case 55: + case 56: + case 57: + case 58: + switch (n) { + case 0: rev = & f -> b.a.Res; break; + case 1: + if (f -> b.a.Res) { + if (f -> b.a.Res[0] == 'A') { + f -> b.a.ack = True; + rev = & f -> b.a.MVP; + } else if (f -> b.a.Res[0] == 'N') { + f -> b.a.ack = False; + rev = & f -> b.a.EC; + } else + fail = True; + } else + fail = True; + break; + case 2: + if (f -> b.a.ack) + f -> b.a.mvp = parse_date (f -> b.a.MVP); + else + f -> b.a.ec = f -> b.a.EC ? atoi (f -> b.a.EC) : -1; + rev = & f -> b.a.MSG; + done = True; + break; + } + break; + } + } else + fail = True; + if (! fail) { + if (! rev) + fail = True; + else { + if (*sav) { + if (! (*rev = strdup (sav))) + fail = True; + } else + *rev = NULL; + } + if ((! ptr) && (! done)) + fail = True; + } + } + if (done && (! fail) && (! ptr)) { + if (f -> ttyp == 'O') { + switch (f -> ot) { + case 1: + case 31: + if (f -> b.s.Msg) + if (! (f -> b.s.msg = parse_message (u, f -> b.s.mt, f -> b.s.Msg))) + fail = True; + break; + } + } else if (f -> ttyp == 'R') { + switch (f -> ot) { + case 1: + case 31: + case 51: + case 52: + case 53: + case 55: + case 56: + case 57: + case 58: + if (f -> b.a.MSG) { + if (! (f -> b.a.adc = strdup (f -> b.a.MSG))) + fail = True; + else { + if (ptr = strchr (f -> b.a.adc, ':')) + *ptr++ = '\0'; + f -> b.a.scts = parse_date (ptr); + } + } + break; + } + } else + fail = True; + if (! fail) + ret = True; + } + free (dat); + return ret; +} + +static frame * +parse_frame (ucp *u, char *str) +{ + frame *f; + char *start, *end; + char *ptr, *sav; + int n, len; + unsigned int chksum; + + f = NULL; + if (str = strdup (str)) { + start = strchr (str, '\x02'); + end = strchr (str, '\x03'); + ptr = strrchr (str, '/'); + if (start && end && (end > start) && + ptr && (start + 1 < ptr) && (ptr + 3 == end) && + (f = new_frame ('\0', 0))) { + memset (f, 0, sizeof (frame)); + len = (int) ((unsigned long) end - (unsigned long) start) - 1; + for (chksum = 0, n = 1; n < len - 1; ++n) + chksum += (unsigned char) start[n]; + chksum &= 0xff; + ptr = start + 1; + for (n = 0; n < 4; ++n) { + if (! (sav = ptr)) + break; + if (ptr = strchr (ptr, '/')) + *ptr++ = '\0'; + switch (n) { + case 0: + f -> trn = atoi (sav); + break; + case 1: + f -> len = atoi (sav); + break; + case 2: + f -> ttyp = *sav; + break; + case 3: + f -> ot = atoi (sav); + break; + } + } + sav = ptr; + if ((n < 4) || (f -> len != len) || (! sav) || (! (ptr = strrchr (sav, '/')))) { + free (f); + f = NULL; + } else { + f -> cnt = (int) ((unsigned long) ptr - (unsigned long) sav); + ++ptr; + if (*ptr && *(ptr + 1)) + f -> chksum = (unhex (*ptr) << 4) | unhex (*(ptr + 1)); + else + f -> chksum = -1; + if ((chksum == f -> chksum) && (f -> data = malloc (f -> cnt + 1))) { + memcpy (f -> data, sav, f -> cnt); + f -> data[f -> cnt] = '\0'; + if (! parse_body (u, f)) { + free_frame (f); + f = NULL; + } + } else { + free (f); + f = NULL; + } + } + } + free (str); + } + return f; +} +/*}}}*/ +/*{{{ convert */ +static string_t * +convert_ucp (ucp *u, Bool last, string_t *pagerid, string_t *msg) +{ + string_t *ret; + frame *f; + int ot; + int n; + int ch; + char *adc, *oadc; + + ret = NULL; + if (! u -> xtend) + ot = 1; + else + ot = 51; + if (f = new_frame ('O', ot)) { + f -> trn = u -> cnr++; + adc = sextract (pagerid); + oadc = u -> callid ? sextract (u -> callid) : NULL; + if (msg) + msg = snew (msg -> str, msg -> len); + if (u -> xtend) { + f -> b.e.adc = adc; + f -> b.e.oadc = oadc; + if (u -> rds && (u -> delay.day <= 0)) { + f -> b.e.nrq = True; + f -> b.e.nt = 7; + } else + f -> b.e.nrq = False; + if (u -> delay.day > 0) { + f -> b.e.dd = True; + f -> b.e.ddt = u -> delay; + } else + f -> b.e.dd = False; + if (u -> expire.day > 0) + f -> b.e.vp = u -> expire; + for (n = 0; n < msg -> len; ++n) + if ((ch = cv_conv (u -> ctab, msg -> str[n])) != -1) + if (ch & 0x80) + break; + if (n < msg -> len) { + f -> b.e.mt = 4; + f -> b.e.nb = msg -> len * 8; + } else + f -> b.e.mt = 3; + f -> b.e.msg = msg; + } else { + f -> b.s.adc = adc; + f -> b.s.oadc = oadc; + f -> b.s.mt = 3; + f -> b.s.msg = msg; + } + ret = assemble_frame (u, f); + free_frame (f); + } + return ret; +} +/*}}}*/ +/*{{{ callback interface */ +static void +grabline (void *sp, string_t *s, char_t sep, void *data) +{ + ucp *u = (ucp *) data; + char *str; + + MCHK (u); + if (u) { + if (u -> uline) { + free (u -> uline); + u -> uline = NULL; + } + if (str = sextract (s)) { + if (u -> uline = malloc (strlen (str) + 4)) + sprintf (u -> uline, "%s%c", str, (char) sep); + free (str); + } + } +} +/*}}}*/ +/*{{{ interpret originate message */ +static void +interpret_originate (ucp *u, frame *f) +{ + string_t *msg; + + msg = NULL; + switch (f -> ot) { + case 1: + case 31: + msg = f -> b.s.msg; + break; + case 51: + case 52: + case 53: + case 55: + case 56: + case 57: + case 58: + msg = f -> b.e.msg; + break; + } + if (msg) + V (1, ("Got message type %d: `%s'\n", f -> ot, schar (msg))); +} +/*}}}*/ +/*{{{ send a message */ +static Bool +send_msg (ucp *u, string_t *msg, Bool wds, int *err) +{ + Bool ret; + Bool dosend; + int n; + int ep; + frame *f, *ans; + banswer *an; + string_t *astr; + char *ptr; + + ret = False; + dosend = True; + for (n = 0; n < 2; ++n) { + if ((! n) && dosend) { + dosend = False; + if (tty_send (u -> sp, msg -> str, msg -> len) != msg -> len) { + V (1, ("Unable to send message\n")); + *err = ERR_FAIL; + break; + } + } + if (n && (! wds)) + continue; + ep = tty_expect (u -> sp, (n ? u -> rds_tout : u -> send_tout), "\x03", 1, NULL); + if ((ep != 1) || (! u -> uline)) { + if (ep < 0) + *err = ERR_FAIL; + break; + } + if (f = parse_frame (u, u -> uline)) { + switch (f -> ttyp) { + case 'R': + if (f -> b.a.ack) { + V (1, ("Message spooled\n")); + ret = True; + break; + } else { + switch (f -> b.a.ec) { + default: + ptr = "unknown error"; + break; + case 1: + ptr = "checksum error"; + break; + case 2: + ptr = "syntax error"; + break; + case 3: + ptr = "operation not supported by system"; + break; + case 4: + ptr = "operation not allowed (at this point)"; + break; + case 5: + ptr = "call barring active"; + break; + case 6: + ptr = "AdC invalid"; + break; + case 7: + ptr = "authentication failure"; + break; + case 8: + ptr = "legitimisation code for all calls, failure"; + break; + case 9: + ptr = "GA not valid"; + break; + case 10: + ptr = "repetition not allowed"; + break; + case 11: + ptr = "legitimisation code for repetition, failure"; + break; + case 12: + ptr = "priority call not allowed"; + break; + case 13: + ptr = "legitimisation code for priority, failure"; + break; + case 14: + ptr = "urgent message not allowed"; + break; + case 15: + ptr = "legitimisation code for urgent message, failure"; + break; + case 16: + ptr = "reveerse charging not allowed"; + break; + case 17: + ptr = "legitimisation code for reverse charging, failure"; + break; + case 18: + ptr = "deferred delivery not allowed"; + break; + case 19: + ptr = "new AC not valid"; + break; + case 20: + ptr = "new legitimisation code not valid"; + break; + case 21: + ptr = "standard text not valid"; + break; + case 22: + ptr = "time period not valid"; + break; + case 23: + ptr = "message type not supported by system"; + break; + case 24: + ptr = "message too long"; + break; + case 25: + ptr = "requested standard text not valid"; + break; + case 26: + ptr = "message type not valid for pager type"; + break; + case 27: + ptr = "message not found in SMSC"; + break; + case 30: + ptr = "subscriber hang-up"; + break; + case 31: + ptr = "fax group not supported"; + break; + case 32: + ptr = "fax message type not supported"; + break; + case 33: + ptr = "address already in list (60 series)"; + break; + case 34: + ptr = "address not in list (60 series)"; + break; + case 35: + ptr = "list full (60 series)"; + break; + } + V (1, ("Invalid message (error %d) %s\n", f -> b.a.ec, (ptr ? ptr : ""))); + switch (f -> b.a.ec) { + case -1: + case 1: case 2: case 3: + case 6: case 7: case 8: + case 9: case 10: case 11: + case 12: case 13: case 14: + case 15: case 16: case 17: + case 18: case 19: case 20: + case 21: case 22: case 23: + case 24: case 25: case 26: + case 27: case 31: case 32: + case 33: case 34: case 35: + *err = ERR_FAIL; + break; + default: + *err = ERR_FATAL; + break; + } + } + break; + case 'O': + if (n && wds && (f -> ot == 53)) { + if (u -> logger) { + switch (f -> b.e.dst) { + default: ptr = NULL; break; + case 0: ptr = "delivered"; break; + case 1: ptr = "buffered"; break; + } + if (ptr) + (*u -> logger) (LG_PROTO, "UCP: message %s", ptr); + } + printf ("Message "); + switch (f -> b.e.dst) { + case 0: printf ("delivered"); break; + case 1: printf ("buffered"); break; + case 2: printf ("not delivered"); break; + default: + printf ("failed"); + break; + } + printf (" (%d)", f -> b.e.rsn); + switch (f -> b.e.rsn) { + default: + ptr = "unknown error"; + break; + case 0x01: + ptr = "successful delivered"; + break; + case 0x02: case 0x03: case 0x04: + case 0x05: case 0x06: case 0x07: + ptr = "temporary no service"; + break; + case 0x09: + ptr = "unknown service"; + break; + case 0x0a: + ptr = "network timeout"; + break; + case 0x32: + ptr = "storing time expired"; + break; + case 0x64: + ptr = "service not supported"; + break; + case 0x65: + ptr = "receiver unknown"; + break; + case 0x66: + ptr = "service not available"; + break; + case 0x67: + ptr = "call locked"; + break; + case 0x68: + ptr = "operation locked"; + break; + case 0x69: + ptr = "service center overrun"; + break; + case 0x6a: + ptr = "service not supported"; + break; + case 0x6b: + ptr = "receiver temporary not reachable"; + break; + case 0x6c: + ptr = "delivering error"; + break; + case 0x6d: + ptr = "receiver run out of memory"; + break; + case 0x6e: + ptr = "protocol error"; + break; + case 0x6f: + ptr = "receiver does not support service"; + break; + case 0x70: + ptr = "unknown serice center"; + break; + case 0x71: + ptr = "service center overrun"; + break; + case 0x72: + ptr = "illegal receiving device"; + break; + case 0x73: + ptr = "receiver no customer"; + break; + case 0x74: + ptr = "error in receiving device"; + break; + case 0x75: + ptr = "lower protocol not available"; + break; + case 0x76: + ptr = "system error"; + break; + case 0x77: + ptr = "PLMN system error"; + break; + case 0x78: + ptr = "HLR system error"; + break; + case 0x79: + ptr = "VLR system error"; + break; + case 0x7a: + ptr = "previous VLR system error"; + break; + case 0x7b: + ptr = "error on delivering (check receiver ID)"; + break; + case 0x7c: + ptr = "VMSC system error"; + break; + case 0x7d: + ptr = "EIR system error"; + break; + case 0x7e: + ptr = "system error"; + break; + case 0x7f: + ptr = "unexpected data"; + break; + case 0xc8: + ptr = "addressing error for service center"; + break; + case 0xc9: + ptr = "invalid absolute storing time"; + break; + case 0xca: + ptr = "message too large"; + break; + case 0xcb: + ptr = "GDM message cannot be extracted"; + break; + case 0xcc: + ptr = "translation into IA5 not possible"; + break; + case 0xcd: + ptr = "invalid format of storing time"; + break; + case 0xce: + ptr = "invalid receiver address"; + break; + case 0xcf: + ptr = "message sent twice"; + break; + case 0xd0: + ptr = "invalid message type"; + break; + } + if (ptr) + printf (" reason is %s", ptr); + if (f -> b.e.msg) + printf (": %s", schar (f -> b.e.msg)); + printf ("\n"); + } else { + if (! n) + --n; + interpret_originate (u, f); + } + if (ans = new_frame ('R', f -> ot)) { + ans -> trn = f -> trn; + an = & ans -> b.a; + an -> adc = u -> callid ? sextract (u -> callid) : NULL; + dat_localtime (& an -> scts); + switch (f -> ot) { + case 1: + an -> ack = False; + an -> ec = 23; + break; + case 31: + an -> ack = True; + dosend = True; + break; + case 51: + an -> ack = False; + an -> ec = 23; + break; + case 52: + an -> ack = True; + break; + case 53: + an -> ack = True; + break; + case 55: + an -> ack = False; + an -> ec = 23; + break; + case 56: + an -> ack = False; + an -> ec = 23; + break; + case 57: + an -> ack = True; + break; + case 58: + an -> ack = True; + break; + } + if (astr = assemble_frame (u, ans)) { + if (tty_send (u -> sp, astr -> str, astr -> len) != astr -> len) { + V (1, ("Unable to send answer\n")); + *err = ERR_FAIL; + } + sfree (astr); + } + free_frame (ans); + } + break; + } + free_frame (f); + if (*err != NO_ERR) + break; + } + } + return ret; +} +/*}}}*/ +/*{{{ login/logout/transmit */ +int +ucp_login (void *up, string_t *callid) +{ + ucp *u = (ucp *) up; + + MCHK (u); + if ((! u) || (! u -> sp)) + return ERR_ABORT; + u -> cnr = 0; + u -> callid = sfree (u -> callid); + if (callid && (! (u -> callid = snew (callid -> str, callid -> len)))) + return ERR_ABORT; + return NO_ERR; +} + +int +ucp_logout (void *up) +{ + MCHK ((ucp *) up); + return NO_ERR; +} + +int +ucp_transmit (void *up, string_t *pagerid, string_t *msg, Bool last) +{ + ucp *u = (ucp *) up; + int n, err; + Bool done; + string_t *smsg; + + MCHK (u); + if ((! u) || (! u -> sp)) + return ERR_FATAL; + err = NO_ERR; + tty_set_line_callback (u -> sp, grabline, "\x03", (void *) u); + for (n = 0; n < u -> send_retry; ++n) + if (smsg = convert_ucp (u, last, pagerid, msg)) { + done = send_msg (u, smsg, (u -> delay.day > 0 ? False : u -> rds), & err); + sfree (smsg); + if (done || (err != NO_ERR)) + break; + } + if ((n == u -> send_retry) || (err != NO_ERR)) { + if (err == NO_ERR) + err = ERR_FAIL; + V (1, ("Unable to send message\n")); + } + tty_set_line_callback (u -> sp, NULL, NULL, NULL); + if (u -> uline) { + free (u -> uline); + u -> uline = NULL; + } + return err; +} +/*}}}*/ +/*{{{ configuration */ +void +ucp_config (void *up, void (*logger) (char, char *, ...), + Bool xtend, int stout, int retry, int rtout, + date_t *delay, date_t *expire, Bool rds) +{ + ucp *u = (ucp *) up; + + MCHK (u); + if (u) { + u -> logger = logger; + u -> xtend = xtend; + if (u -> xtend) + u -> rds = rds; + else { + u -> rds = False; + dat_clear (& u -> delay); + dat_clear (& u -> expire); + } + if (stout >= 0) + u -> send_tout = stout; + if (retry >= 0) + u -> send_retry = retry; + if (rtout >= 0) + u -> rds_tout = rtout; + if (u -> xtend) { + if (delay) + u -> delay = *delay; + if (expire) + u -> expire = *expire; + } + } +} + +void +ucp_set_convtable (void *up, void *ctab) +{ + ucp *u = (ucp *) up; + + MCHK (u); + if (u) { + if (u -> ctab) + cv_free (u -> ctab); + u -> ctab = ctab; + } +} + +void +ucp_add_convtable (void *up, void *ctab) +{ + ucp *u = (ucp *) up; + + MCHK (u); + if (u) { + if (! u -> ctab) + u -> ctab = cv_new (); + if (u -> ctab) + cv_merge (u -> ctab, ctab, True); + } +} +/*}}}*/ +/*{{{ new/free/etc */ +void * +ucp_new (void *sp) +{ + ucp *u; + + if (u = (ucp *) malloc (sizeof (ucp))) { +# ifndef NDEBUG + u -> magic = MAGIC; +# endif /* NDEBUG */ + u -> sp = sp; + u -> ctab = NULL; + u -> logger = NULL; + u -> uline = NULL; + u -> callid = NULL; + u -> xtend = False; + u -> send_tout = 60; + u -> send_retry = 3; + u -> rds_tout = 40; + dat_clear (& u -> delay); + dat_clear (& u -> expire); + u -> rds = False; + u -> cnr = 0; + } + return (void *) u; +} + +void * +ucp_free (void *up) +{ + ucp *u = (ucp *) up; + + MCHK (u); + if (u) { + if (u -> ctab) + cv_free (u -> ctab); + if (u -> uline) + free (u -> uline); + if (u -> callid) + sfree (u -> callid); + free (u); + } + return NULL; +} + +int +ucp_preinit (void) +{ + return 0; +} + +void +ucp_postdeinit (void) +{ +} +/*}}}*/ diff --git a/util.c b/util.c new file mode 100644 index 0000000..c59abb8 --- /dev/null +++ b/util.c @@ -0,0 +1,596 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +# include +# include +# include +# include +# include +# include +# include "pager.h" + +/*{{{ utility functions */ +char * +skip (char *str) +{ + while (*str && (! isspace (*str))) + ++str; + if (*str) { + *str++ = '\0'; + while (isspace (*str)) + ++str; + } + return str; +} + +char * +skipch (char *str, char ch) +{ + while (*str && (*str != ch)) + ++str; + if (*str) { + *str++ = '\0'; + while (isspace (*str)) + ++str; + } + return str; +} + +char * +getline (FILE *fp, Bool cont) +{ + char *buf; + int size; + char *ret; + int len; + char *ptr; + + size = 256; + if (! (buf = malloc (size + 2))) + return NULL; + len = 0; + while (ret = fgets (buf + len, size - len - 2, fp)) { + if (ptr = strchr (buf + len, '\n')) { + *ptr = '\0'; + len += strlen (buf + len); + if (! cont) + break; + if (len && (buf[len - 1] == '\\')) { + --len; + buf[len] = '\0'; + } else + break; + } else + len += strlen (buf + len); + if (len + 64 >= size) { + size += size; + if (! (buf = Realloc (buf, size + 2))) + break; + } + } + if ((! ret) && buf) { + free (buf); + buf = NULL; + } + return len || ret ? buf : NULL; +} + +int +verbose_out (char *fmt, ...) +{ + va_list par; + + va_start (par, fmt); + vfprintf (stdout, fmt, par); + va_end (par); + return 0; +} +/*}}}*/ +/*{{{ string_t handling */ +string_t * +snewc (char *str) +{ + string_t *s; + + if (s = (string_t *) malloc (sizeof (string_t))) { + s -> str = NULL; + if (str) { + s -> len = strlen (str); + s -> size = s -> len + 1; + if (s -> str = (char_t *) malloc (sizeof (char_t) * s -> size)) + memcpy (s -> str, str, s -> len); + else { + free (s); + s = NULL; + } + } else { + s -> len = 0; + s -> size = 0; + } + } + return s; +} + +string_t * +snew (char_t *str, int len) +{ + string_t *s; + + if (s = (string_t *) malloc (sizeof (string_t))) { + s -> str = NULL; + s -> len = 0; + s -> size = 0; + if (len > 0) + if (s -> str = (char_t *) malloc (sizeof (char_t) * (len + 1))) { + s -> size = len + 1; + if (str) { + s -> len = len; + memcpy (s -> str, str, len); + } else + s -> len = 0; + } else { + free (s); + s = NULL; + } + } + return s; +} + +Bool +sexpand (string_t *s, int nsize) +{ + if (s && (nsize + 2 > s -> size)) { + s -> size = nsize + 2; + if (! (s -> str = (char_t *) Realloc (s -> str, sizeof (char_t) * s -> size))) { + s -> size = 0; + s -> len = 0; + return False; + } + } + return True; +} + +Bool +scopy (string_t *dst, string_t *src) +{ + if (dst && src && sexpand (dst, src -> len + 1)) { + if (src -> str) { + memcpy (dst -> str, src -> str, src -> len); + dst -> len = src -> len; + } else + dst -> len = 0; + return True; + } + return False; +} + +Bool +scat (string_t *dst, string_t *src) +{ + if (dst && src && sexpand (dst, dst -> len + src -> len + 1)) { + if (src -> str) { + memcpy (dst -> str + dst -> len, src -> str, src -> len); + dst -> len += src -> len; + } + return True; + } + return False; +} + +static Bool +dostr (string_t *dst, char *src, Bool (*func) (string_t *, string_t *)) +{ + Bool ret; + string_t *rsrc; + + ret = False; + if (dst) + if (src) { + if (rsrc = snewc (src)) { + ret = (*func) (dst, rsrc); + sfree (rsrc); + } + } else + ret = True; + return ret; +} + +Bool +scopyc (string_t *dst, char *src) +{ + return dostr (dst, src, scopy); +} + +Bool +scatc (string_t *dst, char *src) +{ + return dostr (dst, src, scat); +} + +string_t * +scut (string_t *str, int start, int len) +{ + string_t *res; + + if (len < 0) + len = str ? str -> len - start : 0; + if (res = snew (NULL, len + 1)) { + if (str -> len > start) { + if (str -> len - start < len) + len = str -> len - start; + } else + len = 0; + if (len > 0) + memcpy (res -> str, str -> str + start, len); + res -> len = len; + } + return res; +} + +void +sdel (string_t *str, int start, int len) +{ + int size; + + if (str -> len > start) { + if (str -> len - start < len) + len = str -> len - start; + } else + len = 0; + if (len > 0) { + size = str -> len - (start + len); + if (size > 0) + memcpy (str -> str + start, str -> str + start + len, str -> len - (start + len)); + str -> len -= len; + } +} + +Bool +sput (string_t *str, string_t *ins, int pos, int len) +{ + if ((len < 0) || (len > ins -> len)) + len = ins -> len; + if (len + pos >= str -> size) + if (! sexpand (str, len + pos + 1)) + return False; + memcpy (str -> str + pos, ins -> str, len); + if (str -> len < len + pos) + str -> len = len + pos; + return True; +} + +Bool +sputc (string_t *str, char *ins, int pos, int len) +{ + Bool ret; + string_t *rins; + + ret = False; + if (str && ins && (rins = snewc (ins))) { + ret = sput (str, rins, pos, len); + sfree (rins); + } + return ret; +} + +char * +sextract (string_t *s) +{ + char *ret; + + ret = NULL; + if (s) + if (ret = malloc (s -> len + 1)) + if (s -> str) { + memcpy (ret, s -> str, s -> len); + ret[s -> len] = '\0'; + } else + ret[0] = '\0'; + return ret; +} + +char * +schar (string_t *s) +{ + if (s) { + if (s -> len + 1>= s -> size) + sexpand (s, s -> len + 2); + if (s -> len + 1 < s -> size) { + s -> str[s -> len] = '\0'; + return (char *) s -> str; + } + } + return NULL; +} + +void * +sfree (string_t *s) +{ + if (s) { + if (s -> str) + free (s -> str); + free (s); + } + return NULL; +} + +void +srelease (string_t *s) +{ + if (s -> size + 1 > s -> len) { + s -> size = s -> len + 1; + if (! (s -> str = (char_t *) Realloc (s -> str, sizeof (char_t) * (s -> size + 2)))) { + s -> size = 0; + s -> len = 0; + } + } +} + +Bool +siscntrl (string_t *s, int pos) +{ + return (s && (pos >= 0) && (pos < s -> len) && iscntrl (s -> str[pos])) ? True : False; +} + +Bool +sisspace (string_t *s, int pos) +{ + return (s && (pos >= 0) && (pos < s -> len) && isspace (s -> str[pos])) ? True : False; +} + +Bool +sisdigit (string_t *s, int pos) +{ + return (s && (pos >= 0) && (pos < s -> len) && isdigit (s -> str[pos])) ? True : False; +} + +int +stoi (string_t *s) +{ + int ret; + + ret = 0; + if (s && s -> str) { + s -> str[s -> len] = '\0'; + ret = atoi ((char *) s -> str); + } + return ret; +} +/*}}}*/ +/*{{{ date_t handling */ +# if ! HAVE_MEMSET && ! HAVE_BZERO +static void +dozero (void *p, int len) +{ + unsigned char *ptr = (char *) p; + + while (len-- > 0) + *ptr++ = 0; +} +# endif /* ! HAVE_MEMSET && ! HAVE_BZERO */ + +date_t * +dat_free (date_t *d) +{ + if (d) + free (d); + return NULL; +} + +date_t * +dat_parse (char *str) +{ + date_t *d; + time_t tim; + struct tm *tt; + struct tm tm; + Bool add; + char *ptr, *sav; + char *p1, *p2; + int mode; + int n, val; + char sep; + date_t tmp; + + d = NULL; + if ((! str) || (str = strdup (str))) { + if (d = (date_t *) malloc (sizeof (date_t))) { + time (& tim); + if (tt = localtime (& tim)) { + d -> day = tt -> tm_mday; + d -> mon = tt -> tm_mon + 1; + d -> year = tt -> tm_year + 1900; + d -> hour = tt -> tm_hour; + d -> min = tt -> tm_min; + d -> sec = tt -> tm_sec; + } else { + d -> day = 1; + d -> mon = 1; + d -> year = 1970; + d -> hour = 0; + d -> min = 0; + d -> sec = 0; + } + if (str && strcmp (str, "now")) { + if (*str == '+') { + add = True; + ptr = str + 1; + } else { + add = False; + ptr = str; + } + tmp.day = -1; + tmp.mon = -1; + tmp.year = -1; + tmp.hour = -1; + tmp.min = -1; + tmp.sec = -1; + while (*ptr) { + sav = ptr; + ptr = skip (ptr); + if (strchr (sav, '.')) { + sep = '.'; + mode = 1; + } else if (strchr (sav, '/')) { + sep = '/'; + mode = 2; + } else if (strchr (sav, ':')) { + sep = ':'; + mode = 3; + } else { + sep = '\0'; + mode = 0; + } + for (p1 = sav, n = 0; *p1; ++n) { + p2 = p1; + p1 = skipch (p1, sep); + val = atoi (p2); + switch (mode) { + case 0: + tmp.sec = 0; + if (val < 30) { + tmp.hour = val; + tmp.min = 0; + } else { + tmp.hour = val / 60; + tmp.min = val % 60; + } + break; + case 1: + if (n == 0) + tmp.day = val; + else if (n == 1) + tmp.mon = val; + else if (n == 2) { + if ((! add) && (val < 1900)) + val += 1900; + tmp.year = val; + } + break; + case 2: + if (n == 0) + tmp.mon = val; + else if (n == 1) + tmp.day = val; + else if (n == 2) { + if ((! add) && (val < 1900)) + val += 1900; + tmp.year = val; + } + break; + case 3: + if (n == 0) { + tmp.hour = val; + tmp.min = 0; + tmp.sec = 0; + } else if (n == 1) + tmp.min = val; + else if (n == 2) + tmp.sec = val; + break; + } + } + } + if (add) { + if (tmp.day != -1) + d -> day += tmp.day; + if (tmp.mon != -1) + d -> mon += tmp.mon; + if (tmp.year != -1) + d -> year += tmp.year; + if (tmp.hour != -1) + d -> hour += tmp.hour; + if (tmp.min != -1) + d -> min += tmp.min; + if (tmp.sec != -1) + d -> sec += tmp.sec; + } else { + if (tmp.day != -1) + d -> day = tmp.day; + if (tmp.mon != -1) + d -> mon = tmp.mon; + if (tmp.year != -1) + d -> year = tmp.year; + if (tmp.hour != -1) + d -> hour = tmp.hour; + if (tmp.min != -1) + d -> min = tmp.min; + if (tmp.sec != -1) + d -> sec = tmp.sec; + } +# if HAVE_MEMSET + memset (& tm, 0, sizeof (tm)); +# elif HAVE_BZERO + bzero (& tm, sizeof (tm)); +# else + dozero (& tm, sizeof (tm)); +# endif /* ! HAVE_MEMSET && ! HAVE_BZERO */ + tm.tm_mday = d -> day; + tm.tm_mon = d -> mon - 1; + tm.tm_year = d -> year - 1900; + tm.tm_hour = d -> hour; + tm.tm_min = d -> min; + tm.tm_sec = d -> sec; + tm.tm_isdst = tt -> tm_isdst; + if (mktime (& tm) == (time_t) -1) + d = dat_free (d); + else { + d -> day = tm.tm_mday; + d -> mon = tm.tm_mon + 1; + d -> year = tm.tm_year + 1900; + d -> hour = tm.tm_hour; + d -> min = tm.tm_min; + d -> sec = tm.tm_sec; + } + } + } + if (str) + free (str); + } + return d; +} + +int +dat_diff (date_t *d1, date_t *d2) +{ + int v1, v2; + + if (d1) + v1 = (((((d1 -> year * 12 + d1 -> mon) * 31 + d1 -> day) * 24 + d1 -> hour) * 60 + d1 -> min) * 60 + d1 -> sec); + else + v1 = 0; + if (d2) + v2 = (((((d2 -> year * 12 + d2 -> mon) * 31 + d2 -> day) * 24 + d2 -> hour) * 60 + d2 -> min) * 60 + d2 -> sec); + else + v2 = 0; + return v2 - v1; +} + +void +dat_clear (date_t *d) +{ + d -> day = 0; + d -> mon = 0; + d -> year = 0; + d -> hour = 0; + d -> min = 0; + d -> sec = 0; +} + +void +dat_localtime (date_t *d) +{ + time_t tim; + struct tm *tt; + + time (& tim); + if (tt = localtime (& tim)) { + d -> day = tt -> tm_mday; + d -> mon = tt -> tm_mon + 1; + d -> year = tt -> tm_year + 1900; + d -> hour = tt -> tm_hour; + d -> min = tt -> tm_min; + d -> sec = tt -> tm_sec; + } else + dat_clear (d); +} +/*}}}*/ diff --git a/valid.c b/valid.c new file mode 100644 index 0000000..a3212e1 --- /dev/null +++ b/valid.c @@ -0,0 +1,126 @@ +/* -*- mode: c; mode: fold -*- */ +# include "config.h" +/*# undef HAVE_REGEX_H*/ +# include +# include +# include "pager.h" +# include "valid.h" + +valid_t * +v_new (char *pattern) +{ + valid_t *v; +# if ! HAVE_REGEX_H + int cnt, siz; + char *ptr, *sav; + int n; +# endif /* ! HAVE_REGEX_H */ + + if (v = (valid_t *) malloc (sizeof (valid_t))) { +# if HAVE_REGEX_H + if (regcomp (& v -> r, pattern, REG_EXTENDED)) { + free (v); + v = NULL; + } +# else /* HAVE_REGEX_H */ + v -> pats = NULL; + v -> lens = NULL; + v -> malways = 0; + if (! strcmp (pattern, "-")) + v -> malways = 1; + else if (pattern = strdup (pattern)) { + cnt = 0; + siz = 0; + for (ptr = pattern; ptr; ) { + sav = ptr; + if (ptr = strchr (ptr, '|')) + *ptr++ = '\0'; + if (cnt >= siz) { + siz += 4; + if (! (v -> pats = Realloc (v -> pats, (siz + 1) * sizeof (char *)))) + break; + } + if (! (v -> pats[cnt] = strdup (sav))) { + while (--cnt >= 0) + free (v -> pats[cnt]); + free (v -> pats); + v -> pats = NULL; + break; + } + ++cnt; + } + free (pattern); + if (v -> pats) { + v -> pats[cnt] = NULL; + if (! (v -> lens = (int *) malloc ((cnt + 1) * sizeof (int)))) { + for (n = 0; n < cnt; ++n) + free (v -> pats[n]); + free (v -> pats); + v -> pats = NULL; + } else + for (n = 0; n < cnt; ++n) + v -> lens[n] = strlen (v -> pats[n]); + } + if (! v -> pats) { + free (v); + v = NULL; + } + } +# endif /* HAVE_REGEX_H */ + } + return v; +} + +Bool +v_alidate (valid_t *v, char *str, int *start, int *end) +{ + Bool ret = False; +# if HAVE_REGEX_H + regmatch_t mt; + + if (! regexec (& v -> r, str, 1, & mt, 0)) { + *start = mt.rm_so; + *end = mt.rm_eo; + ret = True; + } +# else /* HAVE_REGEX_H */ + int n; + + if (v -> pats) { + for (n = 0; v -> pats[n]; ++n) + if (! strncmp (v -> pats[n], str, v -> lens[n])) + break; + if (v -> pats[n]) { + *start = 0; + *end = v -> lens[n]; + ret = True; + } + } else if (v -> malways) { + *start = 0; + *end = 0; + ret = True; + } +# endif /* HAVE_REGEX_H */ + return ret; +} + +void +v_free (valid_t *v) +{ +# if HAVE_REGEX_H + regfree (& v -> r); +# else /* HAVE_REGEX_H */ + int n; + + if (v -> pats) { + for (n = 0; v -> pats[n]; ++n) + free (v -> pats[n]); + free (v -> pats); + } + if (v -> lens) { + free (v -> lens); + v -> lens = NULL; + } +# endif /* HAVE_REGEX_H */ + free (v); +} diff --git a/valid.h b/valid.h new file mode 100644 index 0000000..b3772a9 --- /dev/null +++ b/valid.h @@ -0,0 +1,21 @@ +/* -*- mode: c; mode: fold -*- */ +# ifndef __VALID_H +# define __VALID_H 1 +# if HAVE_REGEX_H +# include +# endif /* HAVE_REGEX_H */ + +typedef struct { +# if HAVE_REGEX_H + regex_t r; +# else /* HAVE_REGEX_H */ + char **pats; + int *lens; + int malways; +# endif /* HAVE_REGEX_H */ +} valid_t; + +extern valid_t *v_new (char *pattern); +extern Bool v_alidate (valid_t *v, char *str, int *start, int *end); +extern void v_free (valid_t *v); +# endif /* __VALID_H */ diff --git a/yaps.1 b/yaps.1 new file mode 100644 index 0000000..d45ea70 --- /dev/null +++ b/yaps.1 @@ -0,0 +1,19 @@ +.TH YAPS 1 "V 0.92 21. May 1997" +.SH NAME +yaps \- Yet Another Pager Software +.SH SYNOPSIS +yaps [opts] [parameter] +.SH DESCRIPTION +Sorry, this manual page is currently more or less empty. See the user +documentation +.B yaps.html +or +.BR yaps.doc . +.SH AUTHOR +Written and copyrighted by Ulrich Dessauer, Germering, Germany. See file +.B COPYRIGHT +and +.B COPYING.GPL +for details on usage and warrenty. +.SH BUGS +Perhaps a lot as this is considered as alpha software. diff --git a/yaps.c b/yaps.c new file mode 100644 index 0000000..2f9b7a9 --- /dev/null +++ b/yaps.c @@ -0,0 +1,2489 @@ +/* -*- mode: c; mode: fold -*- */ +/*{{{ includes */ +# include "config.h" +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# if HAVE_LOCALE_H +# include +# endif /* HAVE_LOCALE_H */ +# if HAVE_GETOPT_H +# include +# endif /* HAVE_GETOPT_H */ +# include "pager.h" +# include "valid.h" +/*}}}*/ +/*{{{ general definitions */ +# define VERSION "0.96 (alpha software)" + +# ifndef LIBDIR +# define LIBDIR NULL +# endif /* LIBDIR */ + +# if HAVE_SIGSETJMP +# undef jmp_buf +# define jmp_buf sigjmp_buf +# undef setjmp +# define setjmp(xx) sigsetjmp ((xx), 1) +# undef longjmp +# define longjmp(xx,vv) siglongjmp ((xx), (vv)) +# endif /* HAVE_SIGSETJMP */ + +# define SPLIT_TERM " ..." +# define SPLIT_INIT "... " + +# define OOM fprintf (stderr, "Out of memory in line %d, aborted\n", __LINE__) +/*}}}*/ +/*{{{ configuration file entry names and default values */ +# define CONFIG_SEP "," + +# define SEC_ALIAS "alias" + +# define CFG_SERVICES "services" +# define CFG_CALLID "call-id" +# define CFG_SIG "signature" +# define CFG_USESIG "use-signature" +# define CFG_VERBOSE "verbose" +# define CFG_LOGFILE "logfile" +# define CFG_LOGSTR "logstring" +# define CFG_MODEMS "modems" +# define CFG_FREPORT "final-report" +# define CFG_SPEED "speed" +# define CFG_BPB "bits-per-byte" +# define CFG_PARITY "parity" +# define CFG_SB "stopbits" +# define CFG_PHONE "phone" +# define CFG_PROTOCOL "protocol" +# define CFG_MAXSIZE "max-size" +# define CFG_MAYSPLIT "may-split" +# define CFG_MAXMSGS "max-messages" +# define CFG_TRUNCATE "truncate" +# define CFG_USECID "use-call-id" +# define CFG_RMCID "rm-invalids-cid,remove-invalid-character-call-id" +# define CFG_RMPID "rm-invalids-pid,remove-invalid-character-pager-id" +# define CFG_INSPID "insert-pager-id" +# define CFG_CHPID "change-pid,change-pager-id" +# define CFG_VALPID "valid-pid,valid-pager-id" +# define CFG_CHCID "change-cid,change-call-id" +# define CFG_VALCID "valid-cid,valid-call-id" +# define CFG_CONVTAB "conv-table" +# define CFG_CONVERT "convert" +# define CFG_COST "cost" +# define CFG_FORCE "force" +# define CFG_CANDELAY "can-delay" +# define CFG_CANEXP "can-expire" +# define CFG_CANRDS "can-rds,can-request-delivery-status" +# define CFG_DORDS "rds,request-delivery-status" +# define CFG_CHKCID "check-call-id" +# define CFG_CHKPID "check-pager-id" +# define CFG_DEVICE "device" +# define CFG_LCKPRE "lock-prefix" +# define CFG_LCKMETHOD "lock-method" +# define CFG_INIT "init" +# define CFG_LINIT "local-init" +# define CFG_DIAL "dial" +# define CFG_TIMEOUT "timeout" +# define CFG_RESET "reset" + +# define DEF_SPEED 38400 +# define DEF_BPB 8 +# define DEF_PARITY "n" +# define DEF_SB 1 +# define DEF_USECID False +# define DEF_INSPID False +# define DEF_CONVTAB NULL +# define DEF_CONVERT NULL +# define DEF_DEVICE "/dev/modem" +# define DEF_LCKPRE "/usr/spool/uucp/LCK.." +# define DEF_LCKMETHOD NULL +# define DEF_INIT "\\r !d ATZ\\r tm_mday, tt -> tm_mon + 1, tt -> tm_year + 1900, + tt -> tm_hour, tt -> tm_min, tt -> tm_sec, typ); + vfprintf (fp, fmt, par); + fprintf (fp, "\n"); + fclose (fp); + } + umask (omask); + va_end (par); +} +/*}}}*/ +/*{{{ dialing */ +static char * +mkrealphone (char *phone, char *pagerid) +{ + char *dst; + int siz, n, len; + char *ptr; + + siz = strlen (phone) + 16; + if (dst = malloc (siz + 2)) { + for (n = 0; *phone; ) + if (*phone == '%') { + ++phone; + if (*phone) { + ptr = NULL; + switch (*phone) { + case 'P': + ptr = pagerid; + break; + } + if (ptr) { + len = strlen (ptr); + if (n + len >= siz) { + siz = n + len + 64; + if (! (dst = Realloc (dst, siz + 2))) + break; + } + strcpy (dst + n, ptr); + n += len; + } + ++phone; + } + } else { + if (n >= siz) { + siz += 32; + if (! (dst = Realloc (dst, siz + 2))) + break; + } + dst[n++] = *phone++; + } + if (dst) + dst[n] = '\0'; + } + return dst; +} + +static void * +do_dial (void *cfg, char *service, char *pagerid, char **modem) +{ + void *sp; + char *tmp, *sav, *ptr; + char *t2, *s2, *p2; + char *device, *lckpre, *lckmethod; + int speed, rspeed, bpb, parity, sb; + int tout; + char *minit, *linit, *dial, *reset, *phone; + int siz, len; + int n, m; + + sp = NULL; + *modem = NULL; + if ((! (tmp = cfg_get (cfg, service, CFG_MODEMS, NULL))) || + (! (tmp = strdup (tmp)))) + return NULL; + speed = cfg_iget (cfg, service, CFG_SPEED, DEF_SPEED); + bpb = cfg_iget (cfg, service, CFG_BPB, DEF_BPB); + if (ptr = cfg_get (cfg, service, CFG_PARITY, DEF_PARITY)) + parity = *ptr; + else + parity = '\0'; + sb = cfg_iget (cfg, service, CFG_SB, DEF_SB); + dial = NULL; + reset = NULL; + tout = 0; + for (ptr = tmp; *ptr; ) { + sav = ptr; + ptr = skipch (ptr, ','); + device = cfg_get (cfg, sav, CFG_DEVICE, DEF_DEVICE); + lckpre = cfg_get (cfg, sav, CFG_LCKPRE, DEF_LCKPRE); + lckmethod = cfg_get (cfg, sav, CFG_LCKMETHOD, DEF_LCKMETHOD); + minit = cfg_get (cfg, sav, CFG_INIT, DEF_INIT); + linit = cfg_get (cfg, sav, CFG_LINIT, DEF_LINIT); + dial = cfg_get (cfg, sav, CFG_DIAL, DEF_DIAL); + reset = cfg_get (cfg, sav, CFG_RESET, NULL); + tout = cfg_iget (cfg, sav, CFG_TIMEOUT, DEF_TIMEOUT); + rspeed = cfg_block_iget (cfg, sav, CFG_SPEED, speed); + if (device && (t2 = strdup (device))) { + for (p2 = t2; *p2; ) { + s2 = p2; + p2 = skipch (p2, ','); + V (3, ("Trying to open %s for modem %s\n", s2, sav)); + if (sp = tty_open (s2, lckpre, lckmethod)) { + if (tty_setup (sp, True, True, rspeed, bpb, sb, parity) != -1) { + if (! (n = setjmp (env))) { + dojump = True; + tty_hangup (sp, 500); + if (((! minit) || (tty_send_expect (sp, tout, minit, NULL) != -1)) && + ((! linit) || (tty_send_expect (sp, tout, linit, NULL) != -1))) { + dojump = False; + V (3, ("Using modem %s at %d bps, %d%c%d over %s\n", sav, rspeed, bpb, parity, sb, s2)); + *modem = strdup (sav); + break; + } + } else { + V (2, ("\n")); + if ((n == SIGTERM) || (n == SIGQUIT)) + longjmp (termenv, n); + } + dojump = False; + } + sp = tty_close (sp); + } + } + free (t2); + } + if (sp) + break; + } + free (tmp); + if (sp && dial && (phone = cfg_get (cfg, service, CFG_PHONE, NULL)) && + (phone = mkrealphone (phone, pagerid))) { + if (! (n = setjmp (env))) { + dojump = True; + for (p2 = phone; *p2; ) { + s2 = p2; + p2 = skipch (p2, ','); + siz = strlen (dial) + strlen (phone) + 64; + if (tmp = malloc (siz + 2)) { + for (n = 0, m = 0; dial[n]; ) { + if (dial[n] == '%') { + ++n; + if (dial[n]) { + ptr = NULL; + switch (dial[n]) { + case 'L': + ptr = phone; + break; + } + if (ptr) { + len = strlen (ptr); + if (m + len >= siz) { + siz += len + 64; + if (! (tmp = Realloc (tmp, siz + 2))) + break; + } + strcpy (tmp + m, ptr); + m += len; + } + ++n; + } + } else { + if (m + 1 >= siz) { + siz += 128; + if (! (tmp = Realloc (tmp, siz + 2))) + break; + } + switch (dial[n]) { + case '-': + tmp[m++] = ','; + break; + default: + tmp[m++] = dial[n]; + break; + } + ++n; + } + } + if (tmp) { + tmp[m] = '\0'; + V (3, ("Trying do dial %s\n", s2)); + if (tty_send_expect (sp, tout, tmp, NULL) != -1) { + V (1, ("Dial successful\n")); + tty_drain (sp, 1); + while (*p2) + ++p2; + } else { + if (*p2) + tty_hangup (sp, 500); + else + sp = tty_close (sp); + } + free (tmp); + } + } + } + } else { + V (2, ("\n")); + if (sp) { + tty_send (sp, "x\r", 2); + tty_hangup (sp, 500); + if (reset) + tty_send_expect (sp, tout, reset, NULL); + sp = tty_close (sp); + } + if ((n == SIGTERM) || (n == SIGQUIT)) + longjmp (termenv, n); + } + dojump = False; + free (phone); + } + return sp; +} +/*}}}*/ +/*{{{ send ASCII */ +static int +asc_send (void *sp, void *ctab, string_t *callid, message *mg, int mcnt, void *cfg, char *service, date_t *delay, date_t *expire, Bool rds) +{ + int ret; + void *ap; + int n; + int err, nerr; + int atout; + char *alin, *alout, *apid, *amsg, *anext, *async; + char *tmp; + + ret = 0; + if (ap = asc_new (sp)) { + atout = cfg_iget (cfg, service, CFG_ATOUT, DEF_ATOUT); + alin = cfg_get (cfg, service, CFG_ALOGIN, DEF_ALOGIN); + alout = cfg_get (cfg, service, CFG_ALOGOUT, DEF_ALOGOUT); + apid = cfg_get (cfg, service, CFG_APID, DEF_APID); + amsg = cfg_get (cfg, service, CFG_AMSG, DEF_AMSG); + anext = cfg_get (cfg, service, CFG_ANEXT, DEF_ANEXT); + async = cfg_get (cfg, service, CFG_ASYNC, DEF_ASYNC); + asc_config (ap, do_log, atout, alin, alout, apid, amsg, anext, async, delay, expire, rds); + if (ctab) { + asc_add_convtable (ap, ctab); + ctab = cv_free (ctab); + } + if ((err = asc_login (ap, callid)) == NO_ERR) { + for (n = 0; n < mcnt; ++n) { + tmp = schar (mg[n].msg); + if (tmp) + err = asc_transmit (ap, mg[n].pid, tmp); + else + err = ERR_FATAL; + if (err == NO_ERR) + mg[n].st.success = True; + else + mg[n].st.reason = CantSend; + do_log ((err == NO_ERR ? LGS_SENT : LGF_SENT), + "ASCII Message %ssent to %s via %s: %s", (err == NO_ERR ? "" : "NOT "), + (mg[n].alias ? mg[n].alias : (mg[n].pid ? mg[n].pid : "")), service, (tmp ? tmp : "")); + if (ESTOP (err)) + break; + if (n + 1 < mcnt) { + if (err == NO_ERR) + err = asc_next (ap); + else + err = asc_sync (ap); + if (ESTOP (err)) + break; + else if (err != NO_ERR) + if ((nerr = asc_sync (ap)) != NO_ERR) { + if (nerr < err) + err = nerr; + break; + } + } + } + if (n < mcnt) + ret = 1; + if ((err != ERR_ABORT) && (asc_logout (ap) != NO_ERR)) + ret = 1; + } else + ret = 1; + asc_free (ap); + } else + ret = 1; + return ret; +} +/*}}}*/ +/*{{{ send script */ +static int +scr_send (void *sp, void *ctab, string_t *callid, message *mg, int mcnt, void *cfg, char *service, date_t *delay, date_t *expire, Bool rds) +{ + int ret; + void *s; + char *typ; + char *name; + char *scr; + char *slogin, *slogout, *spid, *smsg, *snext, *ssync; + char *tmp; + int n, err, nerr; + + ret = 1; + typ = cfg_get (cfg, service, CFG_STYP, DEF_STYP); + if (s = scr_new (sp, typ, LIBDIR)) { + scr_config (s, do_log, delay, expire, rds); + name = cfg_get (cfg, service, CFG_SNAME, DEF_SNAME); + if (name) + if ((*name == '/') || (*name == '+')) { + if (*name == '+') + ++name; + if (scr_load_file (s, name) == NO_ERR) + ret = 0; + } else if (scr = cfg_get (cfg, service, name, NULL)) + if (scr_load_string (s, scr) == NO_ERR) + ret = 0; + if (! ret) { + slogin = cfg_get (cfg, service, CFG_SLOGIN, DEF_SLOGIN); + slogout = cfg_get (cfg, service, CFG_SLOGOUT, DEF_SLOGOUT); + spid = cfg_get (cfg, service, CFG_SPID, DEF_SPID); + smsg = cfg_get (cfg, service, CFG_SMSG, DEF_SMSG); + snext = cfg_get (cfg, service, CFG_SNEXT, DEF_SNEXT); + ssync = cfg_get (cfg, service, CFG_SSYNC, DEF_SSYNC); + if (ctab) { + scr_add_convtable (s, ctab); + ctab = cv_free (ctab); + } + if (scr_execute (s, slogin, schar (callid)) == NO_ERR) { + err = NO_ERR; + for (n = 0; n < mcnt; ++n) { + err = scr_execute (s, spid, mg[n].pid); + do_log ((err == NO_ERR ? LGS_INF : LGF_INF), + "SCRIPT pagerid %s %ssent via %s", + (mg[n].alias ? mg[n].alias : (mg[n].pid ? mg[n].pid : "")), + (err == NO_ERR ? "" : "NOT "), service); + if (ESTOP (err)) + break; + else if (err == NO_ERR) { + tmp = schar (mg[n].msg); + if (tmp) + err = scr_execute (s, smsg, tmp); + else + err = ERR_FATAL; + if (err == NO_ERR) + mg[n].st.success = True; + else + mg[n].st.reason = CantSend; + do_log ((err == NO_ERR ? LGS_INF : LGF_INF), + "SCRIPT message %s %ssent via %s", (tmp ? tmp : ""), (err == NO_ERR ? "" : "NOT "), service); + do_log ((err == NO_ERR ? LGS_SENT : LGF_SENT), + "SCRIPT Message %ssent to %s via %s: %s", (err == NO_ERR ? "" : "NOT "), + (mg[n].alias ? mg[n].alias : (mg[n].pid ? mg[n].pid : "")), service, (tmp ? tmp : "")); + if (ESTOP (err)) + break; + } + if (n + 1 < mcnt) { + if (err == NO_ERR) + err = scr_execute (s, snext, NULL); + else + err = scr_execute (s, ssync, NULL); + if (ESTOP (err)) + break; + else if (err != NO_ERR) + if ((nerr = scr_execute (s, ssync, NULL)) != NO_ERR) { + if (nerr < err) + err = nerr; + break; + } + } + } + if (n < mcnt) + ret = 1; + if ((err != ERR_ABORT) && (scr_execute (s, slogout, NULL) != NO_ERR)) + ret = 1; + } else + ret = 1; + } + scr_free (s); + } + return ret; +} +/*}}}*/ +/*{{{ send TAP */ +static int +tap_send (void *sp, void *ctab, string_t *callid, message *mg, int mcnt, void *cfg, char *service, date_t *delay, date_t *expire, Bool rds) +{ + void *tp; + string_t *field[4]; + int fld; + int n; + char *tmp; + int err, nerr; + + if (! (tp = tap_new (sp))) + return 1; + tap_timeouts (tp, + cfg_iget (cfg, service, CFG_T1, DEF_T1), + cfg_iget (cfg, service, CFG_T2, DEF_T2), + cfg_iget (cfg, service, CFG_T3, DEF_T3), + cfg_iget (cfg, service, CFG_T4, DEF_T4), + cfg_iget (cfg, service, CFG_T5, DEF_T5)); + tap_retries (tp, + cfg_iget (cfg, service, CFG_N1, DEF_N1), + cfg_iget (cfg, service, CFG_N2, DEF_N2), + cfg_iget (cfg, service, CFG_N3, DEF_N3), + cfg_iget (cfg, service, CFG_TLICNT, DEF_TLICNT), + cfg_iget (cfg, service, CFG_TLOCNT, DEF_TLOCNT)); + tap_config (tp, do_log, cfg_bget (cfg, service, CFG_TOLD, DEF_TOLD)); + if (ctab) { + tap_add_convtable (tp, ctab); + ctab = cv_free (ctab); + } + if (tap_login (tp, NULL, '\0', NULL, callid) != NO_ERR) { + tap_free (tp); + return 1; + } + err = NO_ERR; + for (n = 0; n < mcnt; ++n) { + fld = 0; + field[fld++] = snewc (mg[n].pid); + field[fld++] = snew (mg[n].msg -> str, mg[n].msg -> len); + field[fld] = NULL; + nerr = tap_transmit (tp, field, (n + 1 < mcnt ? False : True)); + if (nerr == NO_ERR) + mg[n].st.success = True; + else + mg[n].st.reason = CantSend; + tmp = schar (mg[n].msg); + do_log ((nerr == NO_ERR ? LGS_SENT : LGF_SENT), + "TAP Message %ssent to %s via %s: %s", (nerr == NO_ERR ? "" : "NOT "), + (mg[n].alias ? mg[n].alias : (mg[n].pid ? mg[n].pid : "")), service, + (tmp ? tmp : "")); + if (nerr != NO_ERR) + err = nerr; + for (fld = 0; field[fld]; ++fld) + sfree (field[fld]); + if (ESTOP (err)) + break; + } + if ((err != ERR_ABORT) && ((n = tap_logout (tp)) != NO_ERR)) + if (err == NO_ERR) + err = n; + tap_free (tp); + return (err != NO_ERR) ? 1 : 0; +} +/*}}}*/ +/*{{{ send UCP */ +static int +ucp_send (void *sp, void *ctab, string_t *callid, message *mg, int mcnt, void *cfg, char *service, date_t *delay, date_t *expire, Bool rds) +{ + void *up; + int n; + int err, nerr; + string_t *pid; + char *tmp; + + if (! (up = ucp_new (sp))) + return 1; + err = ERR_FATAL; + if (ctab) { + ucp_add_convtable (up, ctab); + ctab = cv_free (ctab); + } + ucp_config (up, do_log, + cfg_bget (cfg, service, CFG_UXTEND, DEF_UXTEND), + cfg_iget (cfg, service, CFG_USTOUT, DEF_USTOUT), + cfg_iget (cfg, service, CFG_URETRY, DEF_URETRY), + cfg_iget (cfg, service, CFG_URTOUT, DEF_URTOUT), + delay, expire, rds); + if ((err = ucp_login (up, callid)) == NO_ERR) { + for (n = 0; n < mcnt; ++n) { + pid = snewc (mg[n].pid); + if (pid) { + nerr = ucp_transmit (up, pid, mg[n].msg, (n + 1 < mcnt ? False : True)); + if (nerr == NO_ERR) + mg[n].st.success = True; + else + mg[n].st.reason = CantSend; + } else + nerr = ERR_FATAL; + sfree (pid); + tmp = schar (mg[n].msg); + do_log ((nerr == NO_ERR ? LGS_SENT : LGF_SENT), + "UCP Message %ssent to %s via %s: %s", (nerr == NO_ERR ? "" : "NOT "), + (mg[n].alias ? mg[n].alias : (mg[n].pid ? mg[n].pid : "")), service, + (tmp ? tmp : "")); + if (nerr != NO_ERR) + err = nerr; + if (ESTOP (err)) + break; + } + if ((err != ERR_ABORT) && ((n = ucp_logout (up)) != NO_ERR)) + if (err == NO_ERR) + err = n; + } + ucp_free (up); + return (err != NO_ERR) ? 1 : 0; +} +/*}}}*/ +/*{{{ support routines */ +static Protocol +getproto (char *str) +{ + if (str) + if (! strcmp (str, "ascii")) + return Ascii; + else if (! strcmp (str, "script")) + return Script; + else if (! strcmp (str, "tap")) + return Tap; + else if (! strcmp (str, "ucp")) + return Ucp; + return Unknown; +} + +static string_t * +readfile (char *fname) +{ + FILE *fp; + string_t *str; + int size; + struct stat st; + + str = NULL; + if (fp = fopen (fname, "r")) { + size = 0; + if (fstat (fileno (fp), & st) != -1) + size = st.st_size; + if ((size > 0) && (str = snew (NULL, size + 1))) + if (fread (str -> str, sizeof (char), size, fp) == size) + str -> len = size; + else + str = sfree (str); + fclose (fp); + } + return str; +} + +static string_t * +read_stdin (void) +{ + string_t *str; + int ch; + + if (str = snew (NULL, 256)) + while ((ch = getchar ()) != EOF) { + if (str -> len >= str -> size) + sexpand (str, str -> size + 64); + str -> str[str -> len++] = (char_t) ch; + } + return str; +} + +static void +extend_convtable (void *ctab, void *cfg, char *service) +{ + char *str; + char *cv, *cvs; + char *ptr, *sav; + char *src, *dst; + int n; + char *defconv[] = { + "no-control", + "control", + "no-8bit", + "8bit", + "numeric", + NULL + }; + + if ((str = cfg_get (cfg, service, CFG_CONVERT, DEF_CONVERT)) && + (str = strdup (str))) { + for (cv = str; *cv; ) { + cvs = cv; + cv = skipch (cv, ','); + if (*cvs == '*') { + ++cvs; + for (n = 0; defconv[n]; ++n) + if (! strcmp (cvs, defconv[n])) + break; + switch (n) { + case 0: /* no-control */ + for (n = 0; n < 32; ++n) { + cv_invalid (ctab, (char_t) n); + cv_invalid (ctab, (char_t) (n | 0x80)); + } + cv_invalid (ctab, (char_t) '\x7f'); + break; + case 1: /* control */ + for (n = 0; n < 32; ++n) { + cv_undefine (ctab, (char_t) n); + cv_undefine (ctab, (char_t) (n | 0x80)); + } + cv_undefine (ctab, (char_t) '\x7f'); + break; + case 2: /* no-8bit */ + for (n = 128; n < 256; ++n) + cv_invalid (ctab, (char_t) n); + break; + case 3: /* 8bit */ + for (n = 128; n < 256; ++n) + cv_undefine (ctab, (char_t) n); + break; + case 4: /* numeric */ + for (n = 0; n < 256; ++n) + if (n && strchr ("0123456789", (char) n)) + cv_undefine (ctab, (char_t) n); + else + cv_invalid (ctab, (char_t) n); + break; + } + } else if ((cvs = cfg_get (cfg, service, cvs, NULL)) && + (cvs = strdup (cvs))) { + for (ptr = cvs; ptr; ) { + sav = ptr; + if (ptr = strchr (ptr, '\n')) + *ptr++ = '\0'; + for (src = sav; isspace (*src); ++src) + ; + if (*src && (*src != '#')) { + dst = skip (src); + if (*src && *dst) + cv_sdefine (ctab, src, dst); + } + } + free (cvs); + } + } + free (str); + } +} + +static int +check (char *str, char *pat) +{ + Bool done, incs; + char *ptr, *sav, *val; + int slen; + int n, m, chk; + char *p; + char *chtab[] = { + "type", + "length", + "minimum", + "maximum", + NULL + }, *tytab[] = { + "numeric", + "sedecimal", + "lower", + "upper", + "alpha", + "alphanumeric", + "print", + "ascii", + NULL + }; + + if (! pat) + return 0; + done = False; + if (*pat == '+') { + ++pat; + if (pat = strdup (pat)) { + done = True; + slen = strlen (str); + for (ptr = pat; *ptr && done; ) { + sav = ptr; + ptr = skipch (ptr, ','); + val = skipch (sav, '='); + for (n = 0; chtab[n]; ++n) + if (! strcmp (sav, chtab[n])) + break; + switch (n) { + case 0: /* type */ + for (m = 0; tytab[m]; ++m) + if (! strcmp (val, tytab[m])) + break; + for (p = str; *p; ++p) { + switch (m) { + default: + chk = 1; + break; + case 0: /* numeric */ + chk = isdigit (*p); + break; + case 1: /* sedecimal */ + chk = isxdigit (*p); + break; + case 2: /* lower */ + chk = islower (*p); + break; + case 3: /* upper */ + chk = isupper (*p); + break; + case 4: /* alpha */ + chk = isalpha (*p); + break; + case 5: /* alphanumeric */ + chk = isalnum (*p); + break; + case 6: /* print */ + chk = isprint (*p); + break; + case 7: /* ascii */ + chk = isascii (*p); + break; + } + if (! chk) + return -1; + } + break; + case 1: /* length */ + if (slen != atoi (val)) + return -1; + break; + case 2: /* minimum */ + if (slen < atoi (val)) + return -1; + break; + case 3: /* maximum */ + if (slen > atoi (val)) + return -1; + break; + } + } + } + if (! done) + return -1; + } else { + while (*str && *pat) { + incs = True; + switch (tolower (*pat)) { + case '>': + done = True; + incs = False; + break; + case '<': + return -1; + break; + case '1': + if (! isdigit (*str)) + return -1; + break; + case 'h': + if (! isxdigit (*str)) + return -1; + break; + case 'l': + if (! islower (*str)) + return -1; + break; + case 'u': + if (! isupper (*str)) + return -1; + break; + case 'a': + if (! isalpha (*str)) + return -1; + break; + case 'n': + if (! isalnum (*str)) + return 1; + break; + case 'p': + if (! isprint (*str)) + return -1; + break; + case 'x': + if (! isascii (*str)) + return -1; + break; + } + if ((! done) || *(pat + 1)) + ++pat; + if (incs) + ++str; + } + if ((! done) && (*str || *pat)) + return -1; + } + return 0; +} +/*}}}*/ +/*{{{ preparse receiver and messages */ +static int +ncompare (const void *a, const void *b) +{ + return ((message *) a) -> nr - ((message *) b) -> nr; +} + +static int +mcompare (const void *a, const void *b) +{ + const message *am = (message *) a, + *bm = (message *) b; + int n; + + n = strcmp (am -> service, bm -> service); + if (! n) + n = strcmp (am -> pid, bm -> pid); + return n; +} + +static char * +do_replace (char *str, int start, int len, char *rplc, int rlen) +{ + int n, m; + int slen, diff; + + if (rlen > len) { + slen = strlen (str); + diff = rlen - len; + if (! (str = Realloc (str, slen + diff + 2))) + return NULL; + for (n = slen; n >= start + len; --n) + str[n + diff] = str[n]; + str[slen + diff] = '\0'; + } else if (rlen < len) { + for (n = start + rlen, m = start + len; str[m]; ++n, ++m) + str[n] = str[m]; + str[n] = '\0'; + } + if (rlen > 0) + memcpy (str + start, rplc, rlen); + return str; +} + +static void +remove_invalids (char *str, char *rmv) +{ + int m, n; + char *ptr; + + for (n = 0, m = 0; str[n]; ++n) { + for (ptr = rmv; *ptr; ++ptr) + if (*ptr == str[n]) + break; + if (! *ptr) + if (n != m) + str[m++] = str[n]; + else + ++m; + } + str[m] = '\0'; +} + +static message * +create_messages (void *cfg, char *service, serv *sbase, char **argv, int argc, char *mfile, int *mcnt, + char *callid, char *sig, Bool trunc, Bool force, date_t *delay, date_t *expire, Bool rds) +{ + char **recv; + int rcnt, rsiz; + FILE *fp; + message *mg; + int cnt, siz; + Bool lforce; + serv *srun; + int n, m, len; + char *str, *ptr, *sav, *tstr, *tptr; + string_t *fstdin; + Bool isalias; + Bool doins; + int rsize; + string_t *tmp; + char *rmv; + char *rplc; + int start, end; + Bool ttrunc; + char *tsig; + int msize; + Bool msplit; + char *sr; + + rcnt = 0; + rsiz = 0; + recv = NULL; + for (n = 0; n < argc; ++n) { + if (rcnt + 2 >= rsiz) { + rsiz += 16; + if (! (recv = (char **) Realloc (recv, (rsiz + 2) * sizeof (char *)))) { + OOM; + return NULL; + } + } + if (! (recv[rcnt] = strdup (argv[n]))) { + OOM; + return NULL; + } + ++rcnt; + } + if (mfile) { + if (! (fp = fopen (mfile, "r"))) { + fprintf (stderr, "Unable to open message file %s for reading\n", mfile); + return NULL; + } + while (ptr = getline (fp, False)) { + sav = skip (ptr); + if (*ptr && *sav) { + if (rcnt + 2 >= rsiz) { + rsiz += 16; + if (! (recv = (char **) Realloc (recv, (rsiz + 2) * sizeof (char *)))) { + OOM; + return NULL; + } + } + if ((! (recv[rcnt] = strdup (ptr))) || + (! (recv[rcnt + 1] = strdup (sav)))) { + OOM; + return NULL; + } + rcnt += 2; + } + } + fclose (fp); + } + if (! recv) { + fprintf (stderr, "No receiver found\n"); + return NULL; + } + recv[rcnt] = NULL; + + for (n = 0; n < rcnt; n += 2) { + ptr = recv[n]; + if (((! isdigit (*ptr)) || (*ptr == ':')) && (*ptr != '\\')) { + if (*ptr == ':') + ++ptr; + if (! (str = strdup (ptr))) { + OOM; + return NULL; + } + m = 0; + tstr = 0; + for (ptr = str; *ptr; ) { + sav = ptr; + ptr = skipch (ptr, ','); + if ((! (tptr = cfg_block_get (cfg, SEC_ALIAS, sav, NULL))) || + (! strchr (tptr, ','))) + tptr = sav; + len = strlen (tptr); + if (tstr = Realloc (tstr, m + len + 2)) { + if (m) + tstr[m++] = ','; + strcpy (tstr + m, tptr); + m += len; + } + } + free (str); + free (recv[n]); + if (! (recv[n] = tstr)) { + OOM; + return NULL; + } + } + } + + mg = NULL; + cnt = 0; + siz = 0; + fstdin = NULL; + for (n = 0; n < rcnt; n += 2) + if (str = recv[n]) { + for (ptr = str; *ptr; ) { + if (cnt >= siz) { + siz += 4; + if (! (mg = (message *) Realloc (mg, (siz + 1) * sizeof (message)))) { + OOM; + return NULL; + } + } + mg[cnt].nr = cnt; + sav = ptr; + ptr = skipch (ptr, ','); + if (*sav == '/') { + ++sav; + isalias = False; + } else if (*sav == ':') { + ++sav; + isalias = True; + } else if (! isdigit (*sav)) { + rmv = cfg_get (cfg, NULL, CFG_RMPID, NULL); + if (rmv && *sav && strchr (rmv, *sav)) + isalias = False; + else + isalias = True; + } else + isalias = False; + if (isalias) { + if (! (mg[cnt].alias = strdup (sav))) { + OOM; + return NULL; + } + if (! (tstr = cfg_block_get (cfg, SEC_ALIAS, sav, NULL))) { + fprintf (stderr, "No alias %s found\n", sav); + return NULL; + } else + sav = tstr; + } else + mg[cnt].alias = NULL; + mg[cnt].pid = strdup (sav); + sav = recv[n + 1]; + if (*sav == '+') { + if (! (mg[cnt].msg = readfile (sav + 1))) { + fprintf (stderr, "Unable to read %s, aborted\n", sav + 1); + return NULL; + } + } else if ((*sav == '-') && (! *(sav + 1))) { + if (! fstdin) + if (! (fstdin = read_stdin ())) { + fprintf (stderr, "Unable to read from stdin, aborted\n"); + return NULL; + } + mg[cnt].msg = snew (fstdin -> str, fstdin -> len); + } else if ((*sav == '.') && (! *(sav + 1))) + mg[cnt].msg = snew (NULL, 0); + else { + if (*sav == '\\') + ++sav; + mg[cnt].msg = snewc (sav); + } + if (! (mg[cnt].pid && mg[cnt].msg)) { + OOM; + return NULL; + } + mg[cnt].callid = NULL; + mg[cnt].service = NULL; + mg[cnt].prot = Unknown; + mg[cnt].s = NULL; + memset (& mg[cnt].st, 0, sizeof (mg[cnt].st)); + rmv = cfg_get (cfg, mg[cnt].service, CFG_RMPID, NULL); + for (srun = sbase; srun; srun = srun -> next) + if ((! service) || (! strcmp (srun -> name, service))) { + if (rmv) + remove_invalids (mg[cnt].pid, rmv); + if (v_alidate (srun -> pchk, mg[cnt].pid, & start, & end)) + break; + } + if (! srun) { + fprintf (stderr, "No service for pager id %s found\n", mg[cnt].pid); + return NULL; + } + if (! (mg[cnt].service = strdup (srun -> name))) { + OOM; + return NULL; + } + if (rplc = cfg_get (cfg, mg[cnt].service, CFG_CHPID, NULL)) { + if (! strcmp (rplc, "-")) + rplc = ""; + else if (*rplc == '\\') + ++rplc; + if (! (mg[cnt].pid = do_replace (mg[cnt].pid, start, end - start, rplc, strlen (rplc)))) { + OOM; + return NULL; + } + } + mg[cnt].s = srun; + V (4, ("Found service %s for %s\n", mg[cnt].service, mg[cnt].pid)); + ++cnt; + } + free (str); + } + free (recv); + if (fstdin) + sfree (fstdin); + if (cnt > 1) + qsort (mg, cnt, sizeof (message), mcompare); + for (n = 0; n < cnt; ++n) { + sr = mg[n].service; + if ((mg[n].prot = getproto (cfg_get (cfg, sr, CFG_PROTOCOL, NULL))) == Unknown) { + fprintf (stderr, "Unknown protocol for service %s\n", sr); + return NULL; + } + if (! callid) + ptr = cfg_get (cfg, sr, CFG_CALLID, NULL); + else + ptr = callid; + if (ptr) + if (! (mg[n].callid = strdup (ptr))) { + OOM; + return NULL; + } else if (rmv = cfg_get (cfg, sr, CFG_RMCID, NULL)) + remove_invalids (mg[n].callid, rmv); + if (cfg_bget (cfg, sr, CFG_USECID, DEF_USECID)) { + if (! mg[n].callid) { + fprintf (stderr, "Need caller id for service %s\n", sr); + return NULL; + } + } else if (mg[n].callid) { + free (mg[n].callid); + mg[n].callid = NULL; + } + if (mg[n].callid) { + if (mg[n].s && mg[n].s -> cchk) + if (! v_alidate (mg[n].s -> cchk, mg[n].callid, & start, & end)) { + fprintf (stderr, "Call ID %s is invalid for service %s\n", mg[n].callid, mg[n].service); + return NULL; + } + if (rplc = cfg_get (cfg, mg[n].service, CFG_CHCID, NULL)) { + if (! strcmp (rplc, "-")) + rplc = ""; + else if (*rplc == '\\') + ++rplc; + if (! (mg[n].callid = do_replace (mg[n].callid, start, end - start, rplc, strlen (rplc)))) { + OOM; + return NULL; + } + } + if (check (mg[n].callid, cfg_get (cfg, sr, CFG_CHKCID, NULL)) < 0) { + fprintf (stderr, "Invalid caller id %s for service %s\n", mg[n].callid, sr); + return NULL; + } + } + if (check (mg[n].pid, cfg_get (cfg, sr, CFG_CHKPID, NULL)) < 0) { + fprintf (stderr, "Invalid pager id %s for service %s\n", mg[n].pid, sr); + return NULL; + } + if (force) + lforce = force; + else + lforce = cfg_bget (cfg, sr, CFG_FORCE, False); + if (delay && (! cfg_bget (cfg, sr, CFG_CANDELAY, False)) && (! lforce)) { + fprintf (stderr, "Service %s is unable to delay a message\n", sr); + return NULL; + } + if (expire && (! cfg_bget (cfg, sr, CFG_CANEXP, False)) && (! lforce)) { + fprintf (stderr, "Service %s is unable to set expiration\n", sr); + return NULL; + } + if (! rds) + rds = cfg_bget (cfg, sr, CFG_DORDS, False); + if (rds && (! cfg_bget (cfg, sr, CFG_CANRDS, False)) && (! lforce)) { + fprintf (stderr, "Service %s is unable to report delivery status\n", sr); + return NULL; + } + if (! sig) + tsig = cfg_get (cfg, sr, CFG_SIG, NULL); + else + tsig = sig; + if (tsig && (! cfg_bget (cfg, sr, CFG_USESIG, False))) + tsig = NULL; + if (tsig && *tsig) + if (! (scatc (mg[n].msg, " ") && scatc (mg[n].msg, tsig))) { + OOM; + return NULL; + } + mg[n].s = NULL; + } + for (n = 0; n < cnt; ++n) { + sr = mg[n].service; + if (! trunc) + ttrunc = cfg_bget (cfg, sr, CFG_TRUNCATE, False); + else + ttrunc = trunc; + msize = cfg_iget (cfg, sr, CFG_MAXSIZE, 0); + msplit = cfg_bget (cfg, sr, CFG_MAYSPLIT, False); + if ((msize > 0) && (mg[n].msg -> len > msize)) + if (trunc) + mg[n].msg -> len = msize; + else if (! msplit) { + fprintf (stderr, "Unable to split message for service %s\n", sr); + return NULL; + } else { + doins = (msize > 40) ? True : False; + if (cnt >= siz) { + siz += 4; + if (! (mg = (message *) Realloc (mg, (siz + 1) * sizeof (message)))) { + OOM; + return NULL; + } + } + for (m = cnt; m > n + 1; --m) + mg[m] = mg[m - 1]; + mg[n + 1].nr = mg[n].nr; + mg[n + 1].callid = mg[n].callid ? strdup (mg[n].callid) : NULL; + mg[n + 1].alias = mg[n].alias ? strdup (mg[n].alias) : NULL; + mg[n + 1].pid = strdup (mg[n].pid); + mg[n + 1].service = strdup (mg[n].service); + mg[n + 1].prot = mg[n].prot; + if (doins) + rsize = msize - (sizeof (SPLIT_INIT) - 1); + else + rsize = msize; + for (m = 1; (m < 15) && (m < rsize); ++m) + if (sisspace (mg[n].msg, rsize - m)) { + rsize -= m; + sdel (mg[n].msg, rsize, 1); + break; + } + if (doins) { + if (mg[n + 1].msg = snewc (SPLIT_INIT)) + if (tmp = scut (mg[n].msg, rsize, mg[n].msg -> len - rsize)) { + if (! scat (mg[n + 1].msg, tmp)) + mg[n + 1].msg = sfree (mg[n + 1].msg); + else { + mg[n].msg -> len = rsize; + if (! scatc (mg[n].msg, SPLIT_TERM)) + mg[n].msg = sfree (mg[n].msg); + } + sfree (tmp); + } else + mg[n + 1].msg = sfree (mg[n + 1].msg); + } else { + mg[n + 1].msg = scut (mg[n].msg, rsize, mg[n].msg -> len - rsize); + mg[n].msg -> len = rsize; + } + mg[n + 1].s = NULL; + memset (& mg[n + 1].st, 0, sizeof (mg[n + 1].st)); + srelease (mg[n].msg); + if (! (mg[n + 1].pid && mg[n + 1].msg && mg[n + 1].service && mg[n].msg)) { + OOM; + return NULL; + } + ++cnt; + } + } +# ifndef NDEBUG + if (verbose >= 4) { + (*verbout) ("Sending following message%s:\n", (cnt == 1 ? "" : "s")); + for (n = 0; n < cnt; ++n) + if (mg[n].pid && mg[n].msg) + (*verbout) ("%-12s (%s, %s): %s\n", (mg[n].alias ? mg[n].alias : mg[n].pid), mg[n].service, mg[n].pid, schar (mg[n].msg)); + } +# endif /* NDEBUG */ + *mcnt = cnt; + return mg; +} +/*}}}*/ +/*{{{ calculate costs */ +# define WDAY(cc1,cc2) ((((unsigned char) (cc1)) << 8) | ((unsigned char) (cc2))) +# define DAY (60 * 24) + +typedef struct _ttable { + int wday; + int start, end; + double elen; + struct _ttable + *next; +} ttable; + +static char *vtab[] = { + "fixed", + "entity-length", + "max-entities", + "dial-overhead", + "cost", + "unit", + "timetable", + "remainder", + NULL +}; + +static int +tonum (char ch) +{ + switch (ch) { + default: + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + } +} + +static ttable * +parse_timetable (char *str) +{ + ttable *base, *prev, *tmp; + char *ptr, *sav, *cptr; + int n; + + base = NULL; + prev = NULL; + for (ptr = str; *ptr; ) { + sav = ptr; + ptr = skipch (ptr, ';'); + cptr = skipch (sav, '='); + if (tmp = (ttable *) malloc (sizeof (ttable))) { + tmp -> wday = 0; + tmp -> start = 0; + tmp -> end = DAY - 1; + tmp -> elen = atof (cptr); + tmp -> next = NULL; + while (*sav) + if (isalpha (*sav) && isalpha (*(sav + 1))) { + *sav = tolower (*sav); + *(sav + 1) = tolower (*(sav + 1)); + switch (WDAY (*sav, *(sav + 1))) { + case WDAY ('s', 'o'): tmp -> wday |= (1 << 0); break; + case WDAY ('m', 'o'): tmp -> wday |= (1 << 1); break; + case WDAY ('t', 'u'): tmp -> wday |= (1 << 2); break; + case WDAY ('w', 'e'): tmp -> wday |= (1 << 3); break; + case WDAY ('t', 'h'): tmp -> wday |= (1 << 4); break; + case WDAY ('f', 'r'): tmp -> wday |= (1 << 5); break; + case WDAY ('s', 'a'): tmp -> wday |= (1 << 6); break; + case WDAY ('w', 'k'): + tmp -> wday |= (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5); + break; + case WDAY ('s', 's'): + tmp -> wday |= (1 << 0) | (1 << 6); + break; + case WDAY ('a', 'l'): + tmp -> wday |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + break; + } + sav += 2; + } else + break; + while (*sav && (! isdigit (*sav))) + if (*sav == '-') + break; + if (isdigit (*sav)) { + n = 0; + while (isdigit (*sav)) { + n *= 10; + n += tonum (*sav++); + } + tmp -> start = (n / 100) * 60 + n % 100; + } + while (*sav && (! isdigit (*sav))) + ++sav; + if (isdigit (*sav)) { + n = 0; + while (isdigit (*sav)) { + n *= 10; + n += tonum (*sav++); + } + tmp -> end = (n / 100) * 60 + n % 100; + } + if (prev) + prev -> next = tmp; + else + base = tmp; + prev = tmp; + } + } + return base; +} + +static inline Bool +timetable_valid (ttable *t, int wday, int tim) +{ + if (t -> wday & (1 << wday)) + if (t -> end > t -> start) { + if ((tim >= t -> start) && + (tim <= t -> end)) + return True; + } else + if ((tim <= t -> end) || + (tim >= t -> start)) + return True; + return False; +} + +static char * +calc_cost (void *cfg, char *service, time_t start, int dur) +{ + char *ret; + char *tstr; + char *ptr, *var, *val; + int n; + Bool fixed; + double elen; + int ment; + int dover; + double cost; + char *unit; + char *ttab; + int remainder; + ttable *tbase, *trun; + struct tm *tt; + double fdur; + int cur, wday; + double sec; + int ents; + double fcost; + + if ((! (tstr = cfg_get (cfg, service, CFG_COST, NULL))) || + (! (tstr = strdup (tstr)))) + return NULL; + ret = NULL; + fixed = False; + elen = 0.0; + ment = -1; + dover = 0; + cost = 0.0; + unit = NULL; + ttab = NULL; + remainder = 2; + for (ptr = tstr; *ptr; ) { + var = ptr; + ptr = skipch (ptr, ','); + val = skipch (var, '='); + for (n = 0; vtab[n]; ++n) + if (! strcmp (vtab[n], var)) + break; + switch (n) { + case 0: /* fixed */ + fixed = True; + break; + case 1: /* entity-length */ + elen = atof (val); + break; + case 2: /* max-entities */ + ment = atoi (val); + break; + case 3: /* dial-overhead */ + dover = atoi (val); + break; + case 4: /* cost */ + cost = atof (val); + break; + case 5: /* unit */ + unit = val; + break; + case 6: /* timetable */ + ttab = val; + break; + case 7: /* remainder */ + remainder = atoi (val); + break; + } + } + if (dover > 0) { + start += dover; + dur -= dover; + if (dur < 1) + dur = 1; + } + if (ttab && (tbase = parse_timetable (ttab))) { + if (tt = localtime (& start)) { + cur = tt -> tm_hour * 60 + tt -> tm_min; + sec = (double) tt -> tm_sec; + wday = tt -> tm_wday; + ents = 0; + fdur = 0; + trun = NULL; + while (fdur <= (double) dur) { + if ((! trun) || (! timetable_valid (trun, wday, cur))) { + for (trun = tbase; trun; trun = trun -> next) + if (timetable_valid (trun, wday, cur)) + break; + if (! trun) + trun = tbase; + } + if (fixed) { + cost = trun -> elen; + break; + } + if (trun -> elen < 0.1) + break; + ++ents; + fdur += trun -> elen; + sec += trun -> elen; + while (sec >= 60.0) { + sec -= 60.0; + ++cur; + while (cur >= DAY) { + cur -= DAY; + wday++; + if (wday > 6) + wday = 0; + } + } + } + } + while (tbase) { + trun = tbase; + tbase = tbase -> next; + free (trun); + } + } else + ents = (int) ((double) dur / elen) + 1; + if ((ment > 0) && (ents > ment)) + ents = ment; + if (fixed) + fcost = cost; + else + fcost = cost * (double) ents; + if (fcost > 0.01) { + do_log (LG_COST, "Session costs aprox. %.*f %s", remainder, fcost, (unit ? unit : "")); + if (ret = malloc (64 + (unit ? strlen (unit) : 0))) { + sprintf (ret, "%.*f", remainder, fcost); + if (unit) { + for (ptr = ret; *ptr; ++ptr) + ; + *ptr++ = ' '; + strcpy (ptr, unit); + } + } + } + free (tstr); + return ret; +} +/*}}}*/ +/*{{{ send pages */ +static int +sendit (void *cfg, int cur, message *mg, int mcnt, int *rerr, + date_t *delay, date_t *expire, Bool rds) +{ + char *sr; + Bool err; + int sig; + int n, m; + char *ptr; + int cnt; + int mmsgs; + Bool inspid; + char *convtab; + void *ctab; + message *send; + int count; + void *sp; + char *modem, *reset; + int tout; + int (*transmit) (void *, void *, string_t *, message *, int, void *, char *, date_t *, date_t *, Bool); + time_t start, end; + int diff; + string_t *cid; + + sr = mg[cur].service; + mmsgs = cfg_iget (cfg, sr, CFG_MAXMSGS, 0); + inspid = cfg_bget (cfg, sr, CFG_INSPID, DEF_INSPID); + for (n = cur, cnt = 0; n < mcnt; ++n, ++cnt) { + if (n != cur) + if (strcmp (mg[n].service, sr)) + break; + else if ((mmsgs > 0) && (cnt == mmsgs)) + break; + else if (inspid && strcmp (mg[cur].pid, mg[n].pid)) + break; + mg[n].st.success = False; + mg[n].st.reason = NotTried; + mg[n].st.rmsg = NULL; + } + ctab = NULL; + if (ctab = cv_new ()) { + if (convtab = cfg_get (cfg, sr, CFG_CONVTAB, DEF_CONVTAB)) + cv_read_table (ctab, convtab); + extend_convtable (ctab, cfg, sr); + } + send = mg + cur; + count = cnt; + do_log (LG_SSESSION, "Starting up for service %s", sr); + time (& start); + err = 0; + if (sp = do_dial (cfg, sr, (inspid ? mg[0].pid : NULL), & modem)) { + for (n = 0; n < cnt; ++n) + mg[cur + n].st.reason = NotSend; + if (! (sig = setjmp (env))) { + dojump = True; + switch (mg[cur].prot) { + default: + case Unknown: + transmit = NULL; + break; + case Ascii: + transmit = asc_send; + break; + case Script: + transmit = scr_send; + break; + case Tap: + transmit = tap_send; + break; + case Ucp: + transmit = ucp_send; + break; + } + if (transmit) { + cid = mg[0].callid ? snewc (mg[0].callid) : NULL; + err = (*transmit) (sp, ctab, cid, send, count, cfg, sr, delay, expire, rds); + sfree (cid); + } else + err = 1; + tty_drain (sp, 1); + } else + V (2, ("\n")); + dojump = False; + for (n = 0, m = -1; n < cnt; ++n) + if (mg[n + cur].st.success == True) + m = n; + if (m != -1) + cnt = m + 1; + tty_hangup (sp, 500); + time (& end); + diff = (int) (end - start); + if (ptr = calc_cost (cfg, sr, start, diff)) + if (! mg[cur].st.rmsg) + mg[cur].st.rmsg = ptr; + else + free (ptr); + do_log (LG_ESESSION, "Session %s on service %s (duration %d:%02d)", (err ? "failed" : "succeeded"), sr, diff / 60, diff % 60); + reset = cfg_get (cfg, modem, CFG_RESET, DEF_RESET); + if (reset) { + tout = cfg_iget (cfg, modem, CFG_TIMEOUT, DEF_TIMEOUT); + tty_send_expect (sp, tout, reset, NULL); + } + tty_close (sp); + if (modem) + free (modem); + if ((sig == SIGTERM) || (sig == SIGQUIT)) + longjmp (termenv, sig); + } else { + for (n = cur + 1; n < mcnt; ++n) + if (strcmp (sr, mg[n].service)) + break; + cnt = n - cur; + for (n = 0; n < cnt; ++n) + mg[cur + n].st.reason = CantDial; + do_log (LG_ESESSION, "Unable to dial service %s", sr); + fprintf (stderr, "Unable to dial %s\n", sr); + err = 1; + } + if (err) + *rerr = err; + return cur + cnt; +} +/*}}}*/ +/*{{{ print configuration values */ +static void +print_config (void *cfg, char *service, char **vars) +{ + int n; + char *val; + + while (*vars) { + printf ("%s ->", *vars); + for (n = service ? 0 : 1; n < 2; ++n) { + if (n && service) + printf (" ("); + else + printf (" "); + if (val = n ? cfg_get (cfg, NULL, *vars, NULL) : cfg_block_get (cfg, service, *vars, NULL)) + printf ("%s", val); + else + printf ("*none*"); + if (n && service) + printf (")"); + if (! n) + printf (" ->"); + else + printf ("\n"); + } + ++vars; + } +} +/*}}}*/ +/*{{{ getopt */ +# if ! HAVE_GETOPT && ! HAVE_GETOPT_LONG +# if NEED_OPTIND_OPTARG +# undef NEED_OPTIND_OPTARG +# endif /* NEED_OPTIND_OPTARG */ +static int optind = 1; +static int optidx = 0; +static char *optarg = NULL; + +static int +getopt (int argc, char **argv, char *what) +{ + char ch, *ptr; + + optarg = NULL; + if (optind >= argc) + return -1; + do { + if (! argv[optind][optidx]) { + optidx = 0; + ++optind; + if (optind >= argc) + return -1; + } + if (optidx == 0) + if (argv[optind][0] != '-') + return -1; + else + optidx = 1; + } while (! optidx); + if ((optidx == 1) && (argv[optind][optidx] == '-') && (! argv[optind][optidx + 1])) { + ++optind; + return -1; + } + ch = argv[optind][optidx++]; + if (! (ptr = strchr (what, ch))) { + fprintf (stderr, "%s: Unknown option -%c\n", argv[0] ? argv[0] : "", ch); + return '?'; + } + ++ptr; + if (*ptr == ':') + if (argv[optind][optidx]) { + optarg = (argv[optind]) + optidx; + ++optind; + optidx = 0; + } else { + ++optind; + if (optind < argc) + optarg = argv[optind++]; + else { + fprintf (stderr, "%s: Missiong parameter for option -%c\n", argv[0] ? argv[0] : "", ch); + ch = ':'; + } + optidx = 0; + } + return ch; +} +# endif /* ! HAVE_GETOPT && ! HAVE_GETOPT_LONG */ +/*}}}*/ +/*{{{ usage/version */ +static void +usage (char *pgm) +{ +# if HAVE_GETOPT_LONG +# define PLONG(xxx) fprintf xxx +# else /* HAVE_GETOPT_LONG */ +# define PLONG(xxx) putc ('\t', stderr) +# endif /* HAVE_GETOPT_LONG */ + fprintf (stderr, "Usgae: %s [] { }+\n", pgm); + fprintf (stderr, "Function: sends messages to paging devices\n"); + fprintf (stderr, "Options:\n"); + PLONG ((stderr, "\t--config=, ")); + fprintf (stderr, "-C uses alternate global configurtion file\n"); + PLONG ((stderr, "\t--service=, ")); + fprintf (stderr, "-s use this service, if applicated\n"); + PLONG ((stderr, "\t--truncate, ")); + fprintf (stderr, "-t truncates too long messages\n"); + PLONG ((stderr, "\t--call-id=, ")); + fprintf (stderr, "-c if possible, use this caller id\n"); + PLONG ((stderr, "\t--signature=, ")); + fprintf (stderr, "-S add this signature to the message\n"); + PLONG ((stderr, "\t--logfile=, ")); + fprintf (stderr, "-l log activities to a file\n"); + PLONG ((stderr, "\t--logstring=, ")), + fprintf (stderr, "-L log only these activities\n"); + PLONG ((stderr, "\t--force, ")); + fprintf (stderr, "-f force sending, even if -d/-e/-r is not supported\n"); + PLONG ((stderr, "\t--delay=, ")); + fprintf (stderr, "-d when to send the message (if supported)\n"); + PLONG ((stderr, "\t--expire=, ")); + fprintf (stderr, "-e when should the mesage expire (if supported)\n"); + PLONG ((stderr, "\t--request-delivery-status, ")); + fprintf (stderr, "-r request delivery report (if supported)\n"); + PLONG ((stderr, "\t--final-report=, ")); + fprintf (stderr, "-R store the final report to \n"); + PLONG ((stderr, "\t--message=file=, ")); + fprintf (stderr, "-z read pager-id/message pairs from \n"); + PLONG ((stderr, "\t--verbose[=], ")); + fprintf (stderr, "-v increase verbosity\n"); + fprintf (stderr, "\t this is the receiver of the message\n"); + fprintf (stderr, "\t the message, if it is prefixed by a plus sign (+), then the remaining part\n"); + fprintf (stderr, "\t is taken as a filename from where the message is read\n"); + fprintf (stderr, "\n"); + PLONG ((stderr, "\t--print-config, ")); + fprintf (stderr, "-p print configuration variables/values\n"); + fprintf (stderr, "\t the arguments are the variable names to be displayed\n"); + PLONG ((stderr, "\t--version, ")); + fprintf (stderr, "-V show version and compile informations\n"); +# ifdef VERSION + fprintf (stderr, "\nVersion: %s\n", VERSION); +# endif /* VERSION */ +# undef PLONG +} + +static void +show_version (char *pgm) +{ + printf ("%s: V. %s\n", pgm ? pgm : "yaps", VERSION); + printf ("Definitions:"); +# if POSIX_SIGNAL + printf (" POSIX_SIGNAL"); +# endif /* POSIX_SIGNAL */ +# if BSD_SIGNAL + printf (" BSD_SIGNAL"); +# endif /* BSD_SIGNAL */ +# if SYSV_SIGNAL + printf (" SYSV_SIGNAL"); +# endif /* SYSV_SIGNAL */ +# if SIG_VOID_RETURN + printf (" SIG_VOID_RETURN"); +# endif /* SIG_VOID_RETURN */ +# if SIG_INT_RETURN + printf (" SIG_INT_RETURN"); +# endif /* SIG_INT_RETURN */ +# if HAVE_SYS_SELECT_H + printf (" HAVE_SYS_SELECT_H"); +# endif /* HAVE_SYS_SELECT_H */ +# if HAVE_LOCALE_H + printf (" HAVE_LOCALE_H"); +# endif /* HAVE_LOCALE_H */ +# if HAVE_REGEX_H + printf (" HAVE_REGEX_H"); +# endif /* HAVE_REGEX_H */ +# if HAVE_SYS_SYSMACROS_H + printf (" HAVE_SYS_SYSMACROS_H"); +# endif /* HAVE_SYS_SYSMACROS_H */ +# if HAVE_SYS_MKDEV_H + printf (" HAVE_SYS_MKDEV_H"); +# endif /* HAVE_SYS_MKDEV_H */ +# if HAVE_GETOPT_H + printf (" HAVE_GETOPT_H"); +# endif /* HAVE_GETOPT_H */ +# if HAVE_TZSET + printf (" HAVE_TZSET"); +# endif /* HAVE_TZSET */ +# if HAVE_FCHMOD + printf (" HAVE_FCHMOD"); +# endif /* HAVE_FCHMOD */ +# if HAVE_FCHOWN + printf (" HAVE_FCHOWN"); +# endif /* HAVE_FCHOWN */ +# if HAVE_SIGSETJMP + printf (" HAVE_SIGSETJMP"); +# endif /* HAVE_SIGSETJMP */ +# if HAVE_MEMCPY + printf (" HAVE_MEMCPY"); +# endif /* HAVE_MEMCPY */ +# if HAVE_BCOPY + printf (" HAVE_BCOPY"); +# endif /* HAVE_BCOPY */ +# if HAVE_MEMSET + printf (" HAVE_MEMSET"); +# endif /* HAVE_MEMSET */ +# if HAVE_BZERO + printf (" HAVE_BZERO"); +# endif /* HAVE_BZERO */ +# if HAVE_GETOPT + printf (" HAVE_GETOPT"); +# endif /* HAVE_GETOPT */ +# if HAVE_GETOPT_LONG + printf (" HAVE_GETOPT_LONG"); +# endif /* HAVE_GETOPT_LONG */ +# if NEED_OPTIND_OPTARG + printf (" NEED_OPTIND_OPTARG"); +# endif /* NEED_OPTIND_OPTARG */ +# if BROKEN_REALLOC + printf (" BROKEN_REALLOC"); +# endif /* BROKEN_REALLOC */ + printf ("\n"); +} +/*}}}*/ +/*{{{ main */ +# if HAVE_GETOPT_LONG +static struct option opttab[] = { + { "config", required_argument, NULL, 'C' }, + { "service", required_argument, NULL, 's' }, + { "truncate", no_argument, NULL, 't' }, + { "call-id", required_argument, NULL, 'c' }, + { "signature", required_argument, NULL, 'S' }, + { "logfile", required_argument, NULL, 'l' }, + { "logstring", required_argument, NULL, 'L' }, + { "force", no_argument, NULL, 'f' }, + { "delay", required_argument, NULL, 'd' }, + { "expire", required_argument, NULL, 'e' }, + { "request-delivery-status", no_argument, NULL, 'r' }, + { "final-report", required_argument, NULL, 'R' }, + { "message-file", required_argument, NULL, 'z' }, + { "verbose", optional_argument, NULL, 'v' }, + { "print-config", no_argument, NULL, 'p' }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } +}; +# define GETOPT(what) getopt_long (argc, argv, (what), opttab, NULL) +# else /* HAVE_GETOPT_LONG */ +# define GETOPT(what) getopt (argc, argv, (what)) +# endif /* HAVE_GETOPT_LONG */ + +int +main (int argc, char **argv) +{ +# if NEED_OPTIND_OPTARG + extern int optind; + extern char *optarg; +# endif /* NEED_OPTIND_OPTARG */ + char *cfgfile; + void *cfg; + char *home, *lfn; + int n, m; + int ret; + char *svcs; + serv *sbase, *sprev, *stmp; + char *chk; + char *service; + Bool trunc; + char *callid; + char *sig; + Bool force; + date_t *delay, *expire; + Bool rds; + char *freport; + FILE *fp; + char *mfile; + Bool pcfg; + date_t *cur; + message *mg; + int mcnt; + int onr; + Bool succ; + char *ptr, *sav; +# if POSIX_SIGNAL + struct sigaction + sact, sign; +# endif /* POSIX_SIGNAL */ + + cfgfile = CFGFILE; + service = NULL; + trunc = False; + callid = NULL; + sig = NULL; + force = False; + delay = NULL; + expire = NULL; + rds = False; + freport = NULL; + mfile = NULL; + pcfg = False; + logfile = NULL; + logstr = NULL; + verbose = -1; +# if HAVE_TZSET + tzset (); +# endif /* HAVE_TZSET */ +# if HAVE_LOCALE_H + setlocale (LC_ALL, ""); +# endif /* HAVE_LOCALE_H */ + while ((n = GETOPT ("C:s:tc:S:l:L:fd:e:rR:z:vpVh")) != -1) + switch (n) { + case 'C': + cfgfile = optarg; + break; + case 's': + service = optarg; + break; + case 't': + trunc = True; + break; + case 'c': + callid = optarg; + break; + case 'S': + sig = optarg; + break; + case 'l': + logfile = optarg; + break; + case 'L': + logstr = optarg; + break; + case 'f': + force = True; + break; + case 'd': + if (delay) + delay = dat_free (delay); + if (! (delay = dat_parse (optarg))) { + fprintf (stderr, "Invalid date format for delay\n"); + return 1; + } + break; + case 'e': + if (expire) + expire = dat_free (expire); + if (! (expire = dat_parse (optarg))) { + fprintf (stderr, "Invalid date format for expire\n"); + return 1; + } + break; + case 'r': + rds = True; + break; + case 'R': + freport = optarg; + break; + case 'z': + mfile = optarg; + break; + case 'v': +# if HAVE_GETOPT_LONG + if (optarg) + verbose = atoi (optarg); + else +# endif /* HAVE_GETOPT_LONG */ + if (verbose < 0) + verbose = 1; + else + ++verbose; + break; + case 'p': + pcfg = True; + break; + case 'V': + show_version (argv[0]); + return 0; + case 'h': + default: + usage (argv[0]); + return n == 'h' ? 0 : 1; + } + if (delay || expire) { + if (cur = dat_parse (NULL)) { + if (delay && (dat_diff (cur, delay) < 0)) { + fprintf (stderr, "Delay is before now, invalid\n"); + return 1; + } + if (expire && (dat_diff (cur, expire) < 0)) { + fprintf (stderr, "Expiration already reached\n"); + return 1; + } + dat_free (cur); + } + if (delay && expire) + if (dat_diff (delay, expire) < 0) { + fprintf (stderr, "Message expires before it should be send\n"); + return 1; + } + } + cfg = cfg_read (cfgfile, NULL, CONFIG_SEP); + if ((home = getenv ("HOME")) && (lfn = malloc (strlen (home) + sizeof (LCFGFILE) + 4))) { + sprintf (lfn, "%s/%s", home, LCFGFILE); + cfg = cfg_read (lfn, cfg, CONFIG_SEP); + free (lfn); + } + if (pcfg) { + print_config (cfg, service, argv + optind); + return 0; + } + if (((optind == argc) && (! mfile)) || ((argc - optind) & 1)) { + if ((optind == argc) && (! mfile)) + fprintf (stderr, "No message\n"); + if ((argc - optind) & 1) + fprintf (stderr, "Unbalanced pagerid/message\n"); + usage (argv[0]); + return 1; + } + if (mfile && (access (mfile, R_OK) < 0)) { + fprintf (stderr, "Unable to read message file %s\n", mfile); + return 1; + } + if (verbose < 0) + verbose = cfg_iget (cfg, NULL, CFG_VERBOSE, 0); + if (! logfile) + logfile = cfg_get (cfg, NULL, CFG_LOGFILE, NULL); + if (! logstr) + logstr = cfg_get (cfg, NULL, CFG_LOGSTR, NULL); + if (! (svcs = cfg_get (cfg, NULL, CFG_SERVICES, NULL))) { + fprintf (stderr, "No services definied\n"); + return 1; + } + if (! (svcs = strdup (svcs))) { + OOM; + return 1; + } + sbase = NULL; + sprev = NULL; + for (ptr = svcs; *ptr; ) { + sav = ptr; + ptr = skipch (ptr, ','); + if (! (chk = cfg_block_get (cfg, sav, CFG_VALPID, NULL))) { + fprintf (stderr, "Service %s has no pattern for matching pager ids\n", sav); + return 1; + } + if ((stmp = (serv *) malloc (sizeof (serv))) && + (stmp -> name = strdup (sav))) { + if (! (stmp -> pchk = v_new (chk))) { + fprintf (stderr, "Pattern %s in service %s is not valid\n", chk, sav); + return 1; + } + stmp -> cchk = NULL; + if (chk = cfg_get (cfg, sav, CFG_VALCID, NULL)) + if (! (stmp -> cchk = v_new (chk))) { + fprintf (stderr, "Pattern %s in service %s not valid\n", chk, sav); + return 1; + } + stmp -> next = NULL; + if (sprev) + sprev -> next = stmp; + else + sbase = stmp; + sprev = stmp; + } else { + OOM; + return 1; + } + } + if (! sbase) { + fprintf (stderr, "No services found\n"); + return 1; + } + if (! (mg = create_messages (cfg, service, sbase, argv + optind, argc - optind, mfile, & mcnt, callid, sig, trunc, force, delay, expire, rds))) + return 1; + while (sbase) { + stmp = sbase; + sbase = sbase -> next; + free (stmp -> name); + v_free (stmp -> pchk); + if (stmp -> cchk) + v_free (stmp -> cchk); + free (stmp); + } + jumpdone = False; + if (! (ret = setjmp (termenv))) { + dojump = False; +# if POSIX_SIGNAL + sact.sa_handler = handler; + sign.sa_handler = SIG_IGN; + sigemptyset (& sact.sa_mask); + sigemptyset (& sign.sa_mask); + sigaddset (& sact.sa_mask, SIGHUP); + sigaddset (& sact.sa_mask, SIGINT); + sigaddset (& sact.sa_mask, SIGQUIT); + sigaddset (& sact.sa_mask, SIGTERM); + sigaddset (& sact.sa_mask, SIGPIPE); + sigaddset (& sign.sa_mask, SIGHUP); + sigaddset (& sign.sa_mask, SIGINT); + sigaddset (& sign.sa_mask, SIGQUIT); + sigaddset (& sign.sa_mask, SIGTERM); + sigaddset (& sign.sa_mask, SIGPIPE); + sact.sa_flags = 0; + sign.sa_flags = 0; + sigaction (SIGHUP, & sact, NULL); + sigaction (SIGINT, & sact, NULL); + sigaction (SIGQUIT, & sact, NULL); + sigaction (SIGTERM, & sact, NULL); + sigaction (SIGPIPE, & sign, NULL); +# else /* POSIX_SIGNAL */ + signal (SIGHUP, handler); + signal (SIGINT, handler); + signal (SIGQUIT, handler); + signal (SIGTERM, handler); +# ifdef SIGPIPE + signal (SIGPIPE, SIG_IGN); +# endif /* SIGPIPE */ +# endif /* POSIX_SIGNAL */ + for (n = 0; n < mcnt; ) + n = sendit (cfg, n, mg, mcnt, & ret, delay, expire, rds); + } + if (mg) { + if (! freport) + freport = cfg_get (cfg, NULL, CFG_FREPORT, NULL); + fp = NULL; + if (freport) + if (! strcmp (freport, "-")) + fp = stdout; + else if (! (fp = fopen (freport, "w"))) { + fprintf (stderr, "Unable to open %s for final report, sending it to stdout\n", freport); + fp = stdout; + } + if (fp && (mcnt > 1)) + qsort (mg, mcnt, sizeof (message), ncompare); + for (m = 0, onr = -1; m < mcnt; ++m) { + if (fp && (onr != mg[m].nr)) { + onr = mg[m].nr; + succ = True; + for (n = m; (n < mcnt) && (mg[n].nr == onr); ++n) + if (! (mg[n].st.success)) { + succ = False; + break; + } + ptr = NULL; + if (! succ) { + switch (mg[n].st.reason) { + case NotTried: + ptr = "unable to connect"; + break; + case CantDial: + ptr = "could not dial service provider"; + break; + case NotSend: + ptr = "unable to send"; + break; + case CantSend: + ptr = "could not transmit the message"; + break; + } + } + fprintf (fp, "%c%d %s: %s %s\n", + (succ ? '+' : '-'), mg[m].nr, + (mg[m].alias ? mg[m].alias : (mg[m].pid ? mg[m].pid : "")), + (mg[m].st.rmsg ? mg[m].st.rmsg : ""), + (ptr ? ptr : "")); + } + if (mg[m].callid) + free (mg[m].callid); + if (mg[m].alias) + free (mg[m].alias); + if (mg[m].pid) + free (mg[m].pid); + if (mg[m].service) + free (mg[m].service); + if (mg[m].msg) + sfree (mg[m].msg); + if (mg[m].st.rmsg) + free (mg[m].st.rmsg); + } + free (mg); + if (fp && (fp != stdout)) + fclose (fp); + } + cfg_end (cfg); + return ret; +} +/*}}}*/ diff --git a/yaps.doc b/yaps.doc new file mode 100644 index 0000000..0a7bf8b --- /dev/null +++ b/yaps.doc @@ -0,0 +1,910 @@ + + Table of Contents + + 1. Introduction + 2. Installation + 3. Configuration + 4. Command Line + 5. Sending/Expecting Syntax + 6. Scripting + 7. Examples + 8. Changelog + + Introduction + + Welcome to Yaps, Yet Another Paging Package. The program is used to + send textual messages to a remote paging device using a modem gateway. + Many pager companies offer such modem gateways and are using specific + protocols. The most often used one, TAP is implemented beside others + (more or less complete.) This command line program uses a global and a + private configuration file to get information about each paging + service, installed modems and possibly other. + + As the main part is implemented as a library it is not very + complicated to write a client/server solution. For the legal status + see the File Copying. + + Installation + + The software is written, tested and designed on and for Unix(tm) like + operating systems and requires the so called Posix system calls and + function library. Gnumake and GCC are required, too (or some changes + to the Makefile and the code may be neccessary.) It is known to run at + least on following operating systems: + * Linux (native system) + * Solaris 2.5.1 (Frank Käfer) + * SunOS 4.1.3 aka Solaris 1.1 (Frank Käfer) + + (more may follow.) + + Typically these steps should be enough to install the package: + + Edit Config + For GCC the settings of CC and CFLAGS should be okay. Define + SLANG=True, if you like to use the SLang scripting language, + which can be found on space.mit.edu. Install it somewhere the + compiler can find it. Define LUA=True for using the lua + scripting language, which is located on ftp.icad.puc-rio.br. + READ THE COPYRIGHT RELATED FILES OF EACH PACKAGE BEFORE + INSTALLING IT! + + Edit config.h + This is used to adapt the software to various variants of + Unix(tm). If you do not have a regex.h file, either set + HAVE_REGEX_T to zero or install the GNU regular expression + library. Be sure the library and includefile can be found by + the compiler and the name of the header file is regex.h, + otherwise make a (symbolic) link to this file. Add the libary + to EXLIB = in Config. + + make depend + This create the dependency file which will be included during + make. + + make (or make all) + This compiles the library, the program driver and links the + program. The documentation is created using lynx. As lynx is + not on every system the already formated result is included as + well. + + make install + This copies the program and the global configuration file to + the final location. This step requires a working install + program. If this is missing, you have to install it by hand + (well, two files should not be too much.) + + Edit the global configuration file + With your favorite editor you should edit the global + configuration file (which will be installed as /etc/yaps.rc on + default) and change everything as required. See the + Configuration section for a detailed description of the + contents of the file. + + Done! + You may play around with your configuration until you are + satisfied, but this fine tuning is up to you. + + If you have problems during compilation, feel free to send me a mail. + + Configuration + + The program reads first the global configuration file /etc/yaps.rc + (when not overwritten by command line switch), then the local one in + ~/.yapsrc to get its configuration. Command line options may overwrite + some settings in these files. Both configuration files have the same + layout where the local one can overwrite settings of the global one. + Each file is seperated in sections, where a section is started with + [] or [] for the global section. When a file is opened all + entries are stored in the global section until a section name appears. + If a section expression [..] is followed by a comma seperate list of + names, then these are the fallback sections, i.e. variables not found + int his section are taken from the fallback sections before trying the + global one. + + If the first character is a bar (|) then the rest is taken as a + filename to be included at this point. The current section is NOT + affected by this call. Each line may have exactly one entry (or a + comment starting with a hash sign in the first column) where the + variable name is seperated by whitespaces from its value. If the first + character of the value is a backslash (\), then this is removed. This + allowes creating values starting with whitespaces. But if a value + should start with a backslash itself, be sure to escape it with + another backslash. A line can split over several physical line, if + each line (expect the last one) ends with a backslash. If the value + starts with an opening curly bracket ({) then all lines up to the + closing curly bracket (}) in the first column are taken as the value + for that variable. The brackets (and the lines where they appear) are + not part of the value. + + The value may either be textual, numeric, boolean or a check + expression. Textual values are starting with the first non whitespace + character and continue to the end of the line. Numeric values are a + sequence of digits which are parsed by the C-function atoi(3). Boolean + may either be T, Y, 1 or + for true and F, N, 0 or - for false (or in + greater detail, everything that is not true is false.) Case is here + not significant. The check is a sequence of special characters which + must match the given string. If a > is found, then the rest is + optional, but must still match, if a < is found and the string is not + at its end, it is considered as too long. These characters are + supported: + * 1 numeric + * h hexadecimal + * l lowercase + * u uppercase + * a alpha + * n alphanumeric + * p printable + * x ascii + * every other character matches every character + + Some simple examples: + * >1 A string with unlimited length, but made only out of numeric + values. + * aaa A string of exact three alpha characters. + * >xxxx< A string of length zero up to four made of ASCII + characters. + * ....>1111< A string starting with exact four characters, followed + by zero to four numeric characters. + + Alternative one can specify the check by a description, if the + checkstring is prefixed by a plus sign. This is then a comma seperated + list of checks, which are seperated by an equal sign into variable and + value. These variables are supported: + * type= Each character in the string must match the type, + which may be one of the following: + + numeric only digits are allowed. + + sedecimal only hexdigits are allowed. + + lower only lowercase characters are allowed. + + upper only uppercase characters are allowed. + + alpha only alpha characters are allowed. + + alphanumeric only digits and alpha characters are allowed. + + print only printable characters are allowed. + + ascii only ASCII characters are allowed. + * length= The string must have exact this length. + * minimum= The string must at least this length. + * maximum= The string must not be longer than this length. + + The global section defines global values or default values for other + sections. Some section may not inherit these values, but currently + there is only one and is marked as such. + +Global section + + These are typical found in the global section and can be overwritten + in other sections, if required. + + services + This is the comma seperated list of available services. + + call-id + If the pager allows to send the source caller id this id is + used, if not overwritten by a command line argument. + + signature + If this is present, then the text is appended to each message + sent to the pager (and if use-signature is True). + + verbose + Sets the debugging/verbosity level. The bigger the number, the + more output is generated. + + logfile + If the given text is a valid filename the status of a sending + request is loged there. + + logstring + If this is present, then only these parts are logged, which + match the given string. Currently these elements are supported: + + + message had been trasmitted successfully + - message had not been transmitted + * part had been transmitted successfully + / part had not been transmitted + i informal logfile entry + c cost relevant informations + s session start + e session end + p protocol specific + + modems + This is a list of modem entries, seperated by commas. + + final-report + If this is set, a final report is written to the given + filename. If filename is -, then it is written to stdout. This + feature is intended for use in shell scripts, so the script + could proof which message has actual send and which one has + been rejected instead of simple rely on the return code of + yaps. + +Service section + + The names of these section are free to the user, but their contents + describe a paging service (or a paging company.) Following variables + are allowed for service sections: + + speed + The speed of the serial device. + + bits-per-byte + The number of bits per byte, supported are 5 to 8. + + parity + The used parity, this may either be none, even or odd. + + stopbits + The number of stopbits, supported are 1 or 2. + + phone + The phone number of the modem gateway for this service. If the + number contains %P and insert-pager-id is set, then the pagerid + is inserted into the phonenumber. + + protocol + This is the communication protocol, that this service requires. + Currently are ascii, script, tap and ucp allowed. + + max-size + The maximal length of a message for this service. + + may-split + If this is true, then a message, which is too long will be + split into several messages. + + max-messages + The maximum number of messages per message, that can be send. + If this is zero, then an unlimited number of messages can be + send. + + truncate + If this is set a message is not split, but truncated to the + length allowed by the provider. + + use-call-id + If this is true, then the caller-id is inserted. Where it is + insert is protocol specific. + + use-signature + If this is true and a signature is given, this is appended to + each message. + + insert-pager-id + If this is true, then the pagerid is inserted as part of the + phone number. This implies, that only messages to the same + receipiant can be send at one call. + + rm-invalids-cid + Remove every character in text, that appears in the caller id. + This is designed to make a caller id more readable, but still + useable by the desired service. + + rm-invalids-pid + Dito for pager ids. + + valid-pid + This is a regular expression to check wether a pager-id is + valid for this service. If your system does not support Posix + regular expression, you can use a simple replacement. This is + just a list of strings, seperated by a bar which must match the + beginning of the pager-id. + + change-pid + If the pager-id matches the regular expression of valid-pid, + then this matching part is replaced with this string. If the + string is just a minus (-), then the match is removed from the + resulting pager-id. + + valid-cid + The same like valid-pid for the caller id. + + change-cid + The same like change-pid for the caller id. + + conv-table + The value is a filename to a character conversion table. This + affects only the message (and depending on the protocol the + pagerid) and allows transforming illegal characters to valid + ones. + + convert + This is comma seperated list of direct change entries of the + convertion table. If you want to include more than one pair, + the block construct using curly brackets ({..}) must be used. + Each line contains a source and a destination character, i.e. + if the source character is encountered in a message, the + destination character is used for it. There are also some + predefined convertion rules, which all starts with an asterisk: + + + *no-control control characters are suppressed + + *control control characters are inserted literal + + *no-8bit no characters with 8'th bit set are included + + *8bit use also characters with 8'th bit set. + + *numeric only numeric characters are allowed. + + cost + This string is used to calculate the costs for each call. The + description is a comma seperated list of variables with equal + sign seperated optional value. These variables are currently + implemented: + + fixed + This tells the software, that the cost are fixed per call, + i.e. no real calculation takes place according to the used + entities. + entity-length= + This is the length of one entity. This is interpreted as a + floating point number. + max-entities= + Some providers only charge until a maximum of entites had + been used and stop then charging. + dial-overhead= + The counter starts before dialing. Typically it takes some + time from dialing to the first billed entity. This time + can be specified using this variable. + cost= + This is the cost per entity (or the whole cost on fixed + charging services). This is a floating point number. + unit= + This string is appended in the logfile for the currency. + This is only of cosmetic value. + remainder= + This is the number of digits after the point in the cost + display. This value is typical two. + timetable= + If a timetable entry is given, then the cost are + calculated depending on weekday and time. On fixed + charging services the value describes the complete costs + for the call, in the other case the value is the + entity-length for this day/time. The description is a + semi-colon seperated list of single entries of the form: + -= + Each weekday is a two letter sequence (So, Mo, Tu, We, Th, + Fr, Sa) and there are three special "weekdays": Wk for + working days (monday to friday), Ss for weekend (saturday + and sunday) and al for all days. A typical example may + look like: Wk0800-1800=12;Wk1800-0800=24;Ss=24, but this + could be written shorter as: =24;Wk0800-1800=12 because + the first entry is taken as the default, if no match is + found. And the construct =24 does not contain any weekday, + so it is invalid for any regular check. + + force + If a feature is requested, but this service do not support it, + the message will still be delivered, if this variable is set to + True. + + can-delay + Set this to True, if the service provider accepts a delay for + sending the message. + + can-expire + Set this to True, if the service provider allowes the setting + of an expiration timestamp. + + can-rds + Set this to True, if the service provider allowes the request + for a delivery status. + + rds + Set this to True, if can-rds is True and you always want to get + a delivery status. + + check-call-id + The caller id must match the check expression. + + check-pager-id + Each pager id must match the check expression. + +Modem section + + Each modem should have its own section. Following entries are + currently supported: + + device + The filename for the device where the modem is attached to. + + lock-prefix + This is the pathname to prefix the basename of the modemdevice + to create lockfiles. This can be used to enable more than one + application to use the modem. + + lock-method + This is a comma seperated list of locking mechanism. Currently + these flags are supported: + + + ascii PID is stored as ASCII text in lockfile. + + binary PID is stored in machine representation in lockfile. + + lower converts device part of lockfile to lower case. + + upper converts device part of lockfile to upper case. + + sysv4 append SysV4 style infos to lockfile instead of the + basename of the device. + + timeout= tries to lock the device secs seconds. + + init + This is the init sequence to initialize the modem. + + dial + This is the dial sequence to dial a phone number with the + modem. An \L in the string will be replaced by the phone + number. + + timeout + This is the default timeout to wait for responses of the modem. + + reset + This is the sequence to reset the modem. + + local-init + This is used to customize an existing modem entry for different + purpose (e.g. force a specific connect rate, etc.) + + Beside this the section may contain protocol specific entries to adapt + the protocol for this service. + +ASCII based protocol + + A \C is replaced with the caller id, if available. If request a + delivery report is switched on, a \R is replaced with 1, else with 0. + + asc-timeout + This is the default timeout for sequences when communicating + with the remote side. + + asc-login + This is the sequence to login to the remote service. + + asc-logout + This is the sequence to logout from the remote service. + + asc-pagerid + This is the sequence to embedded the pager id to be sent. \P is + replaced with the pager id. + + asc-message + Dito for the message, \M is replaced with the message. + + asc-next + This is the sequence to start the next transmission. + + asc-sync + If we get out of sync, then this sequence should bring us back + to a stage as if we had just loged in. + +Script specific + + script-type + This is the scripting language to choose. Currently these are + supported: SLang, Lua. + + script-name + This is the name (e.g. the name of the variable containing) the + script. If the name starts with a slash or a plus sign, then + the value is treated as a filename from where the script should + be read. The plus sign is stripped off before opening the file. + + scr-login + This is the function/label where to start at login. If the + caller id is available (and the language supports it) the + caller id is passed as an argument. + + scr-logout + Dito for logout. + + scr-pagerid + Dito for sending the pagerid. The pagerid is passed as an + argument. + + scr-message + Dito for sending the message. The message is passed as an + argument. + + scr-next + Dito to go to the next message. + + scr-sync + Dito to sync to a definite state (i.e. as if we had just loged + in.) + +TAP specific + + tap-t1, tap-t2, tap-t3, tap-t4, tap-t5 + This is the timeout for each stage defined in the TAP + specification. See there for details. + + tap-n1, tap-n2, tap-n3 + This is the retry count for each stage defined int the TAP + specification. See there for details. + + tap-login-count + This is the number of tries to login to the remote service. + + tap-logout-count + This is the number of tries to logout from the remote service. + +UCP specific + + ucp-timeout + This is the time to wait for an answer from the remote system. + The documentation says that this could be about 40 to 60 + seconds. This depends on the provider. + + ucp-retry + This specifies how often a message should be sent, until the + program gives up. + + ucp-extend + If this is True then the extend UCP implementation is used, + i.e. the more complex, but more flexibel protocol (currently + only possible on german cellular phone provider Mannesmann D2.) + + ucp-ds-timeout + Wait a maximum of this value seconds for receiving of the + delivery status. + +Alias section + + The entries in this section are not able to access the global entries. + Each entry contains an alias name with its real number. + + Command Line + + Yaps support several command line parameter which can overwrite the + possibly used default values in the configuration files. Following + options are understood by this program (on some systems, there are is + also support for long options): + + -C , --config= + Use cfgfile instead of the default global configuration file + /etc/yaps.rc. + + -s , --service= + Use paging service service instead of the default of the + configuration file. + + -t, --truncate + If this is set, then a message is truncated, if it is longer + than the allowed limit of the choosen service. + + -c , --call-id= + Use callid as the caller id. + + -S , --signature= + If signature appending is enabled by the service, use this + string as the signature. + + -l , --logfile= + Write status of transmission to logfile. + + -L , --logstring= + This is used to select the messages written to the logfile. See + above unter the configuration entry logstring for a detailed + description. + + -f, --force + Force sending of messages, even if -d/-e/-r is not supported by + the service. + + -d , --delay= + If the service supports it, the message is delayed and sent at + date. + + -e , --expire= + If the service supports it, the message is deleted, if this + date is reached and the message had not been transmitted until + then. + + -r, --request-delivery-status + If the service supports it, a delivery status is requested. + + -R , --final-report= + A final report is written to fname (or stdout, if fname is -.) + This feature is useful to check which message had been + delivered successfully or not. + + -z , --message-file= + Reads pager-ids and messages form fname. Each line contains one + pair, seperated by whitespace(s). + + -v, --verbose + Increase the verbosity of yaps, more -v's give more debug + output. + + -p, --print-config + This is used to printout configuration values for testing + purpose. The remaining paramters are variables to print out + with their value. + + Following the options the user has to give receiver/message pairs, all + using the same service. A receiver is either the pager id itself or an + alias. If the first character is a colon, then the colon is cut off + and the remaining part is taken as an alias, if the first character is + a slash, it is cut off as well and the remaining is taken as the pager + id. If the first character is a digit, it is taken as a pager id, + otherwise as an alias. It is possible to specify multiple receiver, if + the receiver is a comma seperate list of individual receivers. If the + first character of the message is a plus sign, then the remaining + message is treated as a filename and the real message is read from + this file. If the message is just a minus (-), then the message is + read from stdin, if it is just a dot (.), then the message is empty. + + The options -d and -e accept a date representation which must be + (according to option parsing) one argument, e.g. if it contains spaces + it must be quoted. If the first character of date is a plus (+) sign, + then the date is taken as an offset to the current time. All further + definitions are seperated by spaces: + * :: + * .. + * // + * if less than 30 is taken as the hour + * if greater or equal than 30 is taken as minutes + + Sending/Expecting Syntax + + Whenever communication over the serial line takes place the + send/expect functions are comming into play. There are three important + functions, which are called from several parts: sending of data, + expecting data and a send/expect function. Each is explained in detail + here (including the supported syntax): + +Sending + + Sending itself is nothing fancy, just all data will be written to the + serial line. + +Expecting + + Expecting means to wait for some pattern to arrive. It is possible to + expect more than one sequence and the function returns the matching + entry, a timeout or an error condition. + +Send/Expect + + Most communication parts call this function (which then calls the + functions above.) This function requires a string with tokens + seperated by whitespaces. A token may contain whitespaces, if the + token is enclosed in single or double quotes. These quotes (as any + other quotes) are removed during the preparse. The first character of + the token is relevant for the method to be executed. Each token is + preparsed to remove/interpret special characters. Following special + characters are interpreted: + * Backslash \ + The backslash is used to escape any other special character + (inclusive quotes and the backslash itself) or to insert typical + control characters. These are supported: + + \a -> Bel + + \b -> Bs + + \f -> Ff + + \l -> Lf + + \n -> Nl (system depended) + + \r -> CR + + \s -> Space + + \t -> Tab + * Up arrow ^ + This is used to insert control characters, e.g. ^M means CR. + * Percent % + This inserts optional paramter nr, if supplied. + + These character initiate the behaviour of the token: + + < Expect + The rest of the token is for expecting. If the token contains - + then the token is split again and each subtoken is treated as + expect/send, if the expect failed. The expect token may be + seperated by | to indicate alternatives. The expect is failed, + if an error occurs, a timeout arised or not the first + alternatives matches. The < may be appended by a numeric value, + which is used as the timeout in seconds. + + ! Command + To insert dynamic commands into the sequence use this + construct. Following is an optional numeric value n and a + command character. If n is not set, its default value is one. + Following characters are supported: + + + d - sleep n seconds + + D - sleep n miliseconds + + b - send break (length depends on n and the implementation) + + h - hangup the line (lowering DTR by n * 0,5 seconds.) + + o - waits for output to drain + + < - flushes the input queue + + > - flushes the output queue + + f - forces the sequence to fail + + s - forces the sequence to succeed + + Any other character + This string is just send to the remote side. + + Scripting + + Scripting has the advantage (compared to the ASCII protocol) that one + is more flexible. As there are a lot of scripting languages around, + there will be no specific one for this package. As long as such a + language is embeddable into C programs, it should not be too + complicate to integrate it into this package. + + This section explains the additional function (or simular) for the + available scripting languages. The syntax itself is explained in the + distribution of each scripting package. + +SLang + + Beside the new functions there is an extension to the string class, so + you can use (beside others) the plus sign to concaternate strings. One + time this part should make its way into the main SLang distribution. + + Variable index + + int NO_ERR; + This is the return value for a function, that ended successful. + + int ERR_FAIL; + This is the return value for a function, that encountered an + error, but which allowes the script to continue. + + int ERR_FATAL; + Dito, but the script should not continue any more. + + int ERR_ABORT; + Dito, but no further action should take place. + + int rds; + Is set to True, if a delivery status should be requested. + + int delay_day, delay_mon, delay_year, delay_hour, delay_min, + delay_sec; + This is the time/date to delay the delivery of the message. + + int expire_day, expire_mon, expire_year, expire_hour, expire_min, + expire_sec; + This is the time/date to expire a buffered message. + + int False; + The numeric value for False. + + int True; + Dito for True. + + Function index + + void setcb (string func, string sep); + This enable the line callback facility and stores each line + into a local variable, which will be overwritten after each new + encountered line. A line is considered as complete, if one + character in sep is received. If func is the name of a defined + function, then this function is called on every completed new + line. The line is passed as the paramter to this function. + + void clrcb (void); + Clears the line callback facility. + + string line (void); + Returns the last complete read in line, if the line callback + facility is enabled. + + void hangup (void); + Tries to hangup, if the modem is currently off-hook. This is + done by lowering the DTR line. + + int send (string str); + Sends the string to the remote side. Returns True on success, + False otherwise. + + int csend (string str); + Dito, but the string is converted before it is sended to the + remote side. + + int expect (int tout, string str1, ..., string strn, int cnt); + The function waits tout seconds until one of the strings is + received. If no string is received, 0 is returned, -1 on error. + Otherwise the number of the string is returned (1 for str1, 2 + for str2, etc). cnt is the number of strings to wait for. + + int send_expect (int tout, string expr); + This function executes the expr with its internal send/expect + evaluater and returns True, when the sequence had been executed + completely, otherwies False. + + void drain (int secs); + This function reads and discards any character for secs + seconds. + + void cvdef (int src, int dst); + Defines a converion rule. Every character src will be replaced + by dst. The conversion is used in csend(). + + void cvundef (int ch); + Undefines the conversion rule for ch. + + void cvinval (int ch); + Marks the character ch as invalid. + + string conv (string str); + Converts the string str using the defined conversion rules and + returns the converted string. + +Lua + + Examples + + Here are some examples that may help you to understand the software a + bit better. First you should read the example configuration file + yaps.rc. This could be used as a base for your own global + configuration file. + +Calling + + Typically the program is called yaps . pagerid is + either the exact pagerid of the receiver or an alias found in the + alias section. message is the message to send by itself. If a pager-id + leads to more than one provider, then the first is used. To force a + special service use the -s switch. + +Script protocol + + In the contrib directory, you can find tap.sl, an example on how to + use the scripting facility to emulate a protocol. This is a minimal, + but working reimplemetation of TAP. + + Changelog + + This is a list of changes: + 9. May 1997: V 0.90 released + + Minor cleanup on calling sending routines + + Added optional multiple receiver per message + + Changing UCP to prepare the real implementation + + Preparing the creation of a client/server solution + + Compile configuration moved to seperate file + 13. May 1997: V 0.91 released + + Converted `char *' to `string_t *' in several places + + Added max-messages configuration option + + Moved some configuration stuff to config.h + + Got UCP docu! Implemented it partial (as much as it makes + sense) + + Added option -d/-e as UCP (in extended mode) can support it + + Added special date handling functions for this purpose + + Changed copyright to the GPL + + Use transparent data in extended UCP, if characters with set + 8th bit are found + + Better message splitting + + Added some sanity checks + 22. May 1997: V 0.92 released + + Added handling of configuration variants + + Added include option in configuration files + + First bugs encountered by tester fixed + + Message can now be read from stdin, if message == '-' (idea + by ) + + Message can now be empty, if message == '.' + + Minor bugfixing/Makefilehandling (reported by Frank Käfer) + + Enhanced lockfile handling, including SysV4 lockfiles + (inspirated by Frank Käfer) + + Total rewrite of UCP sending + 26. May 1997: V 0.93 released + + UCP works again including delivery report + + Added checking of pager id for a service + + Added signature in configuration file + + Added value start escaping with backslash + + Changed checking of pager id for service + + Automatic assign of a pager id to a service + + Could now handle more than one service at one call + 30. May 1997: V 0.94 released + + Workaround for serial bug in sunos/solaris (by Frank Käfer) + + If the system does not support Posix regular expression, a + simple replacement is added + + More comments in yaps.rc (as wished by Frank Käfer ;-) + + Change handling of control chars in TAP/pre V1.6 (hint by + ) + + Added default conversion rules + + Added force/-f + + Added long options support + + Added getopt() for systems without this function + + Added valid-cid/change-cid/rm-invalids-cid/rm-invalids-pid + 3. June 1997: V 0.95 released + + Added final status report + + Added a 2nd checking scheme + + Added support for lua (another scripting language) + + Removed porting approach, should be done by someone who has + access to such an OS. + + Added reading of pager-id/message pairs from file + + Added cost calculation + + Enhanced logfile handling + 14. June 1997: V 0.96 released diff --git a/yaps.html b/yaps.html new file mode 100644 index 0000000..0cfe242 --- /dev/null +++ b/yaps.html @@ -0,0 +1,883 @@ + +yaps + + + +

Table of Contents

+ +
    +
  1. Introduction +
  2. Installation +
  3. Configuration +
  4. Command Line +
  5. Sending/Expecting Syntax +
  6. Scripting +
  7. Examples +
  8. Changelog +
+ + +

Introduction

+ +Welcome to Yaps, Yet Another Paging Package. The program is used +to send textual messages to a remote paging device using a modem gateway. +Many pager companies offer such modem gateways and are using specific +protocols. The most often used one, TAP is implemented beside others (more +or less complete.) This command line program uses a global and a private +configuration file to get information about each paging service, installed +modems and possibly other.

+ +As the main part is implemented as a library it is not very complicated to +write a client/server solution. For the legal status see the File +Copying. + + +

Installation

+ +The software is written, tested and designed on and for Unix(tm) like +operating systems and requires the so called Posix system calls and function +library. Gnumake and GCC are required, too (or some changes to the Makefile +and the code may be neccessary.) It is known to run at least on following +operating systems: + +(more may follow.)

+ +Typically these steps should be enough to install the package: +

+
Edit Config +
For GCC the settings of CC and CFLAGS should be okay. Define +SLANG=True, if you like to use the SLang scripting +language, which can be found on +space.mit.edu. Install it +somewhere the compiler can find it. Define LUA=True for +using the lua scripting language, which is located on +ftp.icad.puc-rio.br. READ +THE COPYRIGHT RELATED FILES OF EACH PACKAGE BEFORE INSTALLING IT! +
Edit config.h +
This is used to adapt the software to various variants of Unix(tm). If +you do not have a regex.h file, either set HAVE_REGEX_T to zero or install +the GNU regular expression library. Be sure the library and includefile can +be found by the compiler and the name of the header file is +regex.h, otherwise make a (symbolic) link to this file. Add the +libary to EXLIB = in Config. +
make depend +
This create the dependency file which will be included during make. +
make (or make all) +
This compiles the library, the program driver and links the program. +The documentation is created using lynx. As lynx is not on every system the +already formated result is included as well. +
make install +
This copies the program and the global configuration file to the final +location. This step requires a working install program. If this is +missing, you have to install it by hand (well, two files should not be too +much.) +
Edit the global configuration file +
With your favorite editor you should edit the global configuration file +(which will be installed as /etc/yaps.rc on default) and change +everything as required. See the Configuration section +for a detailed description of the contents of the file. +
Done! +
You may play around with your configuration until you are satisfied, +but this fine tuning is up to you. +

+ +If you have problems during compilation, feel free to send me a +mail. + + +

Configuration

+ +The program reads first the global configuration file /etc/yaps.rc +(when not overwritten by command line switch), then the local one in +~/.yapsrc to get its configuration. Command line options may +overwrite some settings in these files. Both configuration files have the +same layout where the local one can overwrite settings of the global one. +Each file is seperated in sections, where a section is started with +[<name>] or [] for the global +section. When a file is opened all entries are stored in the global section +until a section name appears. If a section expression [..] is followed by a +comma seperate list of names, then these are the fallback sections, i.e. +variables not found int his section are taken from the fallback sections +before trying the global one.

+ +If the first character is a bar (|) then the rest is taken +as a filename to be included at this point. The current section is +NOT affected by this call. + +Each line may have exactly one entry (or a comment starting with a hash sign +in the first column) where the variable name is seperated by whitespaces +from its value. If the first character of the value is a backslash +(\), then this is removed. This allowes creating values +starting with whitespaces. But if a value should start with a backslash +itself, be sure to escape it with another backslash. A line can split over +several physical line, if each line (expect the last one) ends with a +backslash. If the value starts with an opening curly bracket ({) +then all lines up to the closing curly bracket (}) in the first +column are taken as the value for that variable. The brackets (and the lines +where they appear) are not part of the value.

+ +The value may either be textual, numeric, boolean or a check expression. +Textual values are starting with the first non whitespace character and +continue to the end of the line. Numeric values are a sequence of digits +which are parsed by the C-function atoi(3). Boolean may either be +T, Y, 1 or + for true and F, +N, 0 or - for false (or in greater detail, +everything that is not true is false.) Case is here not significant. The +check is a sequence of special characters which must match the given string. +If a > is found, then the rest is optional, but must still +match, if a < is found and the string is not at its end, it is +considered as too long. These characters are supported: +

    +
  • 1 numeric +
  • h hexadecimal +
  • l lowercase +
  • u uppercase +
  • a alpha +
  • n alphanumeric +
  • p printable +
  • x ascii +
  • every other character matches every character +
+Some simple examples: +
    +
  • >1 A string with unlimited length, but made only +out of numeric values. +
  • aaa A string of exact three alpha characters. +
  • >xxxx< A string of length zero up to four made +of ASCII characters. +
  • ....>1111< A string starting with exact four +characters, followed by zero to four numeric characters. +
+ +Alternative one can specify the check by a description, if the checkstring +is prefixed by a plus sign. This is then a comma seperated list of checks, +which are seperated by an equal sign into variable and value. These +variables are supported: +
    +
  • type=<type> Each character in the string must +match the type, which may be one of the following: +
      +
    • numeric only digits are allowed. +
    • sedecimal only hexdigits are allowed. +
    • lower only lowercase characters are allowed. +
    • upper only uppercase characters are allowed. +
    • alpha only alpha characters are allowed. +
    • alphanumeric only digits and alpha characters are + allowed. +
    • print only printable characters are allowed. +
    • ascii only ASCII characters are allowed. +
    +
  • length=<length> The string must have exact this +length. +
  • minimum=<length> The string must at least this +length. +
  • maximum=<length> The string must not be longer +than this length. +
+

+ +The global section defines global values or default values for other +sections. Some section may not inherit these values, but currently there is +only one and is marked as such.

+ +

Global section

+ +These are typical found in the global section and can be overwritten in +other sections, if required. + +
+
services <text> +
This is the comma seperated list of available services. +
call-id <text> +
If the pager allows to send the source caller id this id is used, if +not overwritten by a command line argument. +
signature <text> +
If this is present, then the text is appended to each message sent to +the pager (and if use-signature is True). +
verbose <num> +
Sets the debugging/verbosity level. The bigger the number, the more +output is generated. +
logfile <text> +
If the given text is a valid filename the status of a sending request +is loged there. +
logstring <text> +
If this is present, then only these parts are logged, which match the +given string. Currently these elements are supported: +
    +
  • + message had been trasmitted successfully +
  • - message had not been transmitted +
  • * part had been transmitted successfully +
  • / part had not been transmitted +
  • i informal logfile entry +
  • c cost relevant informations +
  • s session start +
  • e session end +
  • p protocol specific +
+
modems <text> +
This is a list of modem entries, seperated by commas. +
final-report <text> +
If this is set, a final report is written to the given filename. If +filename is -, then it is written to stdout. This feature +is intended for use in shell scripts, so the script could proof which +message has actual send and which one has been rejected instead of simple +rely on the return code of yaps. +
+ +

Service section

+ +The names of these section are free to the user, but their contents describe +a paging service (or a paging company.) Following variables are allowed for +service sections: + +
+
speed <num> +
The speed of the serial device. +
bits-per-byte <num> +
The number of bits per byte, supported are 5 to 8. +
parity <text> +
The used parity, this may either be none, even or +odd. +
stopbits <num> +
The number of stopbits, supported are 1 or 2. +
phone <text> +
The phone number of the modem gateway for this service. If the number +contains %P and insert-pager-id is set, then the +pagerid is inserted into the phonenumber. +
protocol <text> +
This is the communication protocol, that this service requires. +Currently are ascii, script, tap and ucp +allowed. +
max-size <num> +
The maximal length of a message for this service. +
may-split <bool> +
If this is true, then a message, which is too long will be split into +several messages. +
max-messages <num> +
The maximum number of messages per message, that can be send. If this +is zero, then an unlimited number of messages can be send. +
truncate <bool> +
If this is set a message is not split, but truncated to the length +allowed by the provider. +
use-call-id <bool> +
If this is true, then the caller-id is inserted. Where it is insert is +protocol specific. +
use-signature <bool> +
If this is true and a signature is given, this is appended to each +message. +
insert-pager-id <bool> +
If this is true, then the pagerid is inserted as part of the phone +number. This implies, that only messages to the same receipiant can be send +at one call. +
rm-invalids-cid <text> +
Remove every character in text, that appears in the caller id. +This is designed to make a caller id more readable, but still useable by the +desired service. +
rm-invalids-pid <text> +
Dito for pager ids. +
valid-pid <text> +
This is a regular expression to check wether a pager-id is valid for +this service. If your system does not support Posix regular expression, you +can use a simple replacement. This is just a list of strings, seperated by a +bar which must match the beginning of the pager-id. +
change-pid <text> +
If the pager-id matches the regular expression of valid-pid, +then this matching part is replaced with this string. If the string is just +a minus (-), then the match is removed from the resulting pager-id. +
valid-cid <text> +
The same like valid-pid for the caller id. +
change-cid <text> +
The same like change-pid for the caller id. +
conv-table <text> +
The value is a filename to a character conversion table. This affects +only the message (and depending on the protocol the pagerid) and allows +transforming illegal characters to valid ones. +
convert <text> +
This is comma seperated list of direct change entries of the convertion +table. If you want to include more than one pair, the block construct using +curly brackets ({..}) must be used. Each line contains a source and +a destination character, i.e. if the source character is encountered in a +message, the destination character is used for it. There are also some +predefined convertion rules, which all starts with an asterisk: +
    +
  • *no-control control characters are suppressed +
  • *control control characters are inserted literal +
  • *no-8bit no characters with 8'th bit set are + included +
  • *8bit use also characters with 8'th bit set. +
  • *numeric only numeric characters are allowed. +
+
cost <text> +
This string is used to calculate the costs for each call. The +description is a comma seperated list of variables with equal sign seperated +optional value. These variables are currently implemented: +
+
fixed +
This tells the software, that the cost are fixed per call, i.e. + no real calculation takes place according to the used entities. +
entity-length=<entity> +
This is the length of one entity. This is interpreted as a + floating point number. +
max-entities=<count> +
Some providers only charge until a maximum of entites had + been used and stop then charging. +
dial-overhead=<seconds> +
The counter starts before dialing. Typically it takes some + time from dialing to the first billed entity. This time can + be specified using this variable. +
cost=<cost> +
This is the cost per entity (or the whole cost on fixed charging + services). This is a floating point number. +
unit=<string> +
This string is appended in the logfile for the currency. This is + only of cosmetic value. +
remainder=<digits> +
This is the number of digits after the point in the cost display. + This value is typical two. +
timetable=<description> +
If a timetable entry is given, then the cost are calculated + depending on weekday and time. On fixed charging services the + value describes the complete costs for the call, in the other + case the value is the entity-length for this day/time. The + description is a semi-colon seperated list of single entries of + the form:
+<Weekday(s)><from>-<to>=<value>
+ Each weekday is a two letter sequence (So, Mo, Tu, We, Th, Fr, Sa) + and there are three special "weekdays": Wk for + working days (monday to friday), Ss for weekend + (saturday and sunday) and al for all days. A typical + example may look like: + Wk0800-1800=12;Wk1800-0800=24;Ss=24, but this + could be written shorter as: + =24;Wk0800-1800=12 because the first entry is + taken as the default, if no match is found. And the construct + =24 does not contain any weekday, so it is invalid for + any regular check. +
+
force <bool> +
If a feature is requested, but this service do not support it, the +message will still be delivered, if this variable is set to True. +
can-delay <bool> +
Set this to True, if the service provider accepts a delay for sending +the message. +
can-expire <bool> +
Set this to True, if the service provider allowes the setting of an +expiration timestamp. +
can-rds <bool> +
Set this to True, if the service provider allowes the request for a +delivery status. +
rds <bool> +
Set this to True, if can-rds is True and you always want to +get a delivery status. +
check-call-id <check> +
The caller id must match the check expression. +
check-pager-id <check> +
Each pager id must match the check expression. +
+ +

Modem section

+ +Each modem should have its own section. Following entries are currently +supported: + +
+
device <text> +
The filename for the device where the modem is attached to. +
lock-prefix <text> +
This is the pathname to prefix the basename of the modemdevice to +create lockfiles. This can be used to enable more than one application to +use the modem. +
lock-method <text> +
This is a comma seperated list of locking mechanism. Currently these +flags are supported: +
    +
  • ascii PID is stored as ASCII text in lockfile. +
  • binary PID is stored in machine representation in + lockfile. +
  • lower converts device part of lockfile to lower case. +
  • upper converts device part of lockfile to upper case. +
  • sysv4 append SysV4 style infos to lockfile instead of the + basename of the device. +
  • timeout=<secs> tries to lock the device + secs seconds. +
+
init <text> +
This is the init sequence to initialize the modem. +
dial <text> +
This is the dial sequence to dial a phone number with the modem. An +\L in the string will be replaced by the phone number. +
timeout <num> +
This is the default timeout to wait for responses of the modem. +
reset <text> +
This is the sequence to reset the modem. +
local-init <text> +
This is used to customize an existing modem entry for different purpose +(e.g. force a specific connect rate, etc.) +
+ +Beside this the section may contain protocol specific entries to adapt the +protocol for this service. + +

ASCII based protocol

+ +A \C is replaced with the caller id, if available. If +request a delivery report is switched on, a \R is replaced +with 1, else with 0. + +
+
asc-timeout <num> +
This is the default timeout for sequences when communicating with the +remote side. +
asc-login <text> +
This is the sequence to login to the remote service. +
asc-logout <text> +
This is the sequence to logout from the remote service. +
asc-pagerid <text> +
This is the sequence to embedded the pager id to be sent. +\P is replaced with the pager id. +
asc-message <text> +
Dito for the message, \M is replaced with the message. +
asc-next <text> +
This is the sequence to start the next transmission. +
asc-sync <text> +
If we get out of sync, then this sequence should bring us back to a +stage as if we had just loged in. +
+ +

Script specific

+ +
+
script-type <text> +
This is the scripting language to choose. Currently these are +supported: SLang, Lua. +
script-name <text> +
This is the name (e.g. the name of the variable containing) the script. +If the name starts with a slash or a plus sign, then the value is treated as +a filename from where the script should be read. The plus sign is stripped +off before opening the file. +
scr-login <text> +
This is the function/label where to start at login. If the caller id is +available (and the language supports it) the caller id is passed as an +argument. +
scr-logout <text> +
Dito for logout. +
scr-pagerid <text> +
Dito for sending the pagerid. The pagerid is passed as an argument. +
scr-message <text> +
Dito for sending the message. The message is passed as an argument. +
scr-next <text> +
Dito to go to the next message. +
scr-sync <text> +
Dito to sync to a definite state (i.e. as if we had just loged in.) +
+ +

TAP specific

+ +
+
tap-t1, tap-t2, tap-t3, tap-t4, tap-t5 <num> +
This is the timeout for each stage defined in the TAP specification. +See + +there +for details. +
tap-n1, tap-n2, tap-n3 <num> +
This is the retry count for each stage defined int the TAP +specification. See + +there +for details. +
tap-login-count <num> +
This is the number of tries to login to the remote service. +
tap-logout-count <num> +
This is the number of tries to logout from the remote service. +
+ +

UCP specific

+ +
+
ucp-timeout <num> +
This is the time to wait for an answer from the remote system. The +documentation says that this could be about 40 to 60 seconds. This depends +on the provider. +
ucp-retry <num> +
This specifies how often a message should be sent, until the program +gives up. +
ucp-extend <bool> +
If this is True then the extend UCP implementation is used, i.e. the +more complex, but more flexibel protocol (currently only possible on german +cellular phone provider Mannesmann D2.) +
ucp-ds-timeout <num> +
Wait a maximum of this value seconds for receiving of the delivery +status. +
+ +

Alias section

+ +The entries in this section are not able to access the global entries. Each +entry contains an alias name with its real number. + + +

Command Line

+ +Yaps support several command line parameter which can overwrite the possibly +used default values in the configuration files. Following options are +understood by this program (on some systems, there are is also support for +long options): + +
+
-C <configfile>, --config=<configfile> +
Use cfgfile instead of the default global +configuration file /etc/yaps.rc. +
-s <service>, --service=<service> +
Use paging service service instead of the default of +the configuration file. +
-t, --truncate +
If this is set, then a message is truncated, if it is longer than the +allowed limit of the choosen service. +
-c <callid>, --call-id=<callid> +
Use callid as the caller id. +
-S <signature>, --signature=<signature> +
If signature appending is enabled by the service, use this string as +the signature. +
-l <logfile>, --logfile=<logfile> +
Write status of transmission to logfile. +
-L <logstr>, --logstring=<logstr> +
This is used to select the messages written to the logfile. See +above unter the configuration entry +logstring for a detailed description. +
-f, --force +
Force sending of messages, even if -d/-e/-r is not supported by the +service. +
-d <date>, --delay=<date> +
If the service supports it, the message is delayed and sent at +date. +
-e <date>, --expire=<date> +
If the service supports it, the message is deleted, if this date is +reached and the message had not been transmitted until then. +
-r, --request-delivery-status +
If the service supports it, a delivery status is requested. +
-R <fname>, --final-report=<fname> +
A final report is written to fname (or stdout, if fname is +-.) This feature is useful to check which message had been +delivered successfully or not. +
-z <fname>, --message-file=<fname> +
Reads pager-ids and messages form fname. Each line contains +one pair, seperated by whitespace(s). +
-v, --verbose +
Increase the verbosity of yaps, more -v's give more debug output. +
-p, --print-config +
This is used to printout configuration values for testing purpose. The +remaining paramters are variables to print out with their value. +
+ +Following the options the user has to give receiver/message pairs, all using +the same service. A receiver is either the pager id itself or an alias. If +the first character is a colon, then the colon is cut off and the remaining +part is taken as an alias, if the first character is a slash, it is cut off +as well and the remaining is taken as the pager id. If the first character +is a digit, it is taken as a pager id, otherwise as an alias. It is possible +to specify multiple receiver, if the receiver is a comma seperate list of +individual receivers. If the first character of the message is a plus sign, +then the remaining message is treated as a filename and the real message is +read from this file. If the message is just a minus (-), +then the message is read from stdin, if it is just a dot +(.), then the message is empty.

+ +The options -d and -e accept a date representation which +must be (according to option parsing) one argument, e.g. if it contains +spaces it must be quoted. If the first character of date is a plus +(+) sign, then the date is taken as an offset to the +current time. All further definitions are seperated by spaces: +

    +
  • <hour>:<min>:<sec> +
  • <day>.<month>.<year> +
  • <month>/<day>/<year> +
  • <numeric> if less than 30 is taken as the hour +
  • <numeric> if greater or equal than 30 is taken as minutes +
+ + +

Sending/Expecting Syntax

+ +Whenever communication over the serial line takes place the send/expect +functions are comming into play. There are three important functions, which +are called from several parts: sending of data, expecting data and a +send/expect function. Each is explained in detail here (including the +supported syntax): + +

Sending

+ +Sending itself is nothing fancy, just all data will be written to the serial +line. + +

Expecting

+ +Expecting means to wait for some pattern to arrive. It is possible to expect +more than one sequence and the function returns the matching entry, a +timeout or an error condition. + +

Send/Expect

+ +Most communication parts call this function (which then calls the functions +above.) This function requires a string with tokens seperated by +whitespaces. A token may contain whitespaces, if the token is enclosed in +single or double quotes. These quotes (as any other quotes) are removed +during the preparse. The first character of the token is relevant for the +method to be executed. Each token is preparsed to remove/interpret special +characters. Following special characters are interpreted: + +
    +
  • Backslash \
    +The backslash is used to escape any other special character (inclusive +quotes and the backslash itself) or to insert typical control characters. +These are supported: +
      +
    • \a -> Bel +
    • \b -> Bs +
    • \f -> Ff +
    • \l -> Lf +
    • \n -> Nl (system depended) +
    • \r -> CR +
    • \s -> Space +
    • \t -> Tab +
    +
  • Up arrow ^
    +This is used to insert control characters, e.g. ^M means CR. +
  • Percent %<nr>
    +This inserts optional paramter nr, if supplied. +

+ +These character initiate the behaviour of the token: +

+
< Expect +
The rest of the token is for expecting. If the token contains +- then the token is split again and each subtoken is +treated as expect/send, if the expect failed. The expect token may be +seperated by | to indicate alternatives. The expect is +failed, if an error occurs, a timeout arised or not the first alternatives +matches. The < may be appended by a numeric value, which +is used as the timeout in seconds. +
! Command +
To insert dynamic commands into the sequence use this construct. +Following is an optional numeric value n and a command +character. If n is not set, its default value is one. +Following characters are supported: +
    +
  • d - sleep n seconds +
  • D - sleep n miliseconds +
  • b - send break (length depends on n and the + implementation) +
  • h - hangup the line (lowering DTR by n * 0,5 + seconds.) +
  • o - waits for output to drain +
  • < - flushes the input queue +
  • > - flushes the output queue +
  • f - forces the sequence to fail +
  • s - forces the sequence to succeed +
+
Any other character +
This string is just send to the remote side. +
+ + +

Scripting

+ +Scripting has the advantage (compared to the ASCII protocol) that one is +more flexible. As there are a lot of scripting languages around, there will +be no specific one for this package. As long as such a language is +embeddable into C programs, it should not be too complicate to integrate it +into this package.

+ +This section explains the additional function (or simular) for the available +scripting languages. The syntax itself is explained in the distribution of +each scripting package. + +

SLang

+ +Beside the new functions there is an extension to the string class, so you +can use (beside others) the plus sign to concaternate strings. One time this +part should make its way into the main SLang distribution. + +

Variable index

+ +
+
int NO_ERR; +
This is the return value for a function, that ended successful. +
int ERR_FAIL; +
This is the return value for a function, that encountered an error, but +which allowes the script to continue. +
int ERR_FATAL; +
Dito, but the script should not continue any more. +
int ERR_ABORT; +
Dito, but no further action should take place. +
int rds; +
Is set to True, if a delivery status should be requested. +
int delay_day, delay_mon, delay_year, delay_hour, delay_min, delay_sec; +
This is the time/date to delay the delivery of the message. +
int expire_day, expire_mon, expire_year, expire_hour, expire_min, expire_sec; +
This is the time/date to expire a buffered message. +
int False; +
The numeric value for False. +
int True; +
Dito for True. +
+ +

Function index

+ +
+
void setcb (string func, string sep); +
This enable the line callback facility and stores each line into a +local variable, which will be overwritten after each new encountered line. A +line is considered as complete, if one character in sep is +received. If func is the name of a defined function, then +this function is called on every completed new line. The line is passed as +the paramter to this function. +
void clrcb (void); +
Clears the line callback facility. +
string line (void); +
Returns the last complete read in line, if the line callback facility +is enabled. +
void hangup (void); +
Tries to hangup, if the modem is currently off-hook. This is done by +lowering the DTR line. +
int send (string str); +
Sends the string to the remote side. Returns True on +success, False otherwise. +
int csend (string str); +
Dito, but the string is converted before it is sended to the remote +side. +
int expect (int tout, string str1, ..., string strn, int cnt); +
The function waits tout seconds until one of the +strings is received. If no string is received, 0 is +returned, -1 on error. Otherwise the number of the string +is returned (1 for str1, +2 for str2, etc). cnt is +the number of strings to wait for. +
int send_expect (int tout, string expr); +
This function executes the expr with its internal +send/expect evaluater and returns True, when the sequence +had been executed completely, otherwies False. +
void drain (int secs); +
This function reads and discards any character for +secs seconds. +
void cvdef (int src, int dst); +
Defines a converion rule. Every character src will be +replaced by dst. The conversion is used in csend(). +
void cvundef (int ch); +
Undefines the conversion rule for ch. +
void cvinval (int ch); +
Marks the character ch as invalid. +
string conv (string str); +
Converts the string str using the defined conversion +rules and returns the converted string. +
+ +

Lua

+ + + +

Examples

+ +Here are some examples that may help you to understand the software a bit +better. First you should read the example configuration file +yaps.rc. This could be used as a base for your own +global configuration file. + +

Calling

+ +Typically the program is called yaps <pagerid> +<message>. pagerid is either the exact pagerid of +the receiver or an alias found in the alias section. message is the +message to send by itself. If a pager-id leads to more than one provider, +then the first is used. To force a special service use the -s +<service> switch. + +

Script protocol

+ +In the contrib directory, you can find tap.sl, +an example on how to use the scripting facility to emulate a protocol. This +is a minimal, but working reimplemetation of TAP. + +

Changelog

+ +This is a list of changes: +
    +
  • 9. May 1997: V 0.90 released +
      +
    • Minor cleanup on calling sending routines +
    • Added optional multiple receiver per message +
    • Changing UCP to prepare the real implementation +
    • Preparing the creation of a client/server solution +
    • Compile configuration moved to seperate file +
    +
  • 13. May 1997: V 0.91 released +
      +
    • Converted `char *' to `string_t *' in several places +
    • Added max-messages configuration option +
    • Moved some configuration stuff to config.h +
    • Got UCP docu! Implemented it partial (as much as it makes sense) +
    • Added option -d/-e as UCP (in extended mode) can support it +
    • Added special date handling functions for this purpose +
    • Changed copyright to the GPL +
    • Use transparent data in extended UCP, if characters with + set 8th bit are found +
    • Better message splitting +
    • Added some sanity checks +
    +
  • 22. May 1997: V 0.92 released +
      +
    • Added handling of configuration variants +
    • Added include option in configuration files +
    • First bugs encountered by tester fixed +
    • Message can now be read from stdin, if message == '-' + (idea by <markus@mail.yezz.de>) +
    • Message can now be empty, if message == '.' +
    • Minor bugfixing/Makefilehandling (reported by Frank Käfer) +
    • Enhanced lockfile handling, including SysV4 lockfiles (inspirated + by Frank Käfer) +
    • Total rewrite of UCP sending +
    +
  • 26. May 1997: V 0.93 released +
      +
    • UCP works again including delivery report +
    • Added checking of pager id for a service +
    • Added signature in configuration file +
    • Added value start escaping with backslash +
    • Changed checking of pager id for service +
    • Automatic assign of a pager id to a service +
    • Could now handle more than one service at one call +
    +
  • 30. May 1997: V 0.94 released +
      +
    • Workaround for serial bug in sunos/solaris (by Frank Käfer) +
    • If the system does not support Posix regular expression, a simple + replacement is added +
    • More comments in yaps.rc (as wished by Frank Käfer ;-) +
    • Change handling of control chars in TAP/pre V1.6 (hint by + <markus@mail.yezz.de>) +
    • Added default conversion rules +
    • Added force/-f +
    • Added long options support +
    • Added getopt() for systems without this function +
    • Added valid-cid/change-cid/rm-invalids-cid/rm-invalids-pid +
    +
  • 3. June 1997: V 0.95 released +
      +
    • Added final status report +
    • Added a 2nd checking scheme +
    • Added support for lua (another scripting language) +
    • Removed porting approach, should be done by someone who + has access to such an OS. +
    • Added reading of pager-id/message pairs from file +
    • Added cost calculation +
    • Enhanced logfile handling +
    +
  • 14. June 1997: V 0.96 released +
+ + diff --git a/yaps.lsm b/yaps.lsm new file mode 100644 index 0000000..fbb44d8 --- /dev/null +++ b/yaps.lsm @@ -0,0 +1,19 @@ +Begin3 +Title: yaps +Version: 0.96 +Entered-date: 14JUN97 +Description: This is a standalone program to send messages to paging + devices over a modem gateway using well defined protocols. +Keywords: pager sms tap ucp +Author: ud@nitmar.tnet.de (Ulrich Dessauer) +Maintained-by: ud@nitmar.tnet.de (Ulrich Dessauer) +Primary-site: ftp.sta.com /pub/fk/yaps + 91k yaps-0.96.tar.gz + 725 yaps.lsm +Alternate-site: sunsite.unc.edu /pub/Linux/apps/serialcomm/machines/ + 91k yaps-0.96.tar.gz + 725 yaps.lsm +Platforms: Linux, Solaris, SunOS and modem (or modem like device, + e.g. ISDN) optional SLang V99.38, Lua-2.5 +Copying-policy: GPL +End diff --git a/yaps.rc b/yaps.rc new file mode 100644 index 0000000..f4b0fe5 --- /dev/null +++ b/yaps.rc @@ -0,0 +1,370 @@ +# -*- sh -*- +# +# General yaps example configuration file +# +######################################### +# global section # +######################################### +# +# This is a comma seperated list of available services. This +# is required for autodetect the matching service for a +# pager-id +services D1,D2,E+ ++services Telmi,Telmi-Fun,Telmi-Family,Telmi-Top,Telmi-Pro ++services Skyper,Quix +# +# This is the default caller-id. If the protocol/service allowes +# it, this is inserted in the message (and the receiver gets this +# as the sender's number) +call-id +1-(555)-1234 +# +# If you forget to sign your message, you can automatically append +# this string to each message. +signature (It's me!) +# +# You can either switch the use of the signature here on for all +# services or in each service section for that particular service. +use-signature True +# +# To get more verbose output when running yaps increase this value. +# Currently 4 is the highest supported value. +verbose 0 +# +# If you'd like to log every sending session define this. It should +# point to a writeable file. If you install the software setgid, then +# change the group of this file to the same yaps uses and make it +# group writeable. +logfile /var/log/yaps.log +# +# A comma seperated list of modem sections. You can also define this +# in each service section. +modems standard +# +# some global defaults for the serial interface (even if the most +# protocols are assuming 7e1, these settings seem to work well) +speed 38400 +bits-per-byte 8 +parity none +stopbits 1 + +# +# To calculate the cost for each call, you can define some globales +# here and do the fine tuning in each protocol +cost cost=0.12,unit=DM,remainder=2,dial-overhead=13 + +# +# These are two example of internal conversion tables. The can be +# used using the `convert' entry (see below). +cv-default { + Ä [ + Ö \ + Ü ] + ä { + ö | + ü } + ß ~ +} +cv-extend { + \l \s + \n \s + \r \s + \f \s + \t \s +} + +# +# These values are normally only found in each service section, +# but are listen here with an explaination for completeness. +# +# This is the phone number of the modem gateway for this service +#phone 0015559876 + +# +# This is the protocol the service uses. Currently this can be +# either ascii, script, ucp or tap. Each protocol has a set of +# special configuration parameters, see below. +#protocol tap + +# +# Most services limit the length of a message to a specific length. +# Enter the value here to check valid messages +#max-size 160 + +# +# If a message is longer than max-size, you can set this to split +# it off into several single messages of allowed length +#may-split True + +# +# If a message is longer than max-size, you can set this to truncate +# the message to the allowed length. The remaining part is discarded. +#truncate True + +# +# Some services allow to send more than one message per session. If +# it is limited, set it using this parameter +#max-messages 1 + +# +# If the service allows using your caller-id set this to True. +#use-call-id True + +# +# If the service is reachable over a phonenumber where the pager-id +# is part of the phone number, define this and add a `%P' anywehere +# in the `phone' number. +#insert-pager-id True + +# +# Remove these characters from the pager-id, as they are not valid +rm-invalids-pid + (-) + +# +# Dito for caller id +rm-invalids-cid + (-) + +# +# Check for valid call id (see below for more details) While +# valid-pid is required, this is optional +#valid-cid ^(001|1) +#change-cid 001 + +# +# Depending on your system the value for this parameter is either a +# regular expression or a simple list of strings (seperated by bars) +# which must match the prefix of the pager-id. +#valid-pid ^(0171|0049171) +#valid-pid 0171|0049171 +# Use these paramters in the global section if you want do not want +# to check pager-ids (and do not set change-pid): +#valid-pid . +#valid-pid - + +# +# If a pagerid has matched the expression above, then the matching part +# may be replaced by this string +#change-pid 0171 + +# +# As the services do not use (in general) the ISO 8859/1 character set, +# one can define convertion tables. you can either specify a file which +# contains the convertion rules: +#conv-table /usr/local/lib/yaps/iso2din.tab + +# or use predefined convertion rules +#convert *no-control,*no-8bit,cv-default,cv-extended + +# +# If this is set, send a message even if a requested feature is +# not available +#force True + +# +# If the provider supportes to set a delay for sending the message, +# set this +#can-delay True + +# +# If the provider can delete a buffered message after a period of time, +# set this +#can-expire True + +# +# If the provider can report a delivery status, set this. +#can-rds True + +# +# To check wether a call-id/pager-id is valid use these paramters. +# For the exact syntax see the documentation +#check-call-id >1111111111111111< +#check-pager-id >1111111111111111< +#check-call-id +minimum=0,maximum=16,type=numeric + +# +# These values are typically found in a modem section, but can also +# appear in the global section to set some defaults + +# +# Set this to the modem device you wish to use +#device /dev/modem +#device /dev/cua1 +#device /dev/cua/a + +# +# Set this to the prefix of the lockfile use for the device. There +# are several places, and two common formats: +#lock-prefix /usr/spool/uucp/LCK.. +#lock-prefix /var/spool/lock/LCK.. +#lock-prefix /var/spool/locks/LK. + +# +# The different format of the part which is appended to the lockfile +# and the contents can be specified here. See documentation for a +# full list inclusive explaination +#lock-method sysv4 +#lock-method binary +#lock-method timeout=10 + +# +# These are send/expect sequences to talk to the modem. For an exact +# definition see (again) the documentation. Following entries are +# supported: +# init: to initialize the modem +# local-init: a second initialization string, used to customize +# already defined modems for different handling +# dial: to dial a phone number %L is replaced with the +# phone number itself +# timeout: the default timeout in seconds to wait for answers +# reset: to reset the modem + +######################################### +# service section # +######################################### +# +# D1 (by Deutsche Telekom) +# +[D1] +protocol tap +valid-pid 0171|0049171|49171 +change-pid 0171 +convert *no-8bit,cv-d1 +cv-d1 { + Ä [ + Ö \ + Ü ~ + ä { + ö | + ü ~ + ß ^] +} ++cost timetable=Wk0800-1800=5.7;Wk1800-0800=11.4;Ss=11.4 +phone 01712092522 +max-size 160 +may-split True +use-call-id False +tap-old True +# +# D2 (by Mannesmann Mobilfunk) +# +[D2] +modem standard-d2 +protocol ucp +valid-pid 0172|0049172|49172 +change-pid 0172 +convert *no-8bit,cv-default ++cost timetable=Wk0800-1800=5.7;Wk1800-0800=11.4;Ss=11.4 +phone 01722278020 +max-size 160 +may-split True +use-call-id True +can-delay True +can-expire True +can-rds True +check-call-id >1111111111111111< +check-pager-id >1111111111111111< +ucp-extend True +# +# E+ (by ???) +# !! UNTESTED !! +# +[E+] +protocol tap +valid-pid 0177|0049177|49177 +change-pid 0177 +convert *no-8bit,cv-default ++cost timetable=Wk0800-1800=5.7;Wk1800-0800=11.4;Ss=11.4 +phone 01771167 +max-size 160 +may-split True +use-call-id False +# +# Telmi, Telmi-* (by Deutsche Funkruf) +# !! ONLY TESTED FOR TELMI-FUN !! +# +[Telmi] +protocol tap +valid-pid 01661|01665|00491661|00491665|491661|491665 +change-pid - +convert *no-control,*no-8bit,cv-default,cv-extend ++cost entity-length=5.2 +phone 016601010101 +max-size 114 +may-split True +max-messages 1 +use-call-id False +check-pager-id 1111111 +tap-old True +[Telmi-Fun] Telmi +[Telmi-Family] Telmi +[Telmi-Top] Telmi +max-size 240 +[Telmi-Pro] Telmi-Top +# +# Skyper (by Deutsche Telekom) +# !! UNTESTED !! +# +[Skyper] +protocol tap +valid-pid 01692|00491692|491692 +change-pid - +convert *no-8bit,cv-default,cv-extend ++cost fixed,cost=0.96 +phone 01692%P +max-size 80 +use-call-id False +insert-pager-id True +# +# Quix (by Miniruf) +# !! UNTESTED !! +# +[Quix] +protocol ucp +valid-pid 01653|00491653|491653 +change-pid - ++cost fixed,timetable=Wk0800-1800=1.20;Al=0.96 +convert *no-8bit,cv-default,cv-extend +phone 016593 +max-size 80 +use-call-id True + +######################################### +# modem section # +######################################### +[standard] +# +# Locking +# +# Older Unix versions +lock-prefix /usr/spool/uucp/LCK.. +# Newer Unix versions +#lock-prefix /var/spool/uucp/LCK.. +#lock-prefix /var/lock/LCK.. +# SysV4 +#lock-prefix /var/spool/locks/LK. +#lock-method sysv4 +# +# Device +# +# Typical a symbolic link to the real device +device /dev/modem +# "normal" call-out device +#device /dev/cua1 +# Solaris convention +#device /dev/cua/a + +# +# Modem talk +# +init \\r !200D ATZ\r